Prevent out-of-proc server shutdown while it has loaded in-proc server object instances - winapi

I have an application which uses an out of process COM server to access a COM object created by an in-proc COM server. This means that the out of process COM server has to load the in process COM DLL to create the final object which it would then return.
For example:
// Create an object which resides in the out of process COM server
container.CoCreateInstance("HelperServerProcess");
// Grab a reference to an object which resides in an in process COM server DLL,
// hosted by the out of process COM server
object = container.GenerateResults();
// Release the object instantiated by the out of process server
container = NULL; // or return, or go out of scope, etc
// This call will fail, because the out of process server has shutdown unloading
// the inproc DLL hosting <object>
object.DoStuff();
However, once the container object is released, the final server process reference (in CoReleaseServerProcess ) is released, and the server shuts down. This results in an E_RPC_SERVER_UNAVAILABLE error when trying to use the result object. At the same time the in-proc DLL hosted in this EXE server still has outstanding objects and therefor returns S_FALSE from CanUnloadNow.
I think adding IExternalConnection to the EXE server 's class factory to manually do reference counting on the remote references will not help, because the objects registered by the DLL in-proc server will use the DLLs class factory and try using IExternalConnection on this factory. Also, if the server spawns interactive child objects in its process space it wouldn't trigger IExternalConnection either way.
It also isn't possible to modify the DLL's reference counting to use CoAddRefServerProcess / CoReleaseServerProcess as the DLL doesn't have access to the container's shutdown code in case it triggers the last release, and third party DLLs can't be changed anyhow.
The only method which I can think of which might work is adding a loop after the server refcount hits zero, which calls CoFreeUnusedLibraries, and then somehow enumerates all loaded COM DLLs and waits until none are loaded and ensures the server refcount is still zero. This would leak processes if a loaded DLL does not implement CanUnloadNow correctly, and involves messing around with low level COM implmentation details which I would like to avoid.
Is there any easier way to ensure that the COM objects instantiated by class factories of in-proc servers keep the process alive, or to enumerate the class factories of DLLs loaded into the current process and query them for the number of references?
Update: Another method which may work, but sounds very much like the sort of things you aren't supposed to do: intercepting every spawned thread in the process, registering a CoInitialize hook via CoRegisterInitializeSpy, and adding server process reference for every thread that currently has COM initialized.

The out-of-proc EXE can delegate the DLL object rather than return it directly. Have GetResults() return an object that the EXE implements, and have that implementation use the DLL internally as needed. This way, the EXE will not be released until the caller releases the EXE's object, thus keeping the EXE's own refcount active. The EXE's object can implement the same interface that the DLL object implements. This way, the caller does not know (or care) that delegation is being used.

Related

Unable to create a com object using proxy stub dll

