强制创建WPF窗口的本机Win32句柄

13

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

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

我尝试在访问句柄之前调用窗口的this.Show()this.Hide()方法。然后我就可以获取句柄,但窗口会瞬间闪烁出现在屏幕上(不美观!)。

有人知道强制创建WPF窗口的方法吗?在Windows Forms中,这就像访问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);

你看过这个问题吗 - https://dev59.com/GHI_5IYBdhLWcg3wBub3 - 它可能没有帮助,因为它没有提到窗口是否已经存在。 - ChrisF
@ChrisF:谢谢!是的,我看到了。不幸的是,它存在窗口尚未创建的问题。 - Zach Johnson
我想提一下,以便得到您的回复,这样如果您的情况不同,它就不会被选为重复项。 - ChrisF
你是否真的需要提前触发句柄创建?还是只需要在它显示之前操纵句柄?如果我没记错的话,你可以使用SourceInitialized事件来实现这一点。 - Brian Reichle
4个回答

23

我遇到了同样的问题,这个答案是最好的! - laishiekai
这个回答比被采纳的那个好多了!请大家使用这个! - xandermonkey

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


3

一种选择是在显示窗口之前将窗口状态设置为最小化并不显示在任务栏中。尝试像这样做。

  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;

谢谢!最终我选择了这个变体(请参见我在问题上的编辑)。 - Zach Johnson
3
@DanielAlbuschat的答案 更简洁,避免了闪烁问题。 - Drew Noakes
是的,下面的答案更加简洁。 - xandermonkey
较新的答案更好,因为框架已经发展得更好以支持这些场景。 - Chris Taylor

1

我曾遇到同样的问题,选择了J Pollack的答案(因为它对我来说更简洁),但需要能够在.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及以上系统)的系统上运行它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接