Search code examples
c#winformswinapitopmost

How to get the handle of the topmost form in a WinForm app?


I have a WinForm app that has other child forms (not mdi). If the user presses "Esc" the topmost form should be closed even if it doesn't have the focus.

I can use a keyboard hook to globally catch the Escape but I also need the handle of the form to be closed.

I guess there is a way to do that using Win32 API, but is there a solution using managed code?


Solution

  • Here is one way to get the topmost form that uses Win32 (not very elegant, but it works):

    public const int GW_HWNDNEXT = 2; // The next window is below the specified window
    public const int GW_HWNDPREV = 3; // The previous window is above
    
    [DllImport("user32.dll")]
    static extern IntPtr GetTopWindow(IntPtr hWnd);
    
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool IsWindowVisible(IntPtr hWnd);
    
    [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "GetWindow", SetLastError = true)]
    public static extern IntPtr GetNextWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.U4)] int wFlag);
    
    /// <summary>
    /// Searches for the topmost visible form of your app in all the forms opened in the current Windows session.
    /// </summary>
    /// <param name="hWnd_mainFrm">Handle of the main form</param>
    /// <returns>The Form that is currently TopMost, or null</returns>
    public static Form GetTopMostWindow(IntPtr hWnd_mainFrm)
    {
        Form frm = null;
    
        IntPtr hwnd = GetTopWindow((IntPtr)null);
        if (hwnd != IntPtr.Zero)
        {
            while ((!IsWindowVisible(hwnd) || frm == null) && hwnd != hWnd_mainFrm)
            {
                // Get next window under the current handler
                hwnd = GetNextWindow(hwnd, GW_HWNDNEXT);
    
                try
                {
                    frm = (Form)Form.FromHandle(hwnd);
                }
                catch
                {
                    // Weird behaviour: In some cases, trying to cast to a Form a handle of an object 
                    // that isn't a form will just return null. In other cases, will throw an exception.
                }
            }
        }
    
        return frm;
    }