When you use a COM object in .NET the .NET framework creates a Runtime Callable Wrapper (RCW) which shows up in the debugger as __ComObject. This allows interoperation between managed .NET and native COM code. One of the major differences between C++ COM and .NET is that in C++ you have deterministic destruction while in .NET objects are finalized at the garbage collector’s leisure. Which mode of operation is preferable is a matter of taste and not the topic of this post (I’ll just say that you can take my deterministic destruction when my cold dead hand goes out of scope) however mixing the two modes can cause problems. The SOP is that the RCW calls AddRef[1] on the COM object it is holding so that it won’t disappear while it’s being used and then, when the RCW is collected by the GC, it will release this reference in its finalizer. This works if you don’t care exactly when the COM object is released and .NET exposes Marshal.ReleaseComObject for cases in which you want to manually control the object’s lifetime. We recently re-implemented a few interfaces (that were previously native COM) in .NET and stuff stopped working. A bit of debugging later I find that we’re calling ReleaseComObject on an object that is now implemented in .NET, this causes an ArgumentException to be thrown. A cow-orker pointed me at a post titled Marshal.ReleaseComObject Considered Dangerous (well at least it’s not harmful), this post describes the problem we were facing and the solution I already put in place (check with Marshal.IsComObject before calling ReleaseComObject) but goes on saying that this isn’t enough since once you call ReleaseComObject the RCW is disconnected from the COM object and can’t be used (and that this can be a problem if the RCW is cached). I thought that this is a theoretical problem (I know we aren’t caching the RCWs) and kept trying to figure out why our code still wasn’t working. It turns out that as in everything in programming it’s all about adding another level. We had three levels, a .NET class (which we’ll call A) a native class (C) and class B which was native in a previous incarnation but is now .NET, A holds a reference to B which hands it a C (via a method or property), but why is this a problem? For that we’ll need to dive a bit deeper into how RCWs work.Read more: I will not buy this blog, it is scratched!
QR:
0 comments:
Post a Comment