【问题标题】:Forcing the creation of a WPF Window's native Win32 handle强制创建 WPF 窗口的本机 Win32 句柄
【发布时间】:2011-03-14 07:49:03
【问题描述】:

我需要访问一些 WPF 窗口的 Win32 窗口句柄,以便处理 Win32 激活消息。我知道我可以使用 PresentationSource.FromVisualWindowInteropHelper 来获取 Win32 窗口句柄,但是如果尚未创建 WPF 窗口,我就会遇到问题。

如果我使用PresentationSource.FromVisual,并且没有创建窗口,则返回的PresentationSource 为空。如果我使用WindowInteropHelper 并且没有创建窗口,则Handle 属性为IntPtr.Zero (null)。

在尝试访问句柄之前,我尝试在窗口上调用this.Show()this.Hide()。然后我可以得到句柄,但窗口在屏幕上瞬间闪烁(丑陋!)。

有人知道强制创建 WPF 窗口的方法吗?在 Windows 窗体中,这就像访问 Form.Handle 属性一样简单。

编辑:我最终选择了 Chris Taylor 答案的变体。在这里,以防它帮助别人:

static void InitializeWindow(Window window)
{
    // Get the current values of the properties we are going to change
    double oldWidth = window.Width;
    double oldHeight = window.Height;
    WindowStyle oldWindowStyle = window.WindowStyle;
    bool oldShowInTaskbar = window.ShowInTaskbar;
    bool oldShowActivated = window.ShowActivated;

    // Change the properties to make the window invisible
    window.Width = 0;
    window.Height = 0;
    window.WindowStyle = WindowStyle.None;
    window.ShowInTaskbar = false;
    window.ShowActivated = false;

    // Make WPF create the window's handle
    window.Show();
    window.Hide();

    // Restore the old values
    window.Width = oldWidth;
    window.Height = oldHeight;
    window.WindowStyle = oldWindowStyle;
    window.ShowInTaskbar = oldShowInTaskbar;
    window.ShowActivated = oldShowActivated;
}

// Use it like this:
InitializeWindow(myWpfWindow);

【问题讨论】:

  • 你见过这个问题吗 - stackoverflow.com/questions/1556182/… - 它可能没有帮助,因为它没有提到窗口是否已经存在。
  • @ChrisF:谢谢!是的,我已经看到了。不幸的是,它的问题是尚未创建窗口。
  • 我想我会提到它以获得您的回复,这样如果您的情况不同,它就不会被选为重复。
  • 你真的需要提前触发句柄创建吗?还是您只需要在显示之前操纵手柄? IIRC,您可以为此使用 SourceInitialized 事件。

标签: c# .net wpf winapi handle


【解决方案1】:

使用WindowInteropHelper.EnsureHandle,它完全满足您的需求。

【讨论】:

  • 我遇到了同样的问题,这个是最好的答案!
  • 再一次,这是一个比公认的更好的答案!请大家改用这个!
【解决方案2】:

一个选项是将窗口状态设置为最小化,并且在显示窗口之前不显示在任务栏中。试试这样的。

  IntPtr hWnd;
  WindowInteropHelper helper = new WindowInteropHelper(wnd);

  WindowState prevState = wnd.WindowState;
  bool prevShowInTaskBar = wnd.ShowInTaskbar;

  wnd.ShowInTaskbar = false;
  wnd.WindowState = WindowState.Minimized;
  wnd.Show();
  hWnd = helper.Handle;
  wnd.Hide();

  wnd.ShowInTaskbar = prevShowInTaskBar;
  wnd.WindowState = prevState;

【讨论】:

  • 谢谢!我最终选择了这个的变体(请参阅我对问题的编辑)。
  • @DanielAlbuschat's answer 更简单,避免闪烁。
  • 是的,下面的答案要干净得多。
  • 随着框架不断发展以更好地支持这些场景,更新的答案会更好。
【解决方案3】:

如果 WindowInteropHelper 的句柄为 NULL,我正在寻找解决方案。希望这篇文章能提供一些额外的信息来解决它。

一种解决方案是使用:

var window = new Window();
var handle = new WindowInteropHelper(window).EnsureHandle()

这仅适用于 .NET Framework 4。

