C# 从控制台应用程序显示窗口时出现DLL故障

C# 从控制台应用程序显示窗口时出现DLL故障,c#,wpf,dll,console-application,C#,Wpf,Dll,Console Application,我创建了一个WPF应用程序,然后通过删除app.xaml并将构建设置为类库将其转换为DLL。我正在使用一个C#控制台应用程序来测试DLL。在我的第一次测试中,如果我将mainwindow.show()放在mainwindow=newmainWindow()下面的try块中,我就能够让应用程序显示得很好。现在我需要能够预加载wpf应用程序,并且只在需要时显示它,而不是每次都要加载它。我遇到的问题是,显示wpf应用程序的调用位于另一个线程上,而ShowWPFAppDLL()主窗口上的调用为null。

我创建了一个WPF应用程序,然后通过删除app.xaml并将构建设置为类库将其转换为DLL。我正在使用一个C#控制台应用程序来测试DLL。在我的第一次测试中,如果我将mainwindow.show()放在mainwindow=newmainWindow()下面的try块中,我就能够让应用程序显示得很好。现在我需要能够预加载wpf应用程序,并且只在需要时显示它,而不是每次都要加载它。我遇到的问题是,显示wpf应用程序的调用位于另一个线程上,而ShowWPFAppDLL()主窗口上的调用为null。我有什么办法可以让它工作吗

控制台应用程序:

namespace ConsoleApp
{
    class Program
    {
        static WPFAppDLL.LoadWpfAppDll loader = new WPFAppDLL.LoadWpfAppDll();

        static void Main(string[] args)
        {
            Thread worker = new Thread(new ThreadStart(LoadWpfApp));
            worker.SetApartmentState(ApartmentState.STA);
            worker.Name = "WpfThread";
            worker.IsBackground = true;
            worker.Start();

            Thread.Sleep(15000);
            ShowWpfApp();
            worker.Join();
        }

        private static void ShowWpfApp()
        {
            loader.ShowWPFAppDLL();
        }

        private static void LoadWpfApp()
        {
            loader.Load();
        }
    }
}
WPF应用程序(DLL):


您可以实现一个ManualResetEvent,该事件显示WPFAPPDLL正在等待,并在主窗口实例化后设置

此外,您必须确保对mainWindow执行的任何操作都发生在运行负责mainWindow的dispatcher的线程上

您的代码可能与此类似:

public class LoadWpfAppDll
{
    private readonly ManualResetEvent _evtMainWindowInstantiated = new ManualResetEvent(false);

    private MainWindow mainWindow = null;


    public void Load(string[] args)
    {
        try
        {
            ......all the stuff you do in Load()...

            try
            {
                mainWindow = new MainWindow();
                mainWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                splashWindow.Close();

                application.Startup +=
                    (sender, e) => _evtMainWindowInstantiated.Set();

                application.Run();
            }
            catch (Exception ex)
            {
                splashWindow.Close();
                MessageBox.Show("Error starting application:" + Environment.NewLine + ex.ToString(), "Error Message", MessageBoxButton.OK, MessageBoxImage.Error);

                mainWindow = null;
            }
        }
        finally
        {
            //
            // Ensures that the _evtMainWindowInstantiated is always set, even in
            // failure case. If this would not be done, a failure of the Load
            // method could block other threads waiting on this ManualResetEvent
            // in one of the public methods below.
            //
            _evtMainWindowInstantiated.Set();
        }
    }


    private void InvokeOnMainWindowThread(Action action)
    {
        _evtMainWindowInstantiated.WaitOne();

        if (mainWindow == null)
        {
            ...something bad happened in Load()...
            ...do error handling or just return, whatever is appropriate...
        }

        //
        // Make sure that the action is invoked on the mainWindow thread.
        // If InvokeOnMainWindowThread is already called on the
        // mainWindow thread, the action should not be queued by the
        // dispatcher but should be executed immediately.
        //
        if (mainWindow.Dispatcher.CheckAccess()) action();
        else mainWindow.Dispatcher.Invoke(action, null);
    }


