Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/311.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 从WinForms GUI线程调用COM线程上的方法时出现问题?_C#_.net_Winforms_Com_Com Interop - Fatal编程技术网

C# 从WinForms GUI线程调用COM线程上的方法时出现问题?

C# 从WinForms GUI线程调用COM线程上的方法时出现问题?,c#,.net,winforms,com,com-interop,C#,.net,Winforms,Com,Com Interop,用.NET编写的COM组件抛出如下警告时遇到问题: 上下文0x15eec0已断开连接。不 代理将用于为 对COM组件的请求。今年五月 导致损坏或数据丢失。到 避免此问题,请确保 所有环境/公寓保持活力 直到应用程序完全完成 使用RuntimeCallableWrappers完成 表示COM组件的 住在里面 这似乎是由于我的GUI线程在COM线程中调用函数而没有进行必要的同步造成的。作为参考,我使用中设置的指南在COM组件中创建GUI线程 我的代码看起来像: class COMClass {

用.NET编写的COM组件抛出如下警告时遇到问题:

上下文0x15eec0已断开连接。不 代理将用于为 对COM组件的请求。今年五月 导致损坏或数据丢失。到 避免此问题,请确保 所有环境/公寓保持活力 直到应用程序完全完成 使用RuntimeCallableWrappers完成 表示COM组件的 住在里面

这似乎是由于我的GUI线程在COM线程中调用函数而没有进行必要的同步造成的。作为参考,我使用中设置的指南在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对象。

因为COM对象是在另一个线程上创建的,所以对COM对象的所有调用都应该从该线程进行。启动GUI线程后,需要设置某种排队机制,以等待调用执行方法(可能是一个委托队列)。您的GUI代码可以将委托推入队列,当原始线程处理队列时,委托将被执行(在原始线程上)。请参阅:(生产者/消费者关于页面中间部分的示例)。

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

有一个明显的问题,您忘记调用Thread.SetApartmentState()


鉴于COM已经封送线程间调用,您可能无法通过启动自己的线程获得任何好处。如果COM服务器拒绝支持它,你就不能神奇地使它成为多线程的。

我发现了问题,这不是线程间的操作。在我的GUI表单中,我创建了一个子窗口,并使用SetParent()将其设置为COM服务器的应用程序窗口的父窗口。这似乎导致了COM代理断开连接的问题(尽管有更多经验的COM专家可能会告诉我为什么它会这样)


与其将我的控件作为窗口的子对象,我将完全断开它的连接,并将WM_WindowPosChange挂接在主应用程序窗口上以移动我的控件。

这在为该应用程序设计COM系统时是不可能的。没有公开的循环,我可以插入代码来使用队列中的事件;如果我向COM对象上调用的任何函数添加循环,它将永远不会返回调用应用程序。坦率地说,我希望有某种ThreadBoundInvoker,我可以向其发布事件,从而在绑定线程上执行。COM对象是否仅由GUI表单使用?如果是这样的话,您可以在表单中实例化它,所有内容都将在GUI线程上。COM线程也会使用comObject,尽管调用可以在GUI表单上调用。问题是,如果我们试图移动它,我们有几千行需要重构,而且我们离完成那么多工作的最后期限太近了。Init()方法由COM服务器应用程序调用(我们没有编写该应用程序),它在STA线程上创建我们的COMClass。在实际代码中,我确实有SetApartmentState(ApartmentState.STA)调用,因此GUI和COMClass都应该位于单独的STA中。我的理解是应该在它们之间封送调用,但是MDA错误似乎表明代理对象由于某种原因被删除;为了使用我们的COMClass运行WinForms GUI,我们需要初始化WinForms消息泵,这需要一个新的线程。从消息来看,这个线程不再存在似乎是合乎逻辑的。现在还不清楚为什么必须在该线程上创建COM对象。