消息泵和 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的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Direct2D 创建自定义窗口镶边?

从 AppDomain 解决 InvalidOperationException

c#读取类库配置文件方法

AppDomain 卷影副本 - 加载/卸载动态加载的 Dll

即使我卸载 appdomain,我的 dll 也不会卸载

Oracle Directory目录的知识