WPF插件的生命周期

3
我创建了一个插件,通过反射调用一个WPF类库。 由于这是一个类库,我必须手动实例化一个new System.Windows.Application()
然后,类的构造函数(通过反射调用)创建一个窗口,并使用Show()(带有Dispatcher.Run()以避免窗口立即关闭)或ShowDialog()。
由于我的应用程序在插件中,Application仍然存在。 因此,我只能实例化一次。
在第一次启动(实例化Application时),Application.Current.Dispatcher正在运行。
但是在第二次启动时,我发现Application.Current.Dispatcher已经停止了。 我从未调用过InvokeShutdown(),因此我不明白Dispatcher何时停止。
当我第二次启动时,Application已经被实例化(这是正常的),但Dispatcher已经停止。
有什么想法吗? 谢谢!
编辑:在我的插件中,我尝试了两种方法:
        foreach (Type type in ass2_l.GetTypes())
        {
            if (type.Name == "Loader")
            {
                object obj_l = Activator.CreateInstance(type);
                BindingFlags bf_l = BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
                object[] argList_l = new object[1];
                argList_l[0] = "ok";
                type.InvokeMember("Load", bf_l, null, obj_l, argList_l);
            }
        }

当我直接从Add-In中调用dll时,Application.Current.Dispatcher处于后台状态,名称为“VSTA_Main”。 第二次启动时,Dispatcher仍处于后台状态。

第二种方式:

        t_m = new Thread(loadDll);
        t_m.SetApartmentState(ApartmentState.STA);
        t_m.Start();

loadDll实际上包含“第一种方法”代码的相同部分。 第一次启动此部分时,Dispatcher正在运行,一切都非常正常。 第二次启动时,Dispatcher停止了。

编辑2: 问题出在第二种方法中。 当loadDll完成后,我再次单击我的插件按钮时,t_m就会停止,并且创建另一个不会解决问题,因为Dispatcher ManagedThreadId具有旧的t_m ManagerThreadId :/

编辑3: 问题绝对不是由Add-in引起的。 如果您只是创建一个程序,每次单击按钮时都会启动一个线程。 该线程尝试通过反射实例化DLL WPF类库,并且如果您第二次单击此按钮(调用另一个线程),则由于Dispatcher仍然“链接”到旧线程,因此Dispatcher将“停止”(如旧线程)


App.ShutdownMode设置为显式还是在最后一个窗口关闭时? - Merlyn Morgan-Graham
我都试过了。 但我认为我已经找到了问题所在。在我的Add-in中,当我点击由我的Add-in创建的按钮时,我启动了一个STA线程。 这个线程通过反射调用WPF类。我尝试在同一个线程中两次调用库,而Dispatcher仍在运行。 这意味着问题出在调用线程的结束,导致Dispatcher停止...但为什么?我不知道 :/ - metalcam
你能提供一个简短的代码示例来重现这个问题吗?另外,不确定是否适用,但是Application不喜欢在同一AppDomain中有多个实例... - Merlyn Morgan-Graham
我已经编辑了我的代码。顺便说一下,我没有多个实例在每个AppDomain中,因为每次我想要实例化一个新的应用程序时,我都会先检查Application.Current是否为空。 - metalcam
2个回答

7
在ThisAddin.Startup事件处理程序中放置以下代码:
private void ThisAddInStartup(object sender, EventArgs e)
{
    if (System.Windows.Application.Current == null)
        new System.Windows.Application();
    System.Windows.Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown
}

这应该能解决问题,它在我过去的使用中表现良好。


很遗憾,这段代码并没有解决我的问题,因为它让我的Dispatcher始终处于“后台”状态。但问题并不是插件的问题。实际上,如果您尝试使用控制台应用程序,在其中启动一个新线程(至少2次),并在此线程中调用一个新的wpf类库(该类库创建一个新的应用程序),则Dispatcher会在第二次启动线程时停止 :/ - metalcam
我的Application类内部机制了解得不太清楚,但我认为因为它与WPF相关,所以在典型应用程序中具有自己的消息泵和其他UI线程相关的功能。VSTO在STA中运行,我假设无论您在哪个线程上调用new Application(),都会启动消息泵。当该线程终止时,该线程上正在运行的消息泵也将终止。在后台线程实例化并不是一件好事,而且正如我所说的那样,上述方式对我而言可行。你能否请提供示例代码?我很想深入研究这个问题。 - Jake Ginnivan
1
此外,上述代码在VSTO启动方法中创建应用程序,该方法基本上是插件UI线程,在Office STA内运行(我想..)。然后,您应该在该UI线程上创建窗口,无论您使用调度程序还是同步上下文来执行它,都没有关系。但是,您应该在创建Application类的同一UI线程上执行所有与WPF相关的代码。 - Jake Ginnivan

1

最终,我使用无限循环来保持线程处于运行状态,并使用AutoResetEvent / ManualResetEvent来启动/停止线程解决了这个问题...

由于线程永远不会结束(并在接收到启动事件时调用加载WPF UI dll的方法),我们永远不会停止线程(直到停止Add-in),而且调度程序也永远不会停止。

顺便说一句,感谢您所有的答案 :)


你能在你的回答中添加代码示例吗?我遇到了同样的问题,但是在一些机器上调度程序没有关闭,在另一些机器上却可以。 - hustler

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