Why does client application report “RPC is Unavailable” after compiled as a Release Version?
One customer has a DCOM application, which was developed by unmanaged C++. The client application is the managed code. When the client application is compiled as a Debug version, everything runs smoothly, however, after it runs as a Release version, an “RPC is Unavailable” happens always.
While we look at the issue, my first thought is that the DCOM server application crashes for some reason. We used debugging tool Adplus.vbs , and confirmed that the DCOM server application just exits normally without any exceptions.
In my opinion, another possible reason is that the DCOM objects has no references, then the DCOM server process quits accordingly by following its mechanism. For more regarding DCOM architecture and overview, please refer to:
DCOM Technical Overview
Why does the DCOM objects reference counter suddenly become Zero and why this only happens to Release mode?
We used the tttracer to capture the client application and DCOM server process. Found the client application hits the “RPC is unavailable” at this call stack:
OS Thread Id: 0x55c (1)
0012df48 79e7cc26 [GCFrame: 0012df48]
0012e150 79e7cc26 [GCFrame: 0012e150]
0012e35c 79e7cc26 [HelperMethodFrame_PROTECTOBJ: 0012e35c] System.RuntimeType.InvokeDispMethod(System.String, System.Reflection.BindingFlags, System.Object, System.Object, Boolean, Int32, System.String)
0012e3e4 7928e3a2 System.RuntimeType.InvokeMember(System.String, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object, System.Object, System.Reflection.ParameterModifier, System.Globalization.CultureInfo, System.String)
0012e530 0a4f6528 Mycomponent.UnisimMycomponent.AddStageDataWithLateBinding(System.Type, System.Object, System.Object)
0012e6bc 0a4f5ec0 Mycomponent.UnisimMycomponent.GetFlowDataWithLateBinding(System.Type, System.Object, Mycomponent.DataStructure.SimulationFile)
0012e71c 0a4f5af0 Mycomponent.UnisimMycomponent.GetSubFlowsWithLateBinding(System.Type, System.Object, System.Object, Mycomponent.DataStructure.SimulationFile, System.String)
0012e75c 0a4f5b26 Mycomponent.UnisimMycomponent.GetSubFlowsWithLateBinding(System.Type, System.Object, System.Object, Mycomponent.DataStructure.SimulationFile, System.String)
0012e79c 0a4f586a Mycomponent.UnisimMycomponent.AddUnisimSimFileWithLateBinding(Mycomponent.DataStructure.SimulationFile, System.String)
Based on the call stack, we double checked the related code, it is like this:
object realVar = mytype.InvokeMember("myPropertyA", BindingFlags.GetProperty, null, stage, null);
double val = (double) mytype.InvokeMember("GetValue", BindingFlags.InvokeMethod, null, realVar, null);
The problem turns out to be a managed object that holds a pointer to an unmanged object and making a p/invoke with no further references to the object, after the p/invoke is garbage collected before the p/invoke is finished. In this case, because objects have not been referenced any more at this moment due to garbage collection in managed client application, then the DCOM server exited “smoothly”. Finally the “RPC is unavailable” error happens.
How to keep the unmanaged object reference while calling unmanaged object through P/Invoke, so that DCOM server process will not exit unexpectedly? The answer is we can use GC.KeepAlive().
The purpose of the KeepAlive method is to ensure the existence of a reference to an object that is at risk of being prematurely reclaimed by the garbage collector. A common scenario where this might happen is when there are no references to the object in managed code or data, but the object is still in use in unmanaged code such as Win32 APIs, unmanaged DLLs, or methods using COM.
The important thing is to code this KeepAlive method at the end, not the beginning, of the range of instructions where obj must be available.
So why in debug mode we didn’t experience this issue? Chris’ blog has mentioned this point:
However if you compile the code in debug mode, the JIT will extend lifetimes of references to the end of their enclosing method. In this case, KeepAlive actually isn’t necessary. But in the release case, a reference should be live until the last line of code that references it.
After customer followed the guideline to use KeepAlive, the application works well in Release Mode now.