2013-04-25 4 views
5

Ich benutze COM mit einer alten VB6-Anwendung.DispID muss über Schnittstellen hinweg eindeutig sein?

Ich habe meinen Code geändert, um DispID in Schnittstellen zu verwenden, da es besser funktioniert als mit [ClassInterface(ClassInterfaceType.AutoDual)].

Aber ist es erlaubt, in jeder Schnittstelle zu beginnen, die von DispID (1) zählt, selbst wenn eine Klasse zwei Schnittstellen verwendet?

Funktioniert es so stabil? Oder verstehe ich etwas nicht?

[ComVisible(true)] 
[Guid("9E1125A6-...")] 
public interface IMyInterface1 
{ 
    [DispId(1)] 
    string Name1 { get; } 
} 

[ComVisible(true)] 
[Guid("123425A6-...")] 
public interface IMyInterface2 
{ 
    [DispId(1)] 
    string Name2 { get; } 
} 

[ComVisible(true)] 
[ClassInterface(ClassInterfaceType.None)] 
class MyClass : IMyInterface1, IMyInterface2 
{ 
    public string Name1 { get { return "Name1"; } } 
    public string Name2 { get { return "Name2"; } } 
} 
+2

Die erste Schnittstelle, die Sie auflisten, ist die einzige, die VB6 sehen kann, wenn es spät bindet. Es wird die Schnittstelle sein, die als die [Standard] Schnittstelle markiert ist. Also kein echtes Problem, da die andere Schnittstelle sowieso nicht verwendbar ist. Obwohl das wahrscheinlich ein echtes Problem ist;) –

Antwort

5

Darf man in jedem Interface mit DispID (1) beginnen, auch wenn eine Klasse zwei Interfaces benutzt?

DISPIDs müssen nur innerhalb der Schnittstelle eindeutig sein. Es ist gut, mit zwei Schnittstellen zu gehen, von denen jede ihre eigenen (unterschiedlichen) Eigenschaften von DISPID 1 hat, selbst wenn beide Schnittstellen durch dasselbe COM-Objekt implementiert sind.

Da VB6 erwähnt wird, müssen Sie jedoch beachten, dass VB6 nicht mehr als 2 Dispatch-Interfaces, die auf demselben COM-Objekt implementiert sind, mögen und nur das erste/wichtigste "sehen" könnte. Das heißt, das Problem ist keine DISPID-Kollision (was überhaupt kein Problem ist), sondern die Tatsache, dass VB6 nicht in der Lage ist, korrekt mit Objekten zu arbeiten, die 2+ Dual-Interfaces offen legen. Der Grund, warum dies in beschrieben auf MSDN in Multiple Dual Interfaces passiert:

Da nur eine IDispatch-Schnittstelle freigelegt ist, Clients, die nur Ihre Objekte über die IDispatch-Schnittstelle, die Methoden oder Eigenschaften in der Lage, nicht in jedem zugreifen können zugreifen andere Schnittstelle.

Und leider ist es der Fall von VB6. Im Gegensatz zu fortgeschritteneren Umgebungen fragt es Schnittstellen auf die Art ab, auf die "Methoden oder Eigenschaften in einer anderen Schnittstelle" nicht zugänglich sind. Die Zuweisung verschiedener DISPIDs wird jedoch nicht helfen.

2

Es gibt nur eine IDispatch-Implementierung pro COM-Objekt, also, wenn Sie einen Anruf wie IDispatch::Invoke erfolgreich sein wollen, müssen Sie DISPIDs einzigartige pro COM-Objekt haben.

EDIT: In der Tat, nach mehr darüber nachzudenken, ist die Frage ziemlich irrelevant, wie Hans in seinem Kommentar darauf hinweist. Da Sie ClassInterfaceType als None definieren, bedeutet dies, dass .NET nur die erste Schnittstelle IMyInterface1 dispids verwendbar macht (standardmäßig, aber Sie können die Standardschnittstelle mit dem Klassenattribut konfigurieren).

Und wenn Sie ClassInterfaceType als AutoDual oder AutoDispatch verwenden, werden DISPIDs automatisch generiert, und die manuell definierte wird nicht verwendet.

.NET kombiniert oder zusammenführen die Schnittstellen nicht, und die Tatsache, dass die DISPIDs unterschiedlich sind, ist daher nicht wichtig in diesem Fall ".NET als COM offengelegt", da nur eine Gruppe von DISPIDs (für die Standardschnittstelle) verwendet wird). Beachten Sie, wenn Sie zweimal die gleiche Gruppe von DISPIDs für dieselbe Klasse definieren, wird es gut kompilieren, aber regasm wird sich beschweren und die doppelten ignorieren.

Hier ist eine kleine C++ Programm, das dies alles bestätigt:

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    CoInitialize(NULL); 
    IDispatch *pDispatch; 
    CoCreateInstance(__uuidof(MyClass), NULL, CLSCTX_ALL, IID_IDispatch, (void**)&pDispatch); 
    DISPID dispid; 
    LPOLESTR name1 = L"Name1"; 
    LPOLESTR name2 = L"Name2"; 
    HRESULT hr; 
    hr = pDispatch->GetIDsOfNames(IID_NULL, &name1, 1, 0, &dispid); 
    printf("Name1:%i hr=0x%08X\n", dispid, hr); 
    hr = pDispatch->GetIDsOfNames(IID_NULL, &name2, 1, 0, &dispid); 
    printf("Name2:%i hr=0x%08X\n", dispid, hr); 
    pDispatch->Release(); 
    CoUninitialize(); 
    return 0; 
} 

gibt er dies:

Name1:1 hr=0x00000000 (S_OK) 
Name2:-1 hr=0x80020006 (DISP_E_UNKNOWNNAME) 

Es Sie Änderung AutoDispatch oder AutoDual, wird es ausgegeben diese (ids berechnet werden mit einigem Algorithmus):

Name1:1610743812 hr=0x00000000 
Name2:1610743813 hr=0x00000000 
+0

Es gibt keine Begrenzung eines IDispatch pro COM-Objekt. Nun, IDispatch kann eins sein, aber Objekte implementieren Schnittstellen abgeleitet ** von IDispatch und ihre Anzahl ist nicht begrenzt (und sicherlich sind DISPIDs Methoden dieser abgeleiteten Schnittstellen zugewiesen, nicht IDispacth). –

+0

Ich glaube nicht, dass ich etwas Gegenteiliges gesagt habe. Aber da das gleiche COM-Objekt immer denselben IDispatch-Zeiger zurückgibt, sehe ich kaum, wie Sie zwei Methoden mit demselben DISPID verwenden können, wenn Sie die IDispatch-Schnittstelle verwenden. Nichts erzwingt diese Regel physisch. Wenn es funktioniert (und ich habe nie gesagt, dass der OP-Code nicht so funktionieren würde, wie es ist, wenn Dispid zweimal deklariert wird), liegt das daran, dass der Code am Ende nicht die IDispatch-Schnittstelle verwendet. –

+0

Die Sache ist, dass nur wenige Apps IDispatch abfragen. Stattdessen fragen sie IDispatch-abgeleitete Schnittstellen direkt ab (z. B. sie werden über die Typbibliothek erkannt), sodass es kein Problem gibt, mehrere IDispatch-Implementierungen zu haben, und alle von ihnen sind perfekt verwendbar. Nicht nur von VB6, sondern eher eine Ausnahme (ungewöhnliche Einschränkung). –