Using Visual c++ 6.0, I have created an ATL based .EXE server .
(VC6 as I am dealing with legacy code, a .exe server as I need to test operation in an out of process context, currently the .exe server is essentialy a no op)
I have built and registered the corresponding proxy stub DLL.
I have a client app that does
CoCreateInstance of IUnknown which invokes FinalConstruct in server object and succeeds (so server is correctly invoked)
OleRun of returned IUnknown interface succeeds
QueryInterface on IUnknown pointer for my server object fails with error code of 0x8000402 (No such interface supported) for the IMarshall interface
These steps were copied from (comip.h::CreateInstance)
The problem appears to be that the proxystub dll is not being invoked (it doesn't appear in the Modules list in the IDE, nor in loaded modules list in debug window)
The OleCom Object viewer for my class and interface can be seen here https://skydrive.live.com/redir?resid=AE43106917EBD9E1!191&authkey=!AIOWeS5P3o2mlpw
8891..ca4d is the class interface id for my object
A298...420c is the interface ID for my server object (IDispatch based)
TIA for any assistance
It's possible that your issue is that the component that is implementing the IRunnableObject interface isn't registering itself in the Running Object Table. This will mean that the CoCreateInstance itself will succeed, however, when the object it called on, the RPC code will be unable to find it.
This MSDN page indicates:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms694517(v=vs.85).aspx
Notes to Implementers
The object should register in the running object table if it has a
moniker assigned. The object should not hold any strong locks on itself;
instead, it should remain in the unstable, unlocked state. The object
should be locked when the first external connection is made to the object.
I'm a little worried about why you're also using the IMarshall interface. Normally it's not necessary to write custom marshaling code, thus you won't need to use this interface.
As long as you don't reference a custom interface the default marshaller in ole32.dll or oleauto32.dll will be used. That's most likely why you don't see your proxy being loaded.
In the case of most COM interfaces, the proxies and stubs for standard
marshaling are in-process component objects which are loaded from a
systemwide DLL provided by COM in Ole32.dll.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms692621(v=vs.85).aspx

Remove the process from task manager

Sometimes ActiveX EXE object still remains in task manager even after the object is set to nothing in Client application.
Is there any way to smoothly terminate an Activex thread?
If the ActiveX object is appearing in task manager as a separate process then it must be an out of process COM server, not hosted in a thread in your application.
I can think of two obvious reasons why the server would still be running after your client has released its instance:
Something else still holds a reference (either your process or another)
The server was implemented incorrectly and does not shut down when all references are released

Does COM activation of LocalServer32 EXE from the same user account share an existing process or not?

I have a COM server LocalServer32 EXE started when a client application calls c_com_ptr::CreateInstance (using ATL wrappers.)
On Windows 7, when a second client application running under the same user account also calls c_com_ptr::CreateInstance, a second copy of the EXE is launched running under the same user account. I was under the impression, from a past life, that the second client would share the first EXE.
Is the LocalServer32 process shared, or not? When, or when not? Googling for an answer gives me a huge noise to signal ratio and I can't find the answer.
My CLSID registry key has the LocalServer32 value giving the EXE path, ProgID, Programmable (empty string), TypeLib (GUID), and a VersionIndependentProgId. I have an AppID key.
I do not want to run the EXE as a service, and I don't mind that the process is not shared. I just want to know the rules so I know what to expect (on Windows Server 2003 onwards.)
EDIT: Following Chris' answer below, I examined the CoRegisterClassObject call in my server. I'm using ATL, and I overrode MyServer::RegisterClassObjects to hook into the calling chain to CAtlExeModuleT::RegisterClassObjects and see that ATL is using CLSCTX_LOCAL_SERVER and REGCLS_MULTIPLEUSE.
Changing this to CLSCTX_LOCAL_SERVER and REGCLS_SINGLEUSE causes more processes to be started, depending on the number of COM objects created by the client, as expected.
Still, going back to REGCLS_MULTIPLEUSE, I get one COM server process per COM client process, each server process containing all of the COM objects for its client, as expected, except that if two COM clients are running under the same user account, they each get their own server which is not how I understood REGCLS_MULTIPLEUSE.
Could the difference be that the clients themselves are actually Windows services? (They are.) When a Windows service process running as a user account creates a COM object under REGCLS_MULTIPLEUSE, is this treated differently, causing the observed behavior? Why am I getting more than one process? (And just to clarify, I do not want my COM server to run as a Windows service, but the clients that use it do run as Windows services.)
Also, running the clients as either Local System, or Network Service, REGCLS_MULTIPLEUSE works as I would have expected: only a single COM server EXE process is started. The multiple processes are started when the COM clients are Windows services running under user accounts.
The routing of out-of-process activation requests is controlled by the registration of class objects with the COM Service Control Manager. If the SCM has a usable registered class object, that will be used to service the request. If it doesn't, it will start an exe process instance of the COM server to get one. Whether multiple activation requests are routed to a single COM server exe process therefore depends on the following factors at least (I'm not sure if this is a complete list):
the activation flags specified by the COM server when it calls CoRegisterClassObject to register with the SCM can cause future activation requests to result in a new exe process instance being started, the simplest and commonest case being the use of the flag REGCLS_SINGLEUSE, which allows the registered class object to be used for a single activation only.
Depending on how the class is registered, activation requests from different security contexts may be serviced by different COM server exe instances (it seems this won't apply in your scenario as your client applications run under the same security contexts).

Is it true that COM services can't be used by multiple programs at the same time?

Before the application terminates its
execution, COM must be shut down
again. (Failure to shut down COM could
result in execution errors when
another program attempts to use COM
services .)
The above quote implies that, right?
No it doesn't.
If you fail to properly release all references to an out of process COM server and correctly close down COM it could lead to that instance of that service being in an odd state (everything should be OK after releasing all references, but sometimes COM might cache part of the out of process marshalling layer).
An out of process COM service can be designed to have separate component instances for each client (within or across services) that are completely independent (even if hosted in the same process), in which case it is hard to see how a failure of one client would affect other instances (other than wasting memory on instances until COM finally times them out). If the instances share state they can of course interfere even if the clients operate perfectly to the rules.
It is rather important that you quote the source of that quote so we can get the context. As near as I can see, you got that from a book about DirectShow programming. What it actually refers to is the need to call CoUninitialize().
Yes, that's kinda important. A thread should call CoInitializeEx() to initialize the COM infrastructure before it starts using any of the COM API functions. You really should call CoUninitialize() when that threads ends so stuff is properly cleaned up. Typically at the end of your program's main() function. Failure to do so may make another app fail when it finds a register class factory that in fact is dead.
This otherwise has nothing to do with a COM out-of-process server having to restrict itself in any way. You specify sharing mode with the REGCLS argument to CoRegisterClassObject(). Of course, a server should not exit and call CoUninitialize until all its objects are released.

detect client process termination from EXE COM Server

I'm writing an EXE COM Server that exposes a class that lock a system resource.
In normal execution the client release the resource (the COM executable shutsdown a couple of seconds later.
In abnormal execution, the client app crashes, leaving the com sever with an instance having a positive reference count. The COM executable runs for ~12 minutes until termination. This means that the system resource is locked during this time.
Is there a way to detect client termination instantaneously, as in socket IPC or driver protocol? if not it would seem that COM is inferior to other IPC mechanisms.
I had the same question a couple of years ago. I found the answer here: How To Turn Off the COM Garbage Collection Mechanism. In short: no, there is no way to detect client termination instantaneously. Excerpts:
When a COM client terminates normally,
it releases all references to its
server object. When a client
terminates abnormally however, there
might be outstanding references to the
server object. Without a garbage
collection mechanism, the server code
has no way of knowing when to reclaim
the resources allocated for the COM
object, which can then cause a
resource leak. To address this
problem, COM implements an automatic
garbage collection mechanism in which
the COM resolver process (RPCSS) on
the client machine pings the server
machine on behalf of the client
process.
Alternatives to using COM's GC
protocol (for example, using periodic
application-level "pings"--method
calls that inform the object that
clients are still alive, or using an
underlying transport mechanism such as
TCP keepalives) are demonstrably much
less efficient. Therefore, DCOM's
default GC mechanism should be used
for any objects that must be shut down
when their clients disappear or
otherwise misbehave if those objects
would effectively become memory leaks
on the server.
The resolver on the server machine
keeps track of the pings for each
server object. The ping period is 2
minutes and, currently, it is non-
configurable. When the resolver on the
server machine detects that an object
has not been pinged for 6 minutes, it
assumes that all clients of the object
have terminated or otherwise are no
longer using the object. The resolver
will then release all external
references to the object. It does this
by simply having the object's stub
manager (the COM runtime code that
delivers calls to each object) call
::Release() on the object's IUnknown
interface. At this point, the object's
reference count will be zero so far as
the COM runtime is concerned. (There
may still be references held by local
(same-apartment) clients, so the
object's internal reference count may
not necessarily go to zero at this
point.) The object may then shut
itself down.
NOTE: Garbage collection applies to
all servers regardless of whether
their clients are local or remote, or
a combination of local and remote. The
underlying pinging mechanism is
different in the local case as no
network packets are generated, but for
all practical purposes, the behavior
is the same.

Resources