C# 在托管STA应用程序中处理来自进程外COM服务器的事件
显然,源于非托管进程外COM服务器的事件托管处理程序在随机池线程上调用,而不是在主STA线程上调用(如我所料)。 我在回答一个问题时发现了这一点。在下面的代码中,C# 在托管STA应用程序中处理来自进程外COM服务器的事件,c#,.net,multithreading,com,com-interop,C#,.net,Multithreading,Com,Com Interop,显然,源于非托管进程外COM服务器的事件托管处理程序在随机池线程上调用,而不是在主STA线程上调用(如我所料)。 我在回答一个问题时发现了这一点。在下面的代码中,DocumentComplete在非UI线程上触发(因此“事件线程”与调试输出中的“主线程”不同)。因此,我必须使用this.Invoke来显示消息框。据我所知,这种行为不同于非托管COM客户端,在非托管COM客户端中,从STA线程订阅的事件会自动封送回同一线程 这种背离传统COM行为的原因是什么?到目前为止,我还没有找到任何证实这一点
DocumentComplete
在非UI线程上触发(因此“事件线程”
与调试输出中的“主线程”
不同)。因此,我必须使用this.Invoke
来显示消息框。据我所知,这种行为不同于非托管COM客户端,在非托管COM客户端中,从STA线程订阅的事件会自动封送回同一线程
这种背离传统COM行为的原因是什么?到目前为止,我还没有找到任何证实这一点的参考资料
using System;
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;
namespace WinformsIE
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs ev)
{
var ie = (SHDocVw.InternetExplorer)Activator.CreateInstance(Type.GetTypeFromProgID("InternetExplorer.Application"));
ie.Visible = true;
Debug.Print("Main thread: {0}", Thread.CurrentThread.ManagedThreadId);
ie.DocumentComplete += (object browser, ref object URL) =>
{
string url = URL.ToString();
Debug.Print("Event thread: {0}", Thread.CurrentThread.ManagedThreadId);
this.Invoke(new Action(() =>
{
Debug.Print("Action thread: {0}", Thread.CurrentThread.ManagedThreadId);
var message = String.Format("Page loaded: {0}", url);
MessageBox.Show(message);
}));
};
ie.Navigate("http://www.example.com");
}
}
}
我从亚当·内森那里找到:
如果COM对象位于STA中,则来自MTA线程的任何调用都将被删除
对COM对象进行了适当的封送处理,以使COM对象保持在其
线程亲和力。但是,在另一个方向上,没有这样的线或线
发生上下文切换
因此,这是预期的行为。到目前为止,这是我能找到的关于这个主题的唯一(半官方)来源。您的“主(…)”线程如何,或者您调用Activator.CreateInstance()的线程的单元状态是什么?这里的主线程和唯一显式线程是STA。该应用程序本身只是VS2012向导生成的WinForms应用程序:
[StatThread]static void Main(){/*…*/Application.Run(new Form1();}
。我认为这是正常的行为。接收事件的是由CLR(CCW或类似程序)实现的隐藏COM对象,该对象可能具有任何线程模型。NET中的事件可以到达任何线程。顺便说一句,让我们刷新一下记忆:-)@SimonMourier,我几乎记得在某个地方读到过,他们完全按照您描述的方式更改了COM=>.NET回调的规则。虽然你给出的链接似乎没有明确说明这一点。我也找不到任何其他来源。也许是亚当·内森写的关于互操作的书。这样吧,ie
out-proc-proxy-free线程化了吗?我可以安全地直接从随机线程上的事件处理程序调用ie
api吗?另外,我想知道我是否可以通过实现一个定制的SynchronizationContext
来恢复传统的COM行为。因为它是进程外的,你可以在我相信的任何线程上调用IE,COM+代理/存根将发挥神奇的作用。我认为返回COM行为的唯一方法不是依赖于COM实现底层的.NET事件(委托等),而是实现手动COM事件接收器(IConnectionPoint等)。您应该能够以自己喜欢的方式声明此接收器对象单元行为。从未测试过这对我来说似乎太过分了:-)。。。下面是如何收回预期行为: