【问题标题】:Using WPF (which requires STAThread) with an API that can't work with STAThread将 WPF(需要 STAThread)与无法使用 STAThread 的 API 一起使用
【发布时间】:2009-07-08 15:07:32
【问题描述】:

我正在编写一个 WPF 应用程序,该应用程序对具有简单要求的 API 具有可选依赖项;它必须在没有 STAThread 属性的线程上初始化/使用。当然,WPF 需要 STA,所以一切都很简单。

在这种情况下,无论如何都需要 WPF。仅当用户选择在应用程序中启用此功能时,才需要此第三方 API。这意味着一旦需要调用其他 API,WPF 应用程序就已经在运行。

如果你不使用 [STAThread] 装饰你的 main 方法,它会自动成为 MTA 线程吗?在这种情况下,这是否意味着我可以创建一个新的 MTA 线程并在其上使用其他 API?

如果这可行,那么我猜来自这个 API 的任何事件都可以使用 Dispatcher 与 WPF 应用程序通信(用于引发需要在 UI 中显示的事件等)。但是,是否有一种简单的方法可以让我的 WPF 应用程序“调用”MTA 线程上的功能来进行 API 调用?

在 MTA 中,我猜每个线程都应该能够处理状态,但我猜我的 STA 线程(WPF 应用程序)不能只是“进入”MTA 线程并执行 API 调用?

这里有很大的混淆可能性,我希望能就如何设计这样的东西提供一些意见!

谢谢!

[编辑 7 月 8 日]

好吧,我在上面混淆了一些概念。线程模型当然是为一个 PROCESS 设置的,而不是为每个线程设置的,而且这个第三方 API 不能与 STA 进程一起使用。

目前,我从这个混乱中看到的唯一方法是编写一个与这个 API 通信的服务,然后使用命名管道与这个服务通信。这一点都不是微不足道的,一个丑陋的解决方法,但第三方 API 不在我的控制之下。这就是生活。 :|

【问题讨论】:

    标签: .net wpf multithreading


    【解决方案1】:

    外部 API 是 GUI API 吗?还是只是功能等?如果是后者,那么您可以生成第二个线程(可能使用生产者/消费者队列),即 MTA:

        Thread thread = new Thread(DoSomeStuff);
        thread.SetApartmentState(ApartmentState.MTA);
        thread.Name = "3rd aprty API spooler";
        thread.Start();
    

    【讨论】:

    • 谢谢马克 - 我会试试这个。外部 API 是一个电话系统,其中功能/事件将需要一些时间(不是几秒钟,但很明显)才能执行,所以这看起来是一个不错的计划。
    【解决方案2】:

    您需要启动一个新线程来调用需要 MTA 线程的代码。 您可以尝试以下类似的方法,以便在 mta 线程上轻松调用。这将在 MTA 线程执行时阻塞主线程。效率如何取决于您的 API 使用情况,因为您每次调用它时都会启动一个新线程。

    public delegate void MtaMethod();
    
    public class MtaHelper
    {
    
        public static void RunMta(MtaMethod method)
        {
    
            ManualResetEvent evnt = new ManualResetEvent(false);
    
            Thread thread = new Thread(delegate()
            {
                method();
                evnt.Set();
            });
    
            thread.SetApartmentState(ApartmentState.MTA);
            thread.Start();
            evnt.WaitOne();
        }
    
    }
    

    然后你应该可以调用你的 api

    MtaHelper.RuntMta( () => OtherAPIMethod() );
    

    【讨论】:

    • 您好,感谢您的建议。我遇到的唯一问题是阻塞,因为其他 API 的类型可能需要一些时间才能完成。我也会试试这个!
    【解决方案3】:

    我在 WPF 中利用 DispatcherObject。

    我创建了 DispatcherObject 的子类,然后使用 MTA 线程创建了一个实例。这样,我可以使用 mySubclass.Invoke(...) 或 mySubclass.BeginInvoke(...) 并且执行代码将发生在我的 MTA 线程上。

    您可以在此处查看示例:

    http://wpfmediakit.codeplex.com/SourceControl/changeset/view/24019#283908

    特别注意 CreateDvdPlayer 静态方法,还要看 BaseClasses.cs 中的基类

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-18
    • 1970-01-01
    • 1970-01-01
    • 2010-11-06
    • 1970-01-01
    相关资源
    最近更新 更多