views:

177

answers:

7

In COM how does one verify that a pointer to a COM object still has a valid object on the other end?

I have an issue where this following bit of code attempts to check if the m_pServer pointer is still alive, however when that application exposing that interface is killed this bit of code crashes the application. Can anybody advice on how to check the pointer before using it?

if (FAILED(m_pServer->StillAlive())) { // do something }

This code fails if m_pServer is no longer in memory.

EDIT:

EXCEPTION: First-chance exception at 0x7728fbae (kernel32.dll) in Client40.exe: 0x800706BA: The RPC server is unavailable.

CALL STACK:

    kernel32.dll!RaiseException()  + 0x58   
    rpcrt4.dll!RpcRaiseException()  + 0x3e  
    rpcrt4.dll!NdrProxyErrorHandler()  + 0x28   
    rpcrt4.dll!NdrProxySendReceive()  + 0xa4    
    rpcrt4.dll!NdrProxySendReceive()  + 0x119   
    rpcrt4.dll!NdrComplexArrayMarshall()  + 0x26d   
--> Client40.exe!SlaveDriver::run()  Line 97 + 0x14 C++  //Runs while loop, to handle requests
    Client40.exe!DThread::tfunc(void * thisptr=0x0047e694)  Line 56 + 0xd   C++
    Client40.exe!_threadstartex(void * ptd=0x01b20e00)  Line 241 + 0xd  C
    kernel32.dll!BaseThreadInitThunk()  + 0x12  
    ntdll.dll!RtlInitializeExceptionChain()  + 0x63 
    ntdll.dll!RtlInitializeExceptionChain()  + 0x36 
+1  A: 

It is up to you to manage the lifetime of the COM object. As long as you have a live pointer to the interface, you have to have at least one AddRef() call on the interface. The final Release() call will delete the object and the pointer goes stale. Using it afterwards will crash your program randomly, usually with an AV. There is no way to detect if it is stale.

You could set m_pServer to NULL when you make your final Release() call.

Hans Passant
So if the application exposing this interface dies, then the one using it will never know until it tries to use it??? Or is there a way to prevent that???
Tony
I guess his problem is that the out-proc server is forcible terminated.
sharptooth
@Tony, you'll get a HRESULT error code from RPC, usually RPC_E_SERVERDIED. Killing the server doesn't kill the proxy for it. You prevent it by not killing the server.
Hans Passant
A: 

Fundamentally, you never check this. It's can only give a useless answer. Consider the following hypothethical example:

if (CoCheckAlive(ptr)) {
  ptr->Foo();
}

Even if CoCheckAlive returned true, that result would not guarantee that the remote server would stay alive long enough to make the next call. That's why you just make the call, and handle failure cases (including RPC_E_SERVERDIED) afterwards.

MSalters
So the FAILED(m_pServer->StillAlive()) is essentially usesless??
Tony
Also how would one kill the client (out of proc com server) properly if its server was no longer available???
Tony
Yes, the `->StillAlive` way of checking is equally useless. It too only tells you that the server was alive, not whether it will be alive when you do the real call. I don't understand your second question, but it appears to be a good question. Formulate it clearly, and then seach StackOverflow. Chances are it's already answered, or else you have a good new question.
MSalters
There are valid use cases for this behaviour where the purpose is to provide informed feedback rather than to guarantee a later call. Consider an idling client with a check on a timer that writes an error log or raises a dialog noting when the server connection was lost.
morechilli
A: 

Since it is a Exception couldn't you just __try it? You could set up an Exception filter to just catch the RPC-Server not available Exception. See http://msdn.microsoft.com/en-us/library/s58ftw19%28VS.80%29.aspx for further explanation on how to do this.

David Feurle
+4  A: 

What you're trying to do here is simply not possible. Because m_pServer lives in another process you're really asking the following question

Is Process XXX still running?

This is simply not an answerable question in the world of windows (or linux / unix). You can never reliably answer the question because the moment the question is answered it's result can be invalidated. Process's can terminate at any point in time including between your check and the access of the COM object.

However a slightly different version of this question is answerable

Was Process XXX still running?

The only way to approach a problem like this is to perform the operation in question and simply expect it to fail. If it succeeds then great you've answered the modified version of the question. If it fails then you need to interpret the failure to determine if the operation failed or the process is gone.

This is the only way to properly handle this situation.

JaredPar
A: 

Just write a wrapper method that checks for failed and catches the exception and report it as a failure.

Mike
+1  A: 

First-chance exceptions from COM are caught by the proxy and reported as error codes. If you continue past the first-chance exception, you should see the proxy return RPC_SERVER_UNAVAILABLE, which you should handle as appropriate.

Eric Brown
A: 

1 - Check the HRESULT inside your code - not just general FAILED - that will give you the place from which you can report more to user and or start alternative action (like close the app gracefully). Actual HRESULT may or may not be exactly RPC_SERVER_UNAVAILABLE - so firt assign it ti a var so that you can see it in debugger (and don't go jumping at first chance exceptions - they are for debbuging lower layers of code).

2 - If you actually get exception thrown in your C++ code then first place a general catch around it and then look at it in debugger. This case would probably come from come kind of smart pointer trying to be too smart :-)

In any case, leave your code a flag to know what happened and close COM references gracefully - just because the server died doesn't mean that you have to leak :-)

ZXX