从WinForms GUI线程调用COM线程上的方法的问题?

时间:2010-02-18 21:52:05

标签: c# .net winforms com com-interop

我在用.NET编写的COM组件出现问题时抛出的警告类似于:

  

上下文0x15eec0已断开连接。没有   代理将用于服务   请求COM组件。这可能   导致腐败或数据丢失。至   避免这个问题,请确保   所有环境/公寓都保持活力   直到申请完全   使用RuntimeCallableWrappers完成   代表COM组件的那些   住在他们里面。

看起来这是由我的GUI线程调用COM线程中的函数引起的,没有必要的同步。作为参考,我使用http://msdn.microsoft.com/en-us/library/ms229609%28VS.80%29.aspx中设置的准则在COM组件中创建我的GUI线程。

我的代码类似于:

class COMClass {
  // this is called before SomeMethod
  public void Init() {
    ComObject comObject = new ComObject(); // this is imported from a TLB

    // I create my GUI thread and start it as in the MSDN sample
    Thread newThread = new Thread(new ThreadStart(delegate() {
      Application.Run(new GUIForm(comObject));
    }));
  }

  public void SomeMethod(){
    comObject.DoSomething();               // this is where the error occurs
  }
}

class GUIForm : Form {
  ComObject com;
  public GUIForm(ComObject com) {comObject = com;}

  public void SomeButtonHandler(object sender, EventArgs e) {
    comObject.SomeMethod();  // call on the GUI thread but the com object is bound to the COM thread...
  }
}

有没有一种既定的处理方法?调用GUI是没有问题的(Invoke / BeginInvoke),但调用另一种方式似乎更难......

编辑:也不能以任何方式修改COM对象。

3 个答案:

答案 0 :(得分:2)

从你的代码片段中不太清楚如何调用最重要的Init()方法以及如何启动线程。显然,创建COM对象的线程与调用SomeMethod()的线程不是同一个线程。进一步假设COM服务器是单元线程的,COM需要封送SomeMethod()调用创建该对象的线程。调用Init()的那个。如果该线程不再运行,则会出现欢闹。

有一个明显的问题,你忘了调用Thread.SetApartmentState()。

鉴于COM已经整理了线程间调用,你可能没有通过启动自己的线程获得任何东西。如果COM服务器拒绝支持它,你就不能神奇地使COM服务器多线程。

答案 1 :(得分:1)

我发现了问题,这不是跨线程操作的问题。在我的GUIForm中,我创建了一个子窗口,并使用SetParent()将其父级添加到COM服务器的应用程序窗口。这似乎导致了COM代理断开连接的问题(尽管有更多经验的COM专家可能必须告诉我为什么它的行为如此)。

不是将我的控件父控制到窗口,而是将完全断开它并挂钩WM_WINDOWPOSCHANGING以使用主App窗口移动我的控件。

答案 2 :(得分:0)

由于COM对象是在另一个线程上创建的,因此应该从该线程对COM对象进行所有调用。启动GUI线程后,您需要设置某种排队机制来等待调用执行方法(可能是委托队列)。您的GUI代码可以将委托推送到队列中,并在原始线程处理队列时执行(在原始线程上)。请参阅:http://www.yoda.arachsys.com/csharp/threads/deadlocks.shtml(关于页面中间的生产者/消费者示例)。