5
votes

I am calling an externally provided COM DLL for which I have generated a COM interop wrapper. For the sake of argument, let's call the interface I want to call IEnumFoo.

IEnumFoo has the typical COM enumerator pattern:

HRESULT Next ( 
   ULONG        celt,
   IFoo**       rgelt,
   ULONG*       pceltFetched
);

where the first parameter is the number of desired results, the second parameter is a buffer to which results are written, and the last parameter describes the number of results actually written.

When I choose "Add Reference" and point Visual Studio at this DLL, it generates a COM Interop Assembly with the following signature:

void Next(uint, out IFoo, out uint)

This only allows the .NET code to request a single object at a time, which can add a significant amount of overhead to using these APIs.

Is there some mechanism I can use to generate a version of Next which would allow me to provide more IFoo "slots" above which would make the marshaler happy? (I'm not averse to editing the IL in the interop assembly by hand :) )

3

3 Answers

4
votes

The proper signature for this would be like so:

void Next(
    uint celt,
    [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] IFoo[] rgelt,
    out uint pceltFetched);

According to MSDN, at least, there's no mechanism to generate this automatically. Even if the original IDL for the interface had length_is applied to rgelt, that information is lost in the typelib. So you'll need to edit the interop assembly manually.

One other option is to define this particular interface entirely by hand in your main assembly, and simply ignore the generated interop version. Remember that when doing casts on RCWs, any interface with a matching GUID (i.e. the one for which QueryInterface is successful) will work, so you can actually have several different managed interfaces that present differing views of the same COM interface.

2
votes

Not answer to your question but a suggestion to try a different approach. I'd create a C++/CLI wrapper to enumerate through the COM interface in unmanaged code (thus avoiding the marshaling overhead) and then build a managed List or other container in which you return your objects.

This is almost guaranteed to be easier than hand-tweaking the IL of the interop assembly and you can debug it easily, too. The unmanaged C++ code will be fairly simple, just like the managed wrapper around that.

1
votes

If the object implementing this interface is native, then just redefine the interface in your code as it should be, making sure to use the same ComImport and Guid attributes on the interface. Then take the object and cast to your interface. You can call through that interface just fine.

Remember: interop assemblies aren't magic, you can always define an interface manually.