场景:
一切似乎都在工作,除了Application.Current在需要它时似乎是null.我真正想做的就是在第一个表单完全显示之前挂钩未处理的异常事件.
Ray*_*rns 12
首先,我将解释消息循环如何在互操作场景中工作,然后我将回答您的问题并提出一些建议.
您的方案中的消息循环实现
您的方案涉及三种不同的技术:VB 6,WinForms和WPF.这些技术中的每一项都是在Win32之上实现的.每个都有自己的GetMessage()/DispatchMessage()循环来泵送Win32窗口消息.
这是每个GetMessage()/DispatchMessage()循环的实现位置:
System.Windows.Forms.ApplicationSystem.Windows.Threading.DispatcherWPF Application对象是可选的
您的问题假定WPF在Application对象中实现了消息循环.不是这种情况.在WPF中,WinForms在Application对象中处理的所有基本函数都已移至其他对象,如Dispatcher,HwndSource,InputManager,KeyboardDevice,MouseDevice等.
在WPF中,Application对象是完全可选的.您可以使用复杂的UI构建完整的WPF应用程序,而无需创建Application对象.应用程序对象仅在您需要其提供的服务之一时才有用,例如:
ResourceDictionaryWM_APPACTIVATE消息映射到事件Activated和Deactivated事件WM_QUERYENDSESSION消息映射到OnSessionEnding事件中MainWindow)Navigated等)StartupUriApplication类还提供了几个有用的静态成员,例如FindResource,GetResourceStream并且LoadComponent不需要Application对象存在.
当你打电话时Application.Run(),它所做的只是:
WM_APPACTIVATE和WM_QUERYENDSESSION,和Dispatcher.Run()所有实际的消息循环功能都在Dispatcher.Run().
在WPF消息循环中注册未处理的异常
在Application.DispatcherUnhandledException你试图使用事件是围绕着一个简单的包装Dispatcher.UnhandledException盛会.我认为它们将它包含在Application对象中,因为WinForms程序员期望它存在,但是你的问题表明这可能适得其反.
要从WPF的Dispatcher注册未处理的异常,您所要做的就是:
Dispatcher.Current.UnhandledException += ...;
Run Code Online (Sandbox Code Playgroud)
不同于Application.Current,Dispatcher.Current不能为null:如果Dispatcher.Current从尚未安装Dispatcher的线程进行访问,则会自动创建一个.
订阅后Dispatcher.UnhandledException,当前线程上的Dispatcher消息循环中的任何未处理异常都将导致调用事件处理程序.请注意,这仅适用于Dispatcher.Run()泵送消息时未处理的异常:当另一种技术(如VB 6或WinForms)正在传送消息时,将使用该技术的异常处理机制.
WPF消息循环也是可选的
WPF不仅可以在不创建Application对象的情况下运行,它也可以在没有的情况下运行Dispatcher.Run(),只要另一项技术正在为Win32窗口消息提供信息.这是通过创建虚拟窗口和/或子类化WPF窗口来安装消息钩子来完成的.因此,无论什么消息循环正在泵送消息,WPF都将按预期工作.
实际上,在使用时ElementHost,除非使用以下方法之一,否则WPF Dispatcher不用于消息传递:
Window.ShowDialogDispatcher.InvokeDispatcher.Run (或等效地Application.Run)DispatcherOperation.Wait因此,可能不会调用您的WPF异常处理程序.相反,您需要在VB 6或WinForms级别安装异常处理程序.
你的问题的答案
在这种情况下是否创建了WPF应用程序对象?
没有.
如果没有,是什么导致消息被抽?
VB 6正在抽取消息.
如果我在后台线程上启动Application对象会发生什么?
很少:
如果您有应用程序资源,则会在后台线程上创建这些资源,这可能会导致在主线程上使用它们时出现异常.
如果向Application.Current.DispatcherUnhandledException添加处理程序,则它只适用于后台线程.换句话说,除非您在后台线程上创建窗口,否则永远不会调用处理程序.
你的Application.Startup将从后台线程调用,这可能是一件坏事.同样适用于StartupUri.
建议
从你的问题来看,听起来你在加载你的WPF控件时遇到了一个未处理的异常,你想捕获那个异常.在这种情况下,最好的计划可能是将WPF控件包装在一个简单的ContentControl中,其构造函数使用这样的代码来构造子代:
Dispatcher.Current.UnhandledException += handler;
Disptacher.Current.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>
{
Content = CreateChildControl();
Dispatcher.Current.Invoke(DispatcherPriority.ApplicationIdle, new Action(() => {});
});
Run Code Online (Sandbox Code Playgroud)
工作原理:BeginInvoke延迟了孩子的构建,直到VB 6和/或InteropControl完成所有处理.创建子控件后的Invoke调用将调用低优先级的空操作,从而导致所有挂起的DispatcherOperations完成.
最终结果是,在构造函数内或之后抛出的任何异常现在都传递给异常处理程序.