消息泵和 AppDomain
Posted
技术标签:
【中文标题】消息泵和 AppDomain【英文标题】:Message Pumps and AppDomains 【发布时间】:2009-01-13 14:45:03 【问题描述】:我有一个将 DLL 作为插件加载的 C# (FFx 3.5) 应用程序。这些插件加载在单独的 AppDomain 中(有很多很好的理由,而且这种架构不能改变)。这一切都很好。
我现在需要显示来自这些插件之一的对话框。请记住,我无法将对话框表单返回到主应用程序并在那里显示(当前的基础架构不支持它)。
失败 1
在我的 DLL 中,我创建了一个名为 Show 的表单。对话框轮廓出现但没有绘制,并且它不响应鼠标事件。我认为这是因为 DLL 位于单独的 AppDomain 中,并且应用程序的消息泵不知何故无法将消息发送到新表单。
失败2
在我的 DLL 中,我创建了一个名为 ShowDialog 的表单,它应该为对话框创建一个内部消息泵。对话框显示并响应点击(万岁),但似乎主应用程序不再正在处理或发送 Windows 消息,因为它退出绘制并且不再响应鼠标事件。出于某种原因,现在似乎主应用程序的消息泵没有发送。
失败 3
在我的 DLL 中,我创建了一个名为 Application.Run 的表单。这肯定会创建一个完整的第二个消息泵。我得到了与失败 2 相同的行为 - 对话框的行为,但调用应用程序没有。
对这里到底发生了什么以及如何从另一个 AppDomain 的 DLL 显示对话框并让调用者和被调用者仍然响应并正确绘制有什么想法?
【问题讨论】:
【参考方案1】:尝试将 appdomain1 的主表单的 BeginInvoke 与显示 appdomain2 中的表单的委托一起使用。所以在伪代码中:
Appdomain1:
AppDomain2.DoSomething(myMainForm);
AppDomain2:
DoSomething(Form parent)
Form foolishForm = new Form();
parent.BeginInvoke(new Action( delegate foolishForm.Show(); ));
代码可能并不完美,但它展示了这个概念。
顺便说一句,如果您因为远程处理而在传递表单时遇到问题,您可以:
public class Container<T> : 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<T> container)
return container.Value;
这将包含你扔给它的物体。
【讨论】:
+1:感谢您的建议。我喜欢容器的概念。在我们前进的过程中,我们可能会牢记这一点。【参考方案2】:我们有一个架构非常相似的应用程序,可以加载 DLL 文件和插件。每个 DLL 文件都加载到单独的 application domain 中,该 application domain 是在单独的线程上创建的。我们有一个第三方控件,除非我们定期调用System.Windows.Forms.Application.DoEvents()
,否则该控件不会出现。
伪代码:
<In new thread>
<Application domain created. Start called inside new application domain.>
<Start loads new DLL file, calls init function in DLL file>
<Start loops, calling DoEvents until the DLL file exits>
<Application domain unloaded>
<Thread exits>
这解决了我们所有的 GUI 问题。
【讨论】:
【参考方案3】:我之前使用过的一件事是实现DomainManager。可以自定义各种application domain 安全/绑定/上下文来处理复杂的或鸡蛋类型的问题,将您的数据泵送到您想要的地方;)
我通常从 native.exe 执行此操作,通过 COM 接口引导 CLR(伪代码,但顺序和方法名称是正确的;):
CorBindToRuntimeEx()
SetHostControl()
GetCLRControl()
SetAppDomainManagerType("yourdomainmanger","info")
// Domain manager set before starting runtime
Start()
HostControl -- GetDomainManagerForDefaultDomain()
DomainManager -- Run()
您的域管理器可以是任何 CLR 类库,因此它们不是更原生的 C。
附注,如果你在WPF;我真的很喜欢使用“Microsoft.DwayneNeed.Controls”方法。您可能在 same UI 控件中使用自己的 Dispatcher pump 分散线程(不需要求助于全新的 Window())。
使用这种方法的独特之处在于,即使主 UI 线程被阻塞/忙碌(一些繁重的操作、扫描文件系统等),这些其他线程也可以绘制/更新其 UIElement 而不会出现任何问题.
【讨论】:
***.com/users/100864/giulio-vian 用一本书的链接回答了另一个问题,该书还记录了如何做到这一点; ***.com/questions/171541/…以上是关于消息泵和 AppDomain的主要内容,如果未能解决你的问题,请参考以下文章
从 AppDomain 解决 InvalidOperationException