2013-04-25 13 views
5

Używam COM ze starą aplikacją VB6.DispID musi być unikalny dla różnych interfejsów?

Zmieniłem swój kod, aby używać DispID w interfejsach, ponieważ wygląda na to, że działa lepiej niż przy użyciu [ClassInterface(ClassInterfaceType.AutoDual)].

Ale czy można rozpocząć w każdym interfejsie licząc od DispID (1), nawet jeśli klasa używa dwóch interfejsów?

Czy działa w ten sposób stabilny? Czy może coś przegapiłem?

[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

Pierwszy wymieniony interfejs jest jedynym, który VB6 może zobaczyć po późnym wiązaniu. Będzie to interfejs oznaczony jako interfejs [Domyślny]. Więc nie jest to prawdziwy problem, ponieważ drugi interfejs i tak nie jest użyteczny. Chociaż to prawdopodobnie jest prawdziwy problem;) –

Odpowiedz

5

Czy można rozpocząć w każdym interfejsie licząc od DispID (1), nawet jeśli klasa używa dwóch interfejsów?

Wyświetlacze DISPID muszą być unikalne tylko w interfejsie. Dobrze jest przejść z dwoma interfejsami, z których każdy ma własną (inną) właściwość DISPID 1, nawet jeśli oba interfejsy są implementowane przez ten sam obiekt COM.

Od czasu VB6 należy jednak pamiętać, że VB6 nie spodoba się 2+ interfejsom dyspozytorskim zaimplementowanym na tym samym obiekcie COM i może "widzieć" tylko pierwszy/główny. Oznacza to, że problemem nie jest kolizja DISPID (która w ogóle nie jest problemem), ale fakt, że VB6 nie jest w stanie poprawnie pracować z obiektami odsłaniającymi 2 + podwójne interfejsy. Powodem, dla którego tak się dzieje w opisany w witrynie MSDN w Multiple Dual Interfaces:

Ponieważ tylko jeden interfejs IDispatch jest narażona, klienci mają dostęp tylko do obiektów poprzez interfejs IDispatch nie będzie w stanie uzyskać dostęp do metod i właściwości w żadnej inny interfejs.

I niestety tak jest w przypadku VB6. W przeciwieństwie do bardziej zaawansowanych środowisk, sprawdza interfejsy w taki sposób, że "metody lub właściwości w dowolnym innym interfejsie" są niedostępne. Przypisywanie różnych DISPIDów nie pomoże jednak.

2

Jest tylko jedna realizacja IDispatch za obiekt COM, więc jeśli chcesz zadzwonić, takich jak IDispatch::Invoke aby odnieść sukces, trzeba mieć DISPID unikalnych za obiekt COM.

EDYCJA: W rzeczywistości, po bardziej szczegółowym przemyśleniu, pytanie to jest zupełnie nieistotne, jak podkreśla Hans w swoim komentarzu. Ponieważ definiujesz ClassInterfaceType jako None, oznacza to, że .NET będzie tylko tworzył pierwszy interfejs IMyInterface1, który nadaje się do użytku (domyślnie, ale możesz skonfigurować domyślny interfejs przy użyciu atrybutu klasy ComDefaultInterfaceAttribute).

Jeśli użyjesz ClassInterfaceType jako AutoDual lub AutoDispatch, DISPID zostaną wygenerowane automatycznie, a zdefiniowane ręcznie nie będą używane.

.NET nie łączy ani nie łączy interfejsów, a fakt, że dyski są różne, nie ma zatem znaczenia w tym przypadku ".NET expected as COM", ponieważ używany jest tylko jeden zestaw identyfikatorów DISPID (dla domyślnego interfejsu). Uwaga: jeśli zdefiniuj dwa razy ten sam zestaw DISPIDów na tej samej klasie, będzie się on kompilował dobrze, ale regresja będzie narzekać i ignorować duplikaty.

Oto mały program w C++, który potwierdza to wszystko:

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; 
} 

Będzie wyjście to:

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

to zmienić, aby AutoDispatch lub AutoDual, to będzie wyjście to (IDS są obliczane korzystaniu z niektórych algorytm):

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

Nie ma limitu jednego IDispatch na obiekt COM. No cóż, IDispatch może być jednym, ale obiekty implementują interfejsy ** pochodzące ** z IDispatch i ich liczba nie jest ograniczona (iz pewnością DISPIDs są przypisane do metod tych pochodnych interfejsów, a nie IDispacth). –

+0

Nie sądzę, żebym powiedział coś przeciwnego temu, co mówisz. Ale ponieważ ten sam obiekt COM zawsze zwraca ten sam wskaźnik IDispatch, prawie nie widzę sposobu, w jaki można użyć dwóch metod z tym samym DISPID, gdy używasz interfejsu IDispatch. Nic fizycznie nie egzekwuje tej reguły. Jeśli to działa (i nigdy nie powiedziałem, że kod OP nie zadziała tak jak jest, z tym samym Dispidem zadeklarowanym dwa razy), to dlatego, że kod nie używa w końcu interfejsu IDispatch. –

+0

Chodzi o to, że kilka aplikacji wysyła zapytanie do IDispatch. Zamiast tego bezpośrednio wyszukują interfejsy pochodzące od IDispatch (np. Są odkrywane przez bibliotekę typów), więc nie ma problemu z wieloma implementacjami IDispatch i wszystkie z nich są doskonale użyteczne. Nie tylko przez VB6, ale jest bardziej jak wyjątek (nietypowe ograniczenie). –

Powiązane problemy