Search code examples
comregistration-free-com

Consuming EXE server through quasi Isolated COM


I have been able to use manifests and especially the MSBuild task GenerateApplicationManifest so that our main application uses Isolated COM. I can create all the COM objects implemented in DLLs that I need without having to register the DLLs on my client machine. But, I'm greedy...

Our application suite also has some separate applications that are invoked through COM, generally. For these, it is said that you can't do EXE to EXE isolated COM. Strictly speaking, that is true, but I have gotten 90% of the way and on other forums I have seen others giving clues to get the rest of the way there.

For my EXE server, I have a entry in the manifest with the name of the EXE server and a sub entry in that entry so that when the ATL server calls LoadRegTypeLib(), the call will succeed. That works.

Of course, the tricky part is that you cannot put a entry for the EXE server in the client application manifest and expect CoCreateInstance() to succeed (by launching the server EXE and doing all the other stuff COM does.)

I am able to fake quite a bit because I know what EXE server to launch. I can call CreateProcess() and then call WaitForInputidle() in the client app to get my server ready for CoCreateInstance() in the client app.

If I call CoCreateInstance() and ask for the IDispatch interface in the client app, the call succeeds and I can call Invoke() and everything works.

Now here comes the greedy part...

It's all well and good that IDispatch works, but I want to be able to call through my dual interfaces derived from IDispatch. I want to do that because I have lots of code written that way and the syntax is simpler and the exception handling is already there.

However, when I call QueryInterface()for the dual interface on my IDispatch interface, I get an E_NOINTERFACE return. I have set breakpoints in my ATL server object in the server EXE and can confirm that on the server side, it finds the interface and returns S_OK. So, it seems like somehow the interface is not able to be marshalled back to the client.

So, the question is, How can I get the QueryInterface() for my custom/dual interface to succeed? I've tried various combinations of using <comInterfaceProxyStub> and <comInterfaceExternalProxyStub> in my client manifest (and server manifest) to try and marshal the interface, but I am still seeing the E_NOINTERFACE return in my client.

I saw a comment by Hans Passant from several years ago in a different forum about maybe needing a separate proxy/stub DLL to marshal the interface, but there wasn't much detail.

Is it even possible to solve this in a registration free context? Is it necessary to create a proxy/stub library? If so, what would the manifest entries look like in my client application (and/or server app and/or proxy/stub DLL)?


Solution

  • If you have a proxy/stub DLL, include it as a file element with a child comInterfaceProxyStub element for each interface it handles (don't forget the threadingModel attribute).

    If you have a type library, include it as a file element with a child typelib element, and add a comInterfaceExternalProxyStub element for each interface in the type library (don't forget the tlbid attribute), where proxyStubClsid32 is the automation marshaler: "{00020424-0000-0000-C000-000000000046}".

    For instance, if you use standard marshaling (proxy/stub DLL):

    <assembly ...>
        <file name="myps.dll">
            <comInterfaceProxyStub iid="{iid1}"
                                   name="IMyDualInterface1"
                                   baseInterface="{00020400-0000-0000-C000-000000000046}"
                                   numMethods="8"
                                   proxyStubClsid32="{proxyStubClsid32}"
                                   threadingModel="Free"
                                   />
        </file>
    </assembly>
    

    If you use type library marshaling:

    <assembly ...>
        <file name="mylib.tlb">
            <typelib tlbid="{tlbid}"
                     version="1.0"
                     helpdir=""
                     />
        </file>
        <comInterfaceExternalProxyStub iid="{iid2}"
                                       baseInterface="{00020400-0000-0000-C000-000000000046}"
                                       numMethod="8"
                                       name="IMyDualInterface2"
                                       tlbid="{tlbid}"
                                       proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
                                       />
    </assembly>
    

    In fact, comInterfaceExternalProxyStub applies to any other registered (non-isolated) proxy/stub. For instance:

    <assembly ...>
        <comInterfaceExternalProxyStub iid="{iid2}"
                                       baseInterface="{00000000-0000-0000-C000-000000000046}"
                                       numMethod="4"
                                       name="IMyInterface3"
                                       proxyStubClsid32="{proxyStubClsid32}"
                                       />
    </assembly>
    

    where, in this case, {proxyStubClsid32} is a registered proxy/stub CLSID.


    If my memory serves me right, back when Windows XP was still supported, I've successfully tried using comInterfaceExternalProxyStub for interfaces in a proxy/stub DLL and then declaring the respective comClass elements in the proxy/stub file element, effectively not needing the comInterfaceProxyStub element.

    However, this is not a good practice, comInterfaceExternalProxyStub should really be used only for external proxy/stubs, as the way it is documented, it sounds like the COM infrastructure is allowed to not look for in isolated CLSIDs when activating the required proxy/stub.