58

I am trying to use the OpcRcw.da.dll. If I interop this dll inside a test console project everything works, but if I build dll project to do my interop gymnastic and ref my library into my console project I am getting this error:

COM object that has been separated from its underlying RCW cannot be used.

What need to be done to a class lib project to not kill the RCW ref?

Justin
  • 84,773
  • 49
  • 224
  • 367
kevin marchand
  • 615
  • 1
  • 6
  • 5

2 Answers2

88

This can happen for a few reasons, the big ones I know of are below.

Event Handlers Without Strong References to the Delegate

A caller subscribes to an event on the com object without keeping a strong reference to the callback delegate. Here is an example of how to do this correctly and how to not do it: The reason for this is a strong reference needs to be kept to the delegate, if it goes out of scope, the wrapper will release the reference count for the interface and bad things will happen.

public class SomeClass
{
    private Interop.ComObjectWrapper comObject;
    private event ComEventHandler comEventHandler;

    public SomeClass()
    {
        comObject = new Interop.ComObjectWrapper();

        // NO - BAD!
        comObject.SomeEvent += new ComEventHandler(EventCallback);

        // YES - GOOD!
        comEventHandler = new ComEventHandler(EventCallback);
        comObject.SomeEvent += comEventHandler
    }

    public void EventCallback()
    {
        // DO WORK
    }
}

Calls to a disposed Runtime Callable Wrapper

The wrapper has been disposed and calls are being made after it has been disposed. A common way this can happen is if a control is using an activex control or COM object and the controls Dispose() is called out of order.

  • A form gets Close() called.
  • System.Windows.Forms.Close() will call Dispose()
  • Your forms virtual Dispose() will be called which hopefully calls base.Dispose() somewhere. Systems.Windows.Forms.Dispose() will release all COM objects and event syncs on the form, even from child controls.
  • If the control that owns a com object is explicitly disposed after base.Dispose() and if it calls any methods on it's COM object, these will now fail and you will get the error “COM object that has been separated from its underlying RCW cannot be used”.

Debugging Steps

A good way to debug this issue is to do the following:

  1. Write a class that inherits from the Interop class (otherwise known as the runtime callable wrapper or RCW).
  2. Override DetachEventSink
  3. Override Dispose
  4. Call your new class instead of calling the interop class directly
  5. Add breakpoint to DetachEventSink and Dispose
  6. See who is calling these methods out of order

One other thing

This isn't related to this issue but while we are on the topic, unless you know otherwise, always remember to check that the thread your COM objects are being used from are marked STA. You can do this by breaking in the debugger and checking the value returned from:

Thread.CurrentThread.GetApartmentState();
Steve Sheldon
  • 6,421
  • 3
  • 31
  • 35
  • Steven, can you please elaborate a little more about your code sample? So do you mean "for COM" that is the correct way? because in normal .NET event handlers I haven't seen that practice so I was wondering if it only for COM that we should do it like what you suggest? I am having a similar RCW issue and wondering if this is my problem too. Much appreciated Sir. – Bohn Nov 26 '12 at 15:28
  • 5
    I'm not actually sure, that holding references to the delegate in the field will solve an issue with a weak references. Even holding references to the delegate in the comEventHandler field will not not prevent the whole object graph from garbage collection. We still should add additional root for the whole object of type SomeClass to prevent the whole graph from GC. – Sergey Teplyakov Nov 26 '12 at 16:06
  • 2
    @Steve Sheldon: I don't think that we have an issue with strong/weak references here. I posted additional thoughts here - http://stackoverflow.com/questions/13567692/difference-between-two-different-types-of-assigning-events-to-event-handlers/13581940#13581940 – Sergey Teplyakov Nov 27 '12 at 10:21
  • @BDotA - yes, just when using references to com objects. You are fine with regular .net events. – Steve Sheldon Dec 31 '12 at 19:48
  • @SergeyTeplyakov - You are correct to elaborate on that, the whole object graph needs to be rooted also. – Steve Sheldon Dec 31 '12 at 19:48
  • The debugging steps looked particularly interesting to me except I can't implement them because all the objects exposed by the COM object appear to be interfaces rather than classes, and the interfaces do not expose DetachEventSink of Dispose. Any thoughts on how to debug in this case? – BlueMonkMN Oct 07 '13 at 18:50
  • Just had this strongly referenced delegate problem. Daym, this COM doesn't allow you to structure your code properly. – bigkahunaburger Mar 14 '16 at 09:54
  • In my case, I got this error because I close the workbook then I did some work on the xls workbook mistakenly. – karpanai Oct 13 '16 at 15:41
  • in arcobjects multithreading thread must set to STA. after thread completing Thread.CurrentThread.GetApartmentState(); must call. – mehdi Jun 29 '17 at 08:40
41

It's somewhat hard to tell what your actual application is doing, but it sounds like you may be instantiating the COM object and then attempting to access it from another thread, perhaps in a Timer.Elapsed event. If your application is multithreaded, you need to instantiate the COM object within each thread you will be using it in.

AJ.
  • 16,368
  • 20
  • 95
  • 150
  • 2
    I found it out I have a Com event wich use a IConnectionPoint and IConnectionPointContainer and I was releasing the IConnectionPointContainer. that was the error thanks – kevin marchand Oct 14 '09 at 15:53
  • 2
    OK cool. Make sure you post your answer and mark it as the accepted answer for anyone who has this problem in the future. – AJ. Oct 14 '09 at 16:31
  • 1
    "You need to instantiate the COM object within each thread you will be using it in." This is the simplest way, but not technically correct. You can access the COM object on another thread, but if your COM object is apartment threaded, the thread it was created on must still exist and be pumping messages. If the thread it was created on dies, the runtime must Release the COM object, and you will get this error message if you try to access the object on another thread. – Kevin Smyth Feb 19 '13 at 15:14
  • in my case I was using a ManagementEventWatcher and I've forgot to dispose it (or put it in a using statement) – Alessio Dec 10 '14 at 18:54