代码之家  ›  专栏  ›  技术社区  ›  Jonathan Allen

WPF、VB和Application对象

  •  7
  • Jonathan Allen  · 技术社区  · 14 年前

    • vb6窗体有一个InteropControl(WinForms)。
    • InteropControl有一个ElementHost
    • ElementHost拥有我的WPF控件

    除了那个应用程序,所有的东西似乎都在工作。当我需要它时,Current似乎是空的。我真正想做的就是在第一个窗体完全显示之前钩住未处理的异常事件。

    1. 在这个场景中,是否创建了WPF应用程序对象?
    2. 如果不是,是什么导致消息被抽取?
    2 回复  |  直到 14 年前
        1
  •  12
  •   Ray Burns    14 年前

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

    场景中的消息循环实现

    您的场景涉及三种不同的技术:vb6、WinForms和WPF。这些技术中的每一种都是在Win32之上实现的。每个人都有自己的想法 GetMessage()/DispatchMessage() 循环以泵送Win32窗口消息。

    这里是每个 GetMessage()/DispatchMessage() 实现循环:

    • vb6内部实现
    • WinForms在中实现它 System.Windows.Forms.Application
    • WPF在 System.Windows.Threading.Dispatcher

    WPF应用程序对象是可选的

    您的问题假设WPF在应用程序对象中实现了消息循环。事实并非如此。在WPF中,WinForms在应用程序对象中处理的所有基本函数都被移动到其他对象,如Dispatcher、HwndSource、InputManager、KeyboardDevice、MouseDevice等。

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

    • 普通的 ResourceDictionary
    • 映射 WM_APPACTIVATE 信息输入 Activated Deactivated 事件
    • 映射 WM_QUERYENDSESSION 信息输入 OnSessionEnding 事件
    • 生命周期管理(启动/运行/关闭/退出)
    • WPF窗口的默认图标
    • 记得第一扇窗户开了吗( MainWindow )
    • Navigated 等)
    • StartupUri

    应用程序类还提供了几个有用的静态成员,例如 FindResource , GetResourceStream LoadComponent

    当你打电话的时候 Application.Run()

    1. 将机构安装到手柄上 WM\U激活 WM\ U查询结束会话
    2. 执行 Dispatcher.Run()

    所有实际的消息循环功能都在 .

    这个 Application.DispatcherUnhandledException 您尝试使用的事件是围绕 Dispatcher.UnhandledException 事件。我认为他们将它包含在应用程序对象中是因为WinForms程序员希望它在那里,但是您的问题表明这可能适得其反。

    要从WPF的Dispatcher注册未处理的异常,您只需执行以下操作:

    Dispatcher.Current.UnhandledException += ...;
    

    Application.Current , Dispatcher.Current 不能为空:如果您访问 调度员。当前

    ,来自 在当前线程上,将导致调用事件处理程序。请注意,这仅适用于以下情况下未处理的异常: Dispatcher.Run() 正在泵送消息:当其他技术(如vb6或WinForms)正在泵送消息时,将使用该技术的异常处理机制。

    WPF不仅可以在不创建应用程序对象的情况下运行,还可以在不创建应用程序对象的情况下运行 Dispatcher.Run() ,只要另一种技术正在泵送Win32窗口消息。这是通过创建一个虚拟窗口和/或子类化一个WPF窗口来安装消息钩子来实现的。因此,无论哪个消息循环在泵送消息,WPF都将按预期工作。

    事实上,当你使用 ElementHost ,除非使用以下方法之一,否则WPF调度程序不用于消息泵送:

    • Window.ShowDialog
    • Dispatcher.Invoke
    • Dispatcher.Run (或等同地, Application.Run
    • DispatcherOperation.Wait

    因此,您的WPF异常处理程序可能不会被调用。相反,您需要在vb6或WinForms级别安装异常处理程序。

    在这个场景中,是否创建了WPF应用程序对象?

    如果不是,是什么导致消息被抽取?

    vb6正在传输消息。

    如果我在后台线程上启动应用程序对象会发生什么?

    • 如果您有应用程序资源,这些资源将在后台线程上创建,当它们在主线程上使用时,可能会导致异常。

    • 如果向Application.Current.dispatchernhandledException添加处理程序,它将仅应用于后台线程。换句话说,除非在后台线程上创建窗口,否则永远不会调用处理程序。

    • 您的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(() => {});
    });
    

    工作原理:BeginInvoke延迟子级的构造,直到vb6和/或InteropControl完成所有处理。创建子控件后的Invoke调用以低优先级调用空操作,导致所有挂起的DispatcherOperations完成。

        2
  •  3
  •   Eli Arbel    14 年前

    在WPF中 Application 对象不直接负责消息泵,它是 Dispatcher Application.Run() 在启动时调用 Dispatcher.Run()

    Application.Current 会回来的 null 既然是这样 从未 创建。消息泵送由VB处理,因为它创建了主窗口。如果在代码中依赖它,则可以:

    • 创建新的 对象:

      if (Application.Current != null)
      {
          new Application();
      }
      

      应用程序。当前 .

    • 尽可能避免依赖它(我认为这是推荐的方法)。您应该注意,这个类提供的许多服务(例如退出事件)无论如何在您的场景中都不可用。如果只需要未处理的异常事件,则可以使用 Dispatcher.CurrentDispatcher.UnhandledException .