2

我必须使用另一个应用程序域中的功能。结果应显示在用户控件中。

我有类似的东西:

var instance = domain.CreateInstanceFromAndUnwrap(...);
instance.Foo(myWpfUserControl as ICallback);

Foo(ICallback itf) {
   itf.SetData("...");
}

WpfUserControl.SetData(string data)
{
   if (!Dispatcher.CheckAccess())
     Dispatcher.Invoke(...)
   ...
}  

我不得不将 [Serializable] 属性放到 WpfUserControll 类上并实现序列化构造函数以及 ISerializable 接口,但现在我收到异常:

The calling thread must be STA because many UI components require this 

从 UserControl() 构造函数中引发

我该怎么做才能避免这种情况?先感谢您 !

===============================

解决方案

正如@Al 所注意到的,当涉及到跨应用程序域调用时,我的用户控件必须被序列化。现在我通过代理,它实现了 ICallback 接口。代理被标记为 Serializable 属性。

代理实现应该完全不了解用户控件,因为应该尝试再次反序列化用户控件实例。当我尝试通过界面从用户控件中抽象代理时,它没有帮助。当我尝试将接口传递给代理(由用户控件实现)时 - 发生了同样的异常。

最后,我用队列/信号量解耦了代理和用户控制。队列由一个工作线程监控,该线程将调用委托给用户控制

ps 这个队列应该继承自“MarshalByObjectRef”。

4

1 回答 1

2

如果异常来自构造函数,则意味着您不是从 UI 线程创建此控件实例。这可以很好,但您必须.SetApartmentState(ApartmentState.STA)在线程启动之前通过调用线程对象来确保线程是 STA 线程。

这也意味着您必须在线程对象启动之前访问它,因此您不能在线程池线程上执行此操作。

避免该问题的最佳方法可能是在主 UI 线程上创建控件,然后使用 Dispatcher(或 UiScheduler 上的任务)分配 Text 值。这样,如果主线程需要设置、获取或绑定到控件,您也可以避免出现问题,因为如果控件是在另一个线程上创建的,则会导致跨线程异常

如果可能的话,我建议不要以这种方式对控件进行序列化。这样做将生成一个未附加到任何面板或类似面板的新对象,并且不会更新原始控件。遗憾的是,您不能继承 MarshalByRefObject 将消除序列化,因为它只会传递对另一个域的引用。

如果可以,单独调用Foo,然后将结果传递给原始Appdomain中的SetData

于 2011-05-26T08:36:38.517 回答