WPF,VB和Application对象

场景:

> VB 6表单有一个InteropControl(WinForms).
> InteropControl有一个ElementHost
> ElementHost有我的WPF控件

一切似乎都在工作,除了Application.Current在需要它时似乎是null.我真正想做的就是在第一个表单完全显示之前挂钩到未处理的异常事件.

>在这种情况下是否创建了WPF应用程序对象?
>如果是,创建时?
>如果没有,是什么原因导致消息被泵送?
>如果我在后台线程上启动Application对象会发生什么?

首先,我将解释消息循环如何在互操作场景中工作,然后我将回答您的问题并提出一些建议.

您的方案中的消息循环实现

您的方案涉及三种不同的技术:VB 6,WinForms和WPF.这些技术中的每一项都是在Win32之上实现的.每个都有自己的GetMessage()/ DispatchMessage()循环来抽取Win32窗口消息.

这是实现每个GetMessage()/ DispatchMessage()循环的地方:

> VB 6在内部实现它
> WinForms在System.Windows.Forms.Application中实现它
> WPF在System.Windows.Threading.Dispatcher中实现它

WPF Application对象是可选的

您的问题假定WPF在Application对象中实现了消息循环.不是这种情况.在WPF中,WinForms在Application对象中处理的所有基本函数都已移动到其他对象,如Dispatcher,HwndSource,InputManager,KeyboardDevice,MouseDevice等.

在WPF中,Application对象是完全可选的.您可以使用复杂的UI构建完整的WPF应用程序,而无需创建Application对象.应用程序对象仅在您需要其提供的服务之一时才有用,例如:

>一个常见的ResourceDictionary
>将WM_APPACTIVATE消息映射到Activated和Deactivated事件
>将WM_QUERYENDSESSION消息映射到OnSessionEnding事件
>生命周期管理(启动/运行/关闭/退出)
>最后一个窗口或主窗口关闭自动关闭
> WPF窗口的默认图标
>记住第一个窗口打开(MainWindow)
> NavigationService事件的常见注册(导航等)
> StartupUri

Application类还提供了几个有用的静态成员,例如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 += ...;

与Application.Current不同,Dispatcher.Current不能为null:如果从尚未具有Dispatcher的线程访问Dispatcher.Current,将自动创建一个Dispatcher.Current.

一旦订阅了Dispatcher.UnhandledException,当前线程上的Dispatcher消息循环中的任何未处理的异常都将导致调用事件处理程序.请注意,这仅适用于Dispatcher.Run()抽取消息时未处理的异常:当另一种技术(如VB 6或WinForms)正在传送消息时,将使用该技术的异常处理机制.

WPF消息循环也是可选的

WPF不仅可以在不创建Application对象的情况下运行,它也可以在没有Dispatcher.Run()的情况下运行,只要另一种技术可以提取Win32窗口消息.这是通过创建一个虚拟窗口和/或子类化WPF窗口来安装消息钩子来完成的.因此,无论什么消息循环正在泵送消息,WPF都将按预期工作.

实际上,当您使用ElementHost时,除非您使用以下方法之一,否则WPF Dispatcher不会用于消息传递:

> Window.ShowDialog
> Dispatcher.Invoke
> Dispatcher.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(() => {});
});

工作原理:Be​​ginInvoke延迟了孩子的构建,直到VB 6和/或InteropControl完成所有处理.创建子控件后的Invoke调用调用低优先级的空操作,从而导致所有挂起的DispatcherOperations完成.

最终结果是,在构造函数内或之后抛出的任何异常现在都传递给异常处理程序.

相关文章

Format[$] ( expr [ , fmt ] ) format 返回变体型 format$ 强制返回为文本 --------------------------...
VB6或者ASP 格式化时间为 MM/dd/yyyy 格式,竟然没有好的办法, Format 或者FormatDateTime 竟然结果和...
在项目中添加如下代码:新建窗口来显示异常信息。 Namespace My ‘全局错误处理,新的解决方案直接...
转了这一篇文章,原来一直想用C#做k3的插件开发,vb没有C#用的爽呀,这篇文章写与2011年,看来我以前没...
Sub 分列() ‘以空格为分隔符,连续空格只算1个。对所选中的单元格进行处理 Dim m As Range, t...
  窗体代码 1 Private Sub Text1_OLEDragDrop(Data As DataObject, Effect As Long, Button As Integ...