消息泵和AppDomains

我有一个C#(FFx 3.5)应用程序,它将DLL作为插件加载。 这些插件加载在单独的AppDomain中(出于很多原因,这种架构无法更改)。 这一切都很好。

我现在需要从其中一个插件中显示一个Dialog。 请记住,我无法将对话框Form返回到主应用程序并将其显示在那里(当前基础结构不支持它)。

失败1

在我的DLL中,我创建了一个Form并称为Show。 对话框轮廓显示但没有绘制,它不响应鼠标事件。 我假设这是因为DLL在一个单独的AppDomain中,并且应用程序的消息泵以某种方式无法将消息发送到新表单。

失败2

在我的DLL中,我创建了一个Form并调用了ShowDialog,所有权利都应该为对话框创建一个内部消息泵。对话框显示并响应点击(万岁),但似乎主应用程序不再处理或调度Windows消息因为它退出绘画而不再响应鼠标事件。 由于某种原因,现在似乎主app的消息泵没有调度。

失败3

在我的DLL中,我创建了一个Form并调用了Application.Run。 这肯定会创建一个完整的第二个消息泵。 我得到了与失败2相同的行为 – 对话行为,但调用应用程序没有。

有关这里到底发生了什么的想法,以及如何从其他AppDomain的DLL显示对话框并让调用者和被调用者仍然响应并正确绘制?

尝试使用appdomain1的主窗体的BeginInvoke和一个显示appdomain2表单的委托。 所以在伪代码中:

Appdomain1: AppDomain2.DoSomething(myMainForm); AppDomain2: DoSomething(Form parent) { Form foolishForm = new Form(); parent.BeginInvoke(new Action( delegate { foolishForm.Show(); } )); } 

代码可能不完美,但它certificate了这个概念。

顺便说一句,如果你因远程传递表格而遇到问题,你可以:

 public class Container : MarshalByRefObject { private T _value; public T Value { get { return _value; } set { _value = value; } } public Container() { } public Container(T value) { Value = value; } public static implicit operator T(Container container) { return container.Value; } } 

那将包含你抛出的对象。

我们有一个非常类似的架构应用程序,可以加载DLL文件和插件。 每个DLL文件都加载在一个单独的应用程序域中 ,该域在单独的线程上创建。 除非我们定期调用System.Windows.Forms.Application.DoEvents() ,否则我们有一个不会出现的第三方控件。

伪代码:

       

这解决了我们所有的GUI问题。

我之前使用的一件事是实现DomainManager 。 可以自定义各种应用程序域安全性/绑定/上下文来处理复杂或鸡蛋类型问题,以便将数据抽取到您想要的位置;)

我通常使用native.exe完成此操作,通过COM接口引导CLR(psudo代码,但顺序和方法名称正确;):

 CorBindToRuntimeEx() SetHostControl() GetCLRControl() SetAppDomainManagerType("yourdomainmanger","info") // Domain manager set before starting runtime Start() HostControl -- GetDomainManagerForDefaultDomain() DomainManager -- Run() 

您的域管理器可以是任何CLR类库,因此它们不是更多的本机C.

附注,如果你在WPF ; 我真的很喜欢使用“Microsoft.DwayneNeed.Controls”方法。 你可能在同一个 UI控件中使用自己的Dispatcher泵来分离线程(不需要求助于全新的Window())。

使用这种方法的独特之处在于,即使主UI线程被阻塞/忙碌(一些繁重的操作,扫描文件系统等等),这些其他线程也可以在没有任何打嗝的情况下绘制/更新其UIElement。