I use COM with an old VB6 application.
I changed my code to use DispID in interfaces as it seems to work better than using [ClassInterface(ClassInterfaceType.AutoDual)]
.
But is it allowed to begin in each interface counting from DispID(1), even when a class uses two interfaces?
Does it work this way stable? Or do I missunderstood something?
[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"; } }
}
Is it allowed to begin in each interface counting from DispID(1), even when a class uses two interfaces?
DISPIDs have to be unique within the interface only. You are good to go with two interfaces each one having their own (different) DISPID 1 properties, even if both interfaces are implemented by the same COM object.
Since VB6 is mentioned, however, you need to keep in mind that VB6 won't like 2+ dispatch interfaces implemented on the same COM object, and might be "seeing" only the first/main one. That is, the problem is not DISPID collision (which is not a problem at all), but the fact that VB6 is unable to work correctly with objects exposing 2+ dual interfaces. The reason why this is happening in described on MSDN in Multiple Dual Interfaces:
Because only one IDispatch interface is exposed, clients that can only access your objects through the IDispatch interface will not be able to access the methods or properties in any other interface.
And sadly, it is the case of VB6. Unlike more advanced environments, it is querying interfaces in the way that "methods or properties in any other interface" are inaccessible. Assigning different DISPIDs is not going to help there though.
There is only one IDispatch implementation per COM object, so if you want a call such as IDispatch::Invoke to succeed, you need to have DISPIDs unique per COM object.
EDIT: In fact, after thinking more about it, the question is quite irrelevant, as Hans points out in his comment. Because you define ClassInterfaceType as None, it means .NET will only make the first interface IMyInterface1 dispids usable (by default, but you can configure the default interface using the ComDefaultInterfaceAttribute Class attribute).
And if you use ClassInterfaceType as AutoDual or AutoDispatch, DISPIDs will be auto generated, and the one defined manually will not be used.
.NET does not combine or merge the interfaces, and the fact that the dispids are different is therefore not important in this ".NET exposed as COM" case, as only one set of DISPIDs are used (for the default interface). Note if you define twice the same set of DISPIDs on the same class, it will compile fine, but regasm will complain and ignore the duplicate ones.
Here is a small C++ program that confirms all this:
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;
}
It will output this:
Name1:1 hr=0x00000000 (S_OK)
Name2:-1 hr=0x80020006 (DISP_E_UNKNOWNNAME)
It you change to AutoDispatch or AutoDual, it will output this (ids are calculated using some algorithm):
Name1:1610743812 hr=0x00000000
Name2:1610743813 hr=0x00000000
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With