无法使用已与其基础RCW分离的COM对象

kev*_*and 48 interop

我正在尝试使用OpcRcw.da.dll.如果我在测试控制台项目中互操作这个dll一切正常,但如果我构建dll项目来进行我的互操作体操并将我的库引入我的控制台项目中,我收到此错误:

无法使用已与其基础RCW分离的COM对象.

需要对类lib项目做些什么来不杀死RCW ref?

Ste*_*don 78

出现这种情况有几个原因,我知道的最重要的原因如下.

没有强烈引用代表的事件处理程序

调用者在com对象上订阅一个事件,而不保留对回调委托的强引用.下面是一个如何正确执行此操作以及如何不执行此操作的示例:原因是需要将强引用保留给委托,如果超出范围,则包装器将释放接口的引用计数坏事会发生.

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
    }
}
Run Code Online (Sandbox Code Playgroud)

调用已处置的运行时可调用包装器

包装材料已经处理完毕,并在处理好后再进行调用.一种常见的方法是,如果控件使用的是activex控件或COM对象,并且控件Dispose()被无序调用.

  • 表单调用Close().
  • System.Windows.Forms.Close()将调用Dispose()
  • 您的表单将调用虚拟Dispose(),希望在某处调用base.Dispose().Systems.Windows.Forms.Dispose()将释放窗体上的所有COM对象和事件同步,甚至是子控件.
  • 如果拥有com对象的控件在base.Dispose()之后显式处理,并且如果它调用它的COM对象上的任何方法,则它们现在将失败并且您将得到错误"已与其基础RCW分离的COM对象不能使用".

调试步骤

调试此问题的好方法是执行以下操作:

  1. 编写一个继承自Interop类的类(也称为运行时可调用包装器或RCW).
  2. 覆盖DetachEventSink
  3. 覆盖Dispose
  4. 调用新类而不是直接调用interop类
  5. 将断点添加到DetachEventSink和Dispose
  6. 查看谁不按顺序调用这些方法

另一件事

这与此问题无关,但在我们讨论该主题时,除非您另有说明,否则请务必检查您的COM对象使用的线程是否标记为STA.您可以通过打破调试器并检查从以下位置返回的值来执行此操作:

Thread.CurrentThread.GetApartmentState();
Run Code Online (Sandbox Code Playgroud)

  • 我不确定,在该字段中持有对该委托的引用将解决弱引用的问题.即使在comEventHandler字段中保持对委托的引用也不会阻止整个对象图进行垃圾回收.我们仍然应该为SomeClass类型的整个对象添加额外的root,以防止GC中的整个图形. (4认同)
  • @Steve Sheldon:我认为我们在这里没有强/弱引用的问题.我在这里发布了其他想法 - http://stackoverflow.com/questions/13567692/difference-between-two-different-types-of-assigning-events-to-event-handlers/13581940#13581940 (2认同)

AJ.*_*AJ. 37

有点难以分辨你的实际应用程序在做什么,但听起来你可能正在实例化COM对象,然后尝试从另一个线程访问它,可能是在Timer.Elapsed事件中.如果您的应用程序是多线程的,则需要在将要使用它的每个线程中实例化COM对象.

  • 我发现我有一个使用IConnectionPoint和IConnectionPointContainer的Com事件,并且我释放了IConnectionPointContainer。那是错误的谢谢 (2认同)
  • 好的.请确保发布您的答案,并将其标记为将来遇到此问题的任何人接受的答案. (2认同)