目前我正在使用 .NET Framework 3.5,所以我需要另一个解决方案。然后我找到了一个带有 WindowInteropHelper 扩展方法的论坛帖子:

#region

using System;
using System.Reflection;
using System.Windows;
using System.Windows.Interop;

#endregion

namespace System.Windows.Interop
{
    /// <summary>
    ///   Provides NetFX 4.0 EnsureHandle method for
    ///   NetFX 3.5 WindowInteropHelper class.
    /// </summary>
    public static class WindowInteropHelperExtension
    {
        /// <summary>
        ///   Creates the HWND of the window if the HWND has not been created yet.
        /// </summary>
        /// <param name = "helper">An instance of WindowInteropHelper class.</param>
        /// <returns>An IntPtr that represents the HWND.</returns>
        /// <remarks>
        ///   Use the EnsureHandle method when you want to separate
        ///   window handle (HWND) creation from the
        ///   actual showing of the managed Window.
        /// </remarks>
        public static IntPtr EnsureHandle(this WindowInteropHelper helper)
        {
            if (helper == null)
                throw new ArgumentNullException("helper");

            if (helper.Handle == IntPtr.Zero)
            {
                var window = (Window) typeof (WindowInteropHelper).InvokeMember(
                    "_window",
                    BindingFlags.GetField |
                    BindingFlags.Instance |
                    BindingFlags.NonPublic,
                    null, helper, null);

                typeof (Window).InvokeMember(
                    "SafeCreateWindow",
                    BindingFlags.InvokeMethod |
                    BindingFlags.Instance |
                    BindingFlags.NonPublic,
                    null, window, null);
            }

            return helper.Handle;
        }
    }
}

WindowInteropHelper.EnsureHandle() 并不期望已经创建了一个窗口。

参考:Alexander Yudakov - http://social.msdn.microsoft.com/Forums/en-MY/wpf/thread/5f89ac58-d2ef-4ac0-aefb-b2826dbef48a

【讨论】:

    【解决方案4】:

    我被困在同样的问题上并选择了J Pollack's answer(因为它对我来说似乎更干净),但需要可以在 .NET 运行时 2.0 和 4.0 上运行的东西。

    但是当我这样做时,我最终得到了一个丑陋的MissingMethodException,因为 SafeCreateWindow 在 .NET 运行时 4.0 中不再存在。为了使代码在两个运行时上都能正常工作,我决定捕获 MissingMethodException 并在 .NET 4.0 运行时中调用等效项,而不是像这样:

        public static IntPtr EnsureHandle(this WindowInteropHelper helper)
        {
            if (helper == null)
                throw new ArgumentNullException("helper");
    
            if (helper.Handle == IntPtr.Zero)
            {
                var window = (Window)typeof(WindowInteropHelper).InvokeMember(
                    "_window",
                    BindingFlags.GetField |
                    BindingFlags.Instance |
                    BindingFlags.NonPublic,
                    null, helper, null);
    
                try
                {
                    // SafeCreateWindow only exists in the .NET 2.0 runtime. If we try to
                    // invoke this method on the .NET 4.0 runtime it will result in a
                    // MissingMethodException, see below.
                    typeof(Window).InvokeMember(
                        "SafeCreateWindow",
                        BindingFlags.InvokeMethod |
                        BindingFlags.Instance |
                        BindingFlags.NonPublic,
                        null, window, null);
                }
                catch (MissingMethodException)
                {
                    // If we ended up here it means we are running on the .NET 4.0 runtime,
                    // where the method we need to call for the handle was renamed/replaced
                    // with CreateSourceWindow.
                    typeof(Window).InvokeMember(
                        "CreateSourceWindow",
                        BindingFlags.InvokeMethod |
                        BindingFlags.Instance |
                        BindingFlags.NonPublic,
                        null, window, new object[] { false });
                }
            }
    
            return helper.Handle;
        }
    

    这使我可以使用 .NET 3.5 编译代码,但在仅安装了更高运行时版本的系统(即 Windows 8 及更高版本)上的 .NET 运行时 4.0 上运行它。

    【讨论】:

      猜你喜欢
      • 2010-09-05
      • 2011-11-21
      • 2010-12-06
      • 2010-12-20
      • 2011-10-17
      • 1970-01-01
      • 2013-05-09
      相关资源
      最近更新 更多