6

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"; } }
}
jeb
  • 78,592
  • 17
  • 171
  • 225
  • 3
    The first interface you list is the only one that VB6 can see when it late-binds. It will be the interface that's marked as the [Default] interface. So not a real problem since the other interface isn't usable anyway. Although that's probably a real problem ;) – Hans Passant Apr 25 '13 at 20:11

3 Answers3

6

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.

Roman R.
  • 68,205
  • 6
  • 94
  • 158
3

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
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • There is no limit of one IDispatch per COM object. Well, IDispatch can be one, but objects implement interfaces **derived** from IDispatch and their number is not limited (and certainly DISPIDs are assigned to methods of those derived interfaces, not IDispacth). – Roman R. Apr 25 '13 at 15:14
  • I don't think I said anything contrary to what you say. But since the same COM object will always return the same IDispatch pointer, I hardly see how you can use two methods with the same DISPID when you're using the IDispatch interface. Nothing physically enforces that rule. If it works (and I never said OP's code wouldn't work as is, with the same Dispid declared twice), it's because the code is not using the IDispatch interface in the end. – Simon Mourier Apr 25 '13 at 15:51
  • The thing is that few apps query IDispatch. Instead, they query IDispatch-derived interfaces directly (e.g. they are discovered via type library), so there is no problem of having multiple IDispatch implementations and all of them are perfectly usable. Not just by VB6, but it is more like an exception (unusual limitation). – Roman R. Apr 25 '13 at 16:25
  • There is no multiple IDispatch implementations for one COM Object. COM mandates one interface -> one pointer. If it works, it's because many clients don't use IDispatch at all, they don't use GetIDsOfNames neither Invoke, they use the V-Table and the target interface directly, whether it derives from IDispatch or not (including VB6). But try VBScript with the same IDs, or use Object declaration in VB6 and you'll will not be able to use two functions with the same id. http://support.microsoft.com/kb/245115 – Simon Mourier Apr 25 '13 at 16:34
  • I did COM objects with 2+ IDispatch interfaces million times. You are not correct here. If you take a look at http://msdn.microsoft.com/en-us/library/ds84x0xt%28v=vs.80%29.aspx you will see that a property is identified by IID + DISPID, not DISPID alone. My multi-dispatch COM objects even work with JS/VBS through support of `IDispatchEx`, but it's a different story. Environments like .NET and Delphi easily and flawlessly work with COM objects implementing multiple IDispatch-derived interfaces. – Roman R. Apr 25 '13 at 16:45
  • FYI, MSDN pages I linked above states it clear: "... if your object supports multiple dual interfaces" – Roman R. Apr 25 '13 at 16:50
  • You're confusing multiple dual interfaces with multiple IDispatch implementation. http://msdn.microsoft.com/en-us/library/36fx03c8(v=vs.110).aspx . Clients that can only access IDispatch of a COM object. will have problems with duplicate DISPIDs. That's all I'm saying. – Simon Mourier Apr 25 '13 at 17:32
-1

enter image description here

DISPID MUST BE UNIQUE HERE 8 IS THE DISPID .I IT REPEATING .IT MUST BE UNIQUE

lava
  • 6,020
  • 2
  • 31
  • 28