我正在努力在 ServicedComponent 中托管 WPF 应用程序。我有一个 WPF 库,我需要从本机代码中使用它。为了达到这个目标,我创建了一个 outproc COM+ 组件,将所有 WPF 调用放入其中,并从本机代码调用该组件,如下所示:
// Managed
[ComVisible(true)]
public class HpPcKeyboardSrv : ServicedComponent, ISrv
{
...
}
// Native
CComPtr<ISrv> spISrv;
hr = spISrv.CoCreateInstance(__uuidof(MySrv), nullptr, CLSCTX_SERVER);
ATLVERIFY(SUCCEEDED(hr));
hr = spISrv->WPFCommand();
ATLVERIFY(SUCCEEDED(hr));
它作为原型完美地工作,但是当我添加实际的 WPF 功能时,一切都开始分崩离析。
由于臭名昭著的 WPF 异常,我无法在 COM+ ServicedComponent 中创建 WPF 窗口,"The calling thread must be STA, because many UI components require this"
. 解决方案之一是使用 Dispatcher。问题是 WPF 调度程序Dispatcher.CurrentDispatcher
没有调用函数BeginInvoke()
:
public void WPFCommand()
{
Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate
{
System.Threading.ApartmentState aptStateLocal = Thread.CurrentThread.GetApartmentState();
Debug.WriteLine("Spawned thread apartment: {0}", aptStateLocal);
_Window = new Window();
});
}
另一种选择是使用Application.Current.Dispatcher
. 这种方法的问题在于,在这个调用Application.Current
中为空,因此没有可用的 Dispatcher。
好的。接下来要尝试的是在 STA 模型中产生威胁,如下所示:
public void WPFCommand()
{
if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
{
Thread thread = new Thread(() =>
{
System.Threading.ApartmentState aptState = Thread.CurrentThread.GetApartmentState();
Debug.WriteLine("Spawned thread apartment: {0}", aptState); // <- Still MTA
// !!! If _Window is the member of the class, the thread will be MTA
// !!! otherwise STA
_Window = new Window();
System.Windows.Threading.Dispatcher.Run();
});
Debug.WriteLine("Thread apartment state1: {0}", thread.GetApartmentState());
thread.SetApartmentState(ApartmentState.STA); // <- even though set as STA
Debug.WriteLine("Thread apartment state2: {0}", thread.GetApartmentState());
thread.IsBackground = true;
thread.Start();
thread.Join();
}
}
这段代码有部分帮助。由于已设置为 STA 模型的生成线程无论如何都会在 MTA 中调用,如果 _Window 是类成员(但如果不是,则它是 STA),因此new Window()
抛出相同的“必须是 STA”异常。
在这一点上,我完全陷入了困境。如何在 ServicedComponent 中实际创建 WPF 元素?或者我如何在本机代码和 WPF 之间进行交互?任何想法表示赞赏。
更新:奇怪的是,赋值 (_Window = new Window()) 影响线程模型。如果 _Window 是该类的成员,则踩踏模型仍然是 MTA。如果它是局部变量,则线程模型将更改为 MTA。似乎应该以其他方式将 _Window 分配为该类的成员。