1
votes

I don't know if there is an API that makes this possible or if I would have to roll my own. Here is what I'm trying to accomplish.

I have an application that connects to an NT service to start a session with another COM server.

  • Application, the client.
  • Broker NT Service; (system account context).
  • Session COM Service; (system account context, will impersonate user as needed).

The session server will have a running instance for every application instance that connects to the NT service. The application can request that the session server load COM library DLLs and host objects and services from those DLLs in the session server. The DLLs register via registration-free activation.

Creating objects from the session server and passing them back to the application works fine as long as they are IDispatch derived, which is a requirement of entire system since it is expected that scripting languages may use this, and that is the interface requested. C++ application may also use objects hosted in the session server. But IDispatch is an overly verbose interface to deal with in C++.

My question is this:

Given that the DLLs being hosted have dual custom interfaces that the application does know about, and type information about those interface can be read by the application via ITypeInfo; Is there an API that at runtime will create a proxy to mimic the original custom interface if I can provide it the IDispatch interface, which also carries the ITypeInfo information. All the proxy needs is call the IDispatch interface, but appear to C++ as the custom interface. A more optimal solution would be to use the same proxy, the default OLE Automation proxy, that the DLLs registered in its manifest.

I cannot register the proxy/stubs for the DLLs since multiple application may have the same modules, but differing in version, hence the use of registration-free activation.

2
I'm not sure if there's way to use dual interfaces directly under this particular reg-free scenario. But you can automatically generate strong-typed IDispatch wrappers via #import with raw_dispinterfaces and/or no_dual_interfaces options, to be used from the client side.noseratio
Interesting idea, I'll give it a try.Matthew
@Noseratio; This does exactly what I wanted, though not the most optimal as I knew asking about it. It still produces a perfect mock over the IDispatch interface as needed. Can you please post a proper answer with examples though so I can mark this as answered? Also include the fact that a normal C/C++ cast must be used to switch it from IDispatch to the mock interface since QueryInterface will not work.Matthew

2 Answers

2
votes

One option is to give up on the hard-typed dual interface and use IDispatch-only dispinterfaces via hard-typed, compile-time generated smart pointer wrappers. You'd use VC++ #import with raw_dispinterfaces and/or no_dual_interfaces options to generate those.

The COM marshaler doesn't need a type library to marshal IDispatch::Invoke calls. You'd however need to compile against the version of the type library/DLL you're going to run side-by-side. Or at least make sure the DISPIDs and method signatures remain the same across all versions of the COM DLL. AFAIR, the generated smart pointers don't use IDispatch::GetIdsOfNames, so DISPIDs are hard-coded.

The IDispatch::Invoke performance might be sub-optional compared to direct dual interface calls, but I don't think it matters, giving the inter-process call scenario you've described. A marshaled out-of-proc COM call is much more expensive than a in-process IDispatch::Invoke call.

2
votes

Any [oleautomation] interface (and any [dual] interface) described in a type library can use the type library marshaler.

Here, you trade the trouble of finding a proxy-stub DLL to that of finding a type library. So, you declare the interface and the type library in your assembly's manifest (directly under the assembly element) like this:

<comInterfaceExternalProxyStub name="IFooBar"
                               iid="{IIIIIIII-IIII-IIII-IIII-IIIIIIIIIIII}"
                               proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
                               tlbid="{TTTTTTTT-TTTT-TTTT-TTTT-TTTTTTTTTTTT}" />

<!-- This also works for a type library embedded in a DLL -->
<file name="FooBar.tlb">
    <!-- If you have multiple embedded type libraries, use the resourceid attribute -->
    <typelib tlbid="{TTTTTTTT-TTTT-TTTT-TTTT-TTTTTTTTTTTT}"
             version="1.0" />
</file>

If your interface were not an [oleautomation] interface, and you'd want to isolate the proxy-stub DLL, you'd use something like this:

<file name="FooBarPS.dll">
    <comInterfaceProxyStub name="IFooBar"
                           iid="{IIIIIIII-IIII-IIII-IIII-IIIIIIIIIIII}"
                           proxyStubClsid32="{PPPPPPPP-PPPP-PPPP-PPPP-PPPPPPPPPPPP}"
                           threadingModel="Both" />
</file>

A comInterfaceProxyStub is much like a comClass, but focused on proxy/stubs and it gets associated with an interface.

You can accomplish the same effect with a pair of comInterfaceExternalProxyStub (under an assembly element level) and comClass (under a file element), in case you want to test with and without an isolated proxy/stub DLL, by (un)commenting the proxy/stub file section:

<comInterfaceExternalProxyStub name="IFooBar"
                               iid="{IIIIIIII-IIII-IIII-IIII-IIIIIIIIIIII}"
                               proxyStubClsid32="{PPPPPPPP-PPPP-PPPP-PPPP-PPPPPPPPPPPP}" />

<file name="FooBarPS.dll">
    <comClass description="PSFactoryBuffer"
              clsid="{PPPPPPPP-PPPP-PPPP-PPPP-PPPPPPPPPPPP}"
              threadingModel="Both" />
</file>

I'm not sure, but if your standard proxy/stub DLL is used for more than one interface, you must use this approach as well.


EDIT: It seems none of this is new to you. Your problem is that in the session service, even though you activate the dynamically loaded libraries' manifest, that state remains only in the current thread. So, COM worker threads (e.g. RPC threads) will not know about your interfaces, coclasses and proxy/stubs.

The error you see in the broker service (REGDB_E_IIDNOTREG) might originate in the marshaling back from the session service. You don't get that error in the session service because it happens after your methods return.

However, it might be happening in the broker service, as it's natural: it doesn't load any libraries, much less their manifests.

The approach I suggest you take is to make the session service and the broker service have manifests that depend on the assemblies where you declare the registration-free COM information. This way, it'll all be part of the default activation context and you don't have to do anything regarding activation contexts.

Remember, you have no control over the activation context of a thread you don't own, other than having the default activation context include what you need upfront.

Regarding this part of your question:

I cannot register the proxy/stubs for the DLLs since multiple application may have the same modules, but differing in version, hence the use of registration-free activation.

It's not clear to me what you're trying to say. If your modules are backwards compatible, you don't need to worry. If they're not, use different CLSIDs/ProgIDs.

I hope you don't mean you're using the same IIDs with actually different interfaces, as that is a violation of COM. The best way to solve this problem is to not do it, period. Another way is to have dedicated threads with dedicated activation contexts, both in the session service and in the broker service, which as you've probably seen with only the session service, this is a very brittle approach.

As I see it, you may have no need for COM isolation at all. But if you still want it, you need to do it for both services, broker and session, and do it from their manifests, instead of at runtime.