11
votes

I need to access the Win32 window handles of some of my WPF windows so I can handle Win32 activation messages. I know I can use PresentationSource.FromVisual or WindowInteropHelper to get the Win32 window handle, but I am running into problems if the WPF window has not been created yet.

If I use PresentationSource.FromVisual and the window has not been created, the returned PresentationSource is null. If I use WindowInteropHelper and the window has not been created, the Handle property is IntPtr.Zero (null).

I tried calling this.Show() and this.Hide() on the window before I tried to access the handle. I can then get the handle, but the window flashes momentarily on the screen (ugly!).

Does anyone know of a way to force a WPF window to be created? In Windows Forms this was as easy as accessing the Form.Handle property.

Edit: I ended up going with a variant on Chris Taylor's answer. Here it is, in case it helps someone else:

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);
4
Have you seen this question - stackoverflow.com/questions/1556182/… - it might not help as it doesn't mention whether the window already exists or not.ChrisF♦
@ChrisF: Thanks! Yes, I have seen that. Unfortunately, it has the problem with the window not being created yet.Zach Johnson
I thought I'd mention it to get your reply so that if your case was different it wouldn't get selected as a duplicate.ChrisF♦
Do you actually need to trigger handle creation early? or do you just need to manipulate the handle before its shown? IIRC, you could use the SourceInitialized event for this.Brian Reichle

4 Answers

3
votes

One option is to set window state to minimized and not to show in the taskbar before Showing the window. Try something like this.

  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;
3
votes

I was looking for a solution if the WindowInteropHelper's handle is NULL. Hopefully this post gives some additional information how to solve it.

One solution is to use:

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

This works only with .NET Framework 4.

At the moment I am using .NET Framework 3.5 so I needed another solution. Then I found a forum thread with a WindowInteropHelper extension method:

#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;
        }
    }
}

The WindowInteropHelper.EnsureHandle() does not expect a window is already created.

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

1
votes

I was stuck on this same issue and went with J Pollack's answer (because it seems cleaner to me), but needed something that would run both on the .NET runtime 2.0 and 4.0.

But when I did that I ended up with an ugly MissingMethodException because SafeCreateWindow does not exist in the .NET runtime 4.0 anymore. To make the code work on both runtimes I decided to catch the MissingMethodException and invoke the equivalent in the .NET 4.0 runtime instead like this:

    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;
    }

This allowed me to compile the code with .NET 3.5 but run it on .NET runtime 4.0 on systems that only have the higher runtime version installed (namely Windows 8 and above).