    public void ShowWPFAppDLL()
    {
        InvokeOnMainWindowThread(mainWindow.Show);
    }
}
请注意,\u EVTMainWindowInstanced是一个手动重置事件,永远不会重置。因此,一旦在Load()方法
中设置了它,EVTMainWindowInstanced.WaitOne()
将不再阻塞/等待

此外,我还特意引入了一个InvokeOnMainWindowThread方法,该方法负责处理EVTMainWindow实例化并在mainWindow的dispatcher线程上执行操作。如果您需要实现多个公共方法(如ShowWPFAppDLL),那么使用一个专用方法是值得的

与此相关,我将mainWindow设置为私有,因为对它的访问需要由InvokeOnMainWindowThread管理。允许其他类直接访问mainWindow可能会导致与多线程相关的问题,或者因为Load()尚未完成

如果您想从修补mainWindow的公共方法返回一些结果,可以实现InvokeOnMainWindowThread的重载,该重载将Func委托作为参数


最后,我建议您让WpfAppDll库也为主窗口创建和设置线程。在我看来,让“外部世界”设置将运行mainWindow的dispatcher(也称为消息循环)的线程很容易出错。

那么上面的代码到底在哪里失败了?显然,mainWindow只在Load方法中初始化。在使用mainWindow之前,您需要调用它一次。当mainWindow为null时,什么会阻止您从ShowWPFAppDLL方法内部调用Load?这真的是你的问题吗?@Fayilt main window在我调用ShowWPFAppDLL()时为空,因此它不会像我希望的那样显示。@elgonzo我在加载时调用它,这样它可以在内存中预加载,并在调用ShowWPFAppDLL时立即显示,因为它将被调用多次,并且主窗口可能需要一段时间才能加载。我在最上面做了MainWindow MainWindow=null,并认为在加载时它会初始化它以在中使用ShowWPFAppDLL@Danno,我明白了(抱歉,在看到“ThreadStart(LoadWpfApp)”之前,太盲目了)。我会写一个答案…@Danno,很高兴我能帮上忙:)@Danno,还有一件事:别忘了做正确的异常处理。如果Application.Run由于传递给Dispatcher.Invoke的操作导致的未处理异常而退出,则您可能很难将应用程序保持在可用状态(一种方法是通过Application.DispatcherUnhandledException)。
public class LoadWpfAppDll
{
    private readonly ManualResetEvent _evtMainWindowInstantiated = new ManualResetEvent(false);

    private MainWindow mainWindow = null;


    public void Load(string[] args)
    {
        try
        {
            ......all the stuff you do in Load()...

            try
            {
                mainWindow = new MainWindow();
                mainWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                splashWindow.Close();

                application.Startup +=
                    (sender, e) => _evtMainWindowInstantiated.Set();

                application.Run();
            }
            catch (Exception ex)
            {
                splashWindow.Close();
                MessageBox.Show("Error starting application:" + Environment.NewLine + ex.ToString(), "Error Message", MessageBoxButton.OK, MessageBoxImage.Error);

                mainWindow = null;
            }
        }
        finally
        {
            //
            // Ensures that the _evtMainWindowInstantiated is always set, even in
            // failure case. If this would not be done, a failure of the Load
            // method could block other threads waiting on this ManualResetEvent
            // in one of the public methods below.
            //
            _evtMainWindowInstantiated.Set();
        }
    }


    private void InvokeOnMainWindowThread(Action action)
    {
        _evtMainWindowInstantiated.WaitOne();

        if (mainWindow == null)
        {
            ...something bad happened in Load()...
            ...do error handling or just return, whatever is appropriate...
        }

        //
        // Make sure that the action is invoked on the mainWindow thread.
        // If InvokeOnMainWindowThread is already called on the
        // mainWindow thread, the action should not be queued by the
        // dispatcher but should be executed immediately.
        //
        if (mainWindow.Dispatcher.CheckAccess()) action();
        else mainWindow.Dispatcher.Invoke(action, null);
    }


    public void ShowWPFAppDLL()
    {
        InvokeOnMainWindowThread(mainWindow.Show);
    }
}