2010-02-18 91 views
3

我在使用.NET编写投掷警告,看起来像我的COM组件的麻烦:从WinForms GUI线程调用COM线程方法的问题?

上下文0x15eec0断开。没有 代理将用于服务COM组件上的 请求。这可能会导致数据损坏或数据丢失 。为了 避免此问题,请确保 所有上下文/公寓活路 直到应用程序完全 与代表 住在他们里面COM组件RuntimeCallableWrappers 完成。

看起来这是由我的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对象的任何方式。

回答

1

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

而不是父母我的控制窗口我要完全断开它,只是挂钩WM_WINDOWPOSCHANGING移动我的控制主应用程序窗口。

0

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

+0

这是不适用于此应用程序的COM系统的设计。没有暴露的循环,我可以插入代码来使用队列中的事件;如果我将一个循环添加到在我的COM对象上调用的任何函数,它将永远不会返回到调用应用程序。 – 2010-02-18 23:18:34

+0

坦率地说,我希望有某种ThreadBoundInvoker,我可以发布事件来执行绑定的线程。 – 2010-02-18 23:20:03

+0

是否仅由GUIForm使用COM对象?如果是这样,你可以在窗体中实例化它,并且所有的东西都在GUI线程中。 – PatrickSteele 2010-02-19 01:27:44

2

从你的代码片段不太清楚如何调用所有重要的Init()方法以及线程是如何启动的。显然,创建COM对象的线程与创建SomeMethod()调用的线程不是同一个线程。进一步假定COM服务器是单元线程的,COM需要将SomeMethod()调用编组到创建该对象的线程。叫做Init()的那个。如果该线程不再运行,则会出现闹剧。

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

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

+0

Init()方法由在STA线程上创建我们的COMClass的COM服务器应用程序(我们没有写它)调用。在实际的代码中,我确实有SetApartmentState(ApartmentState.STA)调用,所以GUI和COMClass都应该在单独的STA中。我的理解是调用应该在它们之间编组,但MDA错误似乎表明代理对象由于某种原因被删除。 – 2010-02-19 14:20:31

+0

我们也没有多少选择创建线程;为了使用我们的COMClass运行WinForms GUI,我们需要初始化WinForms消息泵,这需要一个新的线程。 – 2010-02-19 14:22:50

+0

那么,Init()是由你不控制的线程调用的。从消息来看,这个线程已经不在了。目前尚不清楚为什么*你必须在该线程上创建COM对象。 – 2010-02-19 15:31:26