消息泵和应用程序域

3
我有一个使用C# (FFx 3.5)编写的应用程序,可以加载DLL作为插件。这些插件在单独的AppDomain中加载(由于许多良好的原因,这个架构不能更改)。目前看来一切都很好。
现在,我有一个要求需要从其中一个插件显示对话框。请注意,我不能将对话框表单返回到主应用程序并在那里显示它(当前基础架构不支持)。
失败1
在我的DLL中,我创建了一个表单并调用了Show方法。对话框轮廓出现了,但没有绘制,并且不响应鼠标事件。我认为这是因为DLL位于单独的AppDomain中,应用程序的消息泵无法向新表单分派消息。
失败2
在我的DLL中,我创建了一个表单并调用了ShowDialog方法,这应该会为对话框创建一个内部消息泵。对话框被显示并响应点击(万岁),但似乎主应用程序不再处理或分派窗口消息,因为它停止绘制并且不再响应鼠标事件。由于某种原因,现在似乎主应用程序的消息泵没有分派。
失败3
在我的DLL中,我创建了一个表单并调用了Application.Run。这肯定会创建一个完整的第二个消息泵。我得到了与故障2相同的行为-对话框表现良好,但是调用应用程序不会。
有什么想法可以解决这里发生的情况以及如何显示来自其他AppDomain的DLL的对话框,并使调用方和被调用方仍然正确响应和绘制?
3个回答

4
尝试使用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: 感谢您的建议。我喜欢容器概念,我们在未来的开发中可以考虑采用它。 - ctacke

1
我们有一个非常类似的应用程序,它加载DLL文件和插件。每个DLL文件都在单独的应用程序域中加载,该域在单独的线程上创建。我们在一个窗体中使用了第三方控件,除非我们定期调用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问题。


1

我以前用过的一个东西就是实现 DomainManager。可以自定义各种 应用程序域 的安全/绑定/上下文,以处理涉及将数据导入指定位置时出现的复杂或鸡生蛋式问题。 ;)

我通常是从本机 .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”方法。在同一个 UI 控件中可能有不同的线程和它们自己的 Dispatcher 泵(而不需要转向全新的 Window())。

使用这种方法的独特之处在于,即使主 UI 线程被阻塞/忙碌(某些重操作、扫描文件系统等),这些其他线程也可以毫不费力地绘制/更新其 UIElement,不会出现任何中断。


http://stackoverflow.com/users/100864/giulio-vian回答了另一个问题,并提供了一本书的链接,该书还记录了如何做到这一点; http://stackoverflow.com/questions/171541/how-do-i-obtain-a-list-of-the-application-domains-my-application-has-created - RandomNickName42

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接