代码之家  ›  专栏  ›  技术社区  ›  Ron Warholic

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

  •  3
  • Ron Warholic  · 技术社区  · 15 年前

    我在使用.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 回复  |  直到 15 年前
        1
  •  2
  •   Hans Passant    15 年前

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

    有一个明显的问题,您忘记调用thread.setApartmentState()。

    考虑到COM已经对线程间调用进行封送,您可能无法通过启动自己的线程获得任何信息。如果COM服务器拒绝支持它,就不能神奇地使它成为多线程的。

        2
  •  1
  •   Ron Warholic    15 年前

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

    我将完全断开与窗口的连接,而不是将控件设置为父级,只需钩住wm_windowposchanging以将控件移动到主应用程序窗口。

        3
  •  0
  •   PatrickSteele    15 年前

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