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