Overloads in COM interop (CCW) - IDispatch names i

2020-02-06 05:10发布

问题:

I have a managed assembly containing a few classes, and those classes have overloaded methods. I expose the assembly to COM/IDispatch callers via

[ComVisible(true)]

..and also setting the proper Guid, on the assembly itself. I do not define an explicit interface for the COM interop. It's all done dynamically. I run regasm.exe /codebase on the managed DLL and it registers it for COM interop.

When I run OleView, I can see the ProgId's of the various classes in the assembly. But, browsing into those ProgIds, and expanding IDispatch node, there is no TypeLib information for these classes.

Even so, from a script, I can invoke a method that accepts zero arguments or a method that accepts one argument. If there is also an overload that accepts more than one argument, I cannot invoke that method by name. The error I get, consistently, is

Microsoft VBScript runtime error: Wrong number of arguments or invalid property assignment:  <methodname>

From this I understood that COM/IDispatch clients were not able to properly resolve overloaded methods on an object exposed via COM interop.


Then I added

[ClassInterface(ClassInterfaceType.AutoDual)]

...to each of the classes in question. After regasm.exe on the DLL, I can see typelib information for each method, under the IDispatch node.

What I found is that overloaded methods automatically get a name that includes an appended suffix. MethodX will expose overloads in the auto-generated typelib assembly as MethodX, MethodX_2, MethodX_3, and so on.

And I found that by referencing the method names with those suffixes, I could invoke overloaded methods, although not with the common name.

More interestingly, if I then removed the [ClassInterface(ClassInterfaceType.AutoDual)] from the classes, I could still invoke the overloaded methods in this way, thus avoiding the Wrong number of arguments or invalid property assignment error.

My question is: is this behavior - appending numeric suffixes to the member names - stable? documented? dependable?

回答1:

COM does not support method overloading, so .NET COM Interop layer has to improvise. I'm not sure if name mangling as you described as documented anywhere, but even if it is, I don't think that using it is a good idea - it's still pretty inconvenient API for COM users. If you want to expose your classes to COM, the best way is to write a distinct COM-friendly [ComVisible] interface, and hide the class itself. The correct way to handle overloads in a COM-friendly way would be have a single method with some [Optional] arguments (and delegate to your corresponding .NET overloads).



回答2:

In my experience, having interop generate Method, Method_1, Method_2, etc. is normal and stable but not really desirable. It's annoying that overloads don't cross the Managed/Unmanaged boundary. Instead of having it arbitrarily add a numeric suffix to my methods, I try to refactor the COM-Visible methods into separate methods with unique names so it's more obvious to the COM consumer what is being called.



回答3:

Yes, it's documented on MSDN:

  • Advanced COM Interoperability: Exported Member Conversion

Since changing it would be a "breaking change" of a documented feature, I guess you can rely on it being "stable". The big disadvantage is that your method names depend on the order in which they are defined in the source file.

Note, though, that COM supports optional arguments, so using them might be a viable alternative to overloads. If you need to add overloads to be binary-compatible to old .NET clients of your library, I found the following pattern to be useful:

// used for binary-compatibility with .NET clients who currently use 
// the old, one-parameter version
[ComVisible(false)]
void myMethod(String oneParameter) {   
    ... 
}   

// since this is the first COM-visible version, it is assigned the "correct" name.
void myMethod(String oneParameter, int newParameter = 0) {
    ...
}

Since COM supports optional parameters, both myMethod(string) and myMethod(string, int) will work.