C# 如何在 WinForm 应用程序中获取最顶层表单的句柄?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1000847/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 05:14:53  来源:igfitidea点击:

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

c#winformswinapitopmost

提问by tzup

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.

我有一个 WinForm 应用程序,它有其他子表单(不是 mdi)。如果用户按下“Esc”,即使没有焦点,最上面的表单也应该关闭。

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

我可以使用键盘钩子全局捕获 Escape 但我还需要关闭表单的句柄。

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

我想有一种方法可以使用 Win32 API 来做到这一点,但是是否有使用托管代码的解决方案?

采纳答案by tzup

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

这是获取使用 Win32 的最顶层表单的一种方法(不是很优雅,但它有效):

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

回答by Bogdan_Ch

FormCollection is used by the Application object to list the currently open forms in an application through the OpenForms property

Application 对象使用 FormCollection 来通过 OpenForms 属性列出应用程序中当前打开的表单

See http://msdn.microsoft.com/en-us/library/system.windows.forms.application.openforms.aspx

请参阅http://msdn.microsoft.com/en-us/library/system.windows.forms.application.openforms.aspx

Then you could check TopMost() property of each form. And when you find a topmost form, you close it.

然后你可以检查每个表单的 TopMost() 属性。当您找到最顶层的表单时,将其关闭。

回答by NascarEd

You could implement a singleton-like pattern in your topmost form, and provide a static property that returns the one instance of itself and simply close it.

您可以在最顶层的表单中实现类似单例的模式,并提供一个静态属性,该属性返回自身的一个实例并简单地关闭它。

   public class MainForm : Form
   {
      private static MainForm mainForm;

      public static MainForm { get { return mainForm; } }

      public MainForm()
      {
         mainForm = this;
      }
   }


   // When the ESC key is pressed...
   MainForm.MainForm.Close();

回答by Simon

How about this using Application.Openforms

如何使用Application.Openforms

Form GetTopMostForm()
{
    return Application.OpenForms
        .Cast<Form>()
        .First(x => x.Focused);
}

回答by fred

I know this is a 4 yr old thread, but I had a similar problem and just came up with an alternative solution just in case anyone else stumbles on this question and doesn't want to mess around with Win32 calls.

我知道这是一个 4 岁的线程,但我遇到了类似的问题,只是想出了一个替代解决方案,以防万一其他人在这个问题上绊倒并且不想弄乱 Win32 调用。

I assume the top-most form will be the one that was last activated. So you could keep a separate collection of forms, similar to Application.OpenForms, except this collection would be ordered by when each was last activated. Whenever a form is activated, move it to the first item of the collection. Whenever you see the ESC key, you would close collection[0] and remove it.

我假设最顶层的表单将是最后激活的表单。因此,您可以保留一个单独的表单集合,类似于 Application.OpenForms,但该集合将按每个表单的上次激活时间排序。每当激活表单时,将其移动到集合的第一项。每当您看到 ESC 键时,您就会关闭 collection[0] 并将其删除。