wpf - 托管 WPF 和 native 代码互操作。 WPF 托管在 ServicedComponent 中

标签 wpf com-interop com+ sta servicedcomponent

我正在努力在 ServicedComponent 中托管 WPF 应用程序。我有一个 WPF 库,我需要从 native 代码中使用它。为了达到这个目标,我创建了一个 outproc COM+ 组件,将所有 WPF 调用放入其中,并从 native 代码调用该组件,如下所示:

// 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));

它作为原型(prototype)完美运行,但当我添加实际的 WPF 功能时,一切都开始分崩离析。

由于臭名昭著的 WPF 异常,我无法在 COM+ ServicedComponent 中创建 WPF 窗口,“调用线程必须是 STA,因为许多 UI 组件都需要这个”。解决方案之一是使用 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 元素?或者我如何在 native 代码和 WPF 之间进行交互?任何想法表示赞赏。

更新:奇怪的是,赋值 (_Window = new Window()) 会影响线程模型。如果 _Window 是类的成员,则踩踏模型仍然是 MTA。如果它是局部变量,则线程模型将更改为 MTA。看来 _Window 应该以其他方式分配为该类的成员。

最佳答案

我可能正在寻找解决方案的正轨或完全偏离正轨-

http://drwpf.com/blog/2007/10/05/managing-application-resources-when-wpf-is-hosted/

基本上,上述方法加载所有资源字典并创建 WPF 环境。请检查“在代码中管理资源字典的集合并在元素级别合并它们”。

因此,在此之后,您只需从 WPFCommand 调用您的窗口,而不必担心 STA\MTA。

关于wpf - 托管 WPF 和 native 代码互操作。 WPF 托管在 ServicedComponent 中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24226231/

相关文章:

wpf - 绑定(bind)以显示 double 作为 int

c# - 修剪 wav 文件

c# - 通过 .NET COM 互操作调用方法时出错

c++ - 为什么 COM+ 组件的 CoCreation 失败并显示 0x8007007e "The specified module could not be found"?

.net - 是否需要在 GAC 中安装 (.net) COM+ 程序集?

WPF 和 ToolboxItem(false)

windows - COM+ 库应用程序的用途是什么?

c# - 在 C# 中类上方出现的 Guid 属性是什么?

c# - 从 C# 应用程序调用本地 .NET (2) COM+ 组件

c# - 在 Windows Phone 8 中选择联系人