本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《 阿里云开发者社区用户服务协议 》和 《 阿里云开发者社区知识产权保护指引 》。如果您发现本社区中有涉嫌抄袭的内容,填写 侵权投诉表单 进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

本篇主要介绍一些与窗口句柄相关的一些API,比如设置窗口状态、当前激活的窗口、窗口客户区的大小、鼠标位置、禁用控件等,以及介绍Winform中的句柄属性,便于直接获取控件或窗体句柄,以及不推荐使用C#自带的 Process MainWindowHandle 获取句柄的原因...

HWND窗口句柄其他的一些API

ShowWindow操作窗口状态(最大化、最小化、正常大小、关闭窗口)

[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern bool ShowWindow(IntPtr hwnd, int nCmdShow);

nCmdShow参数表示窗体状态,其常见的取值含义为:

  • 0 —— SW_HIDE 隐藏窗口,并激活其他窗口
  • 1 —— SW_SHOWNORMAL / SW_NORMAL 激活并正常大小显示窗口
  • 2 —— SW_SHOWMINIMIZED 最小化窗口
  • 3 —— SW_SHOWMAXIMIZED / SW_MAXIMIZE 最大化窗口
  • 4 —— SW_SHOWNOACTIVATE 显示但不激活
  • 更多的其它取值请查看官方文档中的介绍。

    获取当前激活的正在工作的窗口句柄(获取当前活动窗口句柄)

    返回值有可能为null

    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    public static extern IntPtr GetForegroundWindow();

    判断窗口状态

    /// <summary>
    /// 当前窗口是否最小化
    /// </summary>
    /// <param name="hWnd"></param>
    /// <returns></returns>
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool IsIconic(IntPtr hWnd);
    /// <summary>
    /// 当前窗口是否最大化
    /// </summary>
    /// <param name="hWnd"></param>
    /// <returns></returns>
    [DllImport("user32.dll")]
    public static extern bool IsZoomed(IntPtr hWnd);
    /// <summary>
    /// 判断窗口是否可见
    /// </summary>
    /// <param name="hWnd"></param>
    /// <returns></returns>
    [DllImport("user32")]
    public static extern bool IsWindowVisible(IntPtr hWnd);

    设置窗口标题Title

    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    public static extern int SetWindowText(IntPtr hWnd, string title);

    获取窗口客户区大小

    [DllImport("user32.dll")]
    private static extern bool GetClientRect(IntPtr hwnd, out LPRECT lpRect);
    /// <summary>
    /// 获取窗口客户区大小
    /// </summary>
    /// <param name="hwnd"></param>
    /// <returns></returns>
    public static Rectangle GetClientRect(IntPtr hwnd)
        LPRECT rect = default;
        GetClientRect(hwnd, out rect);
        return new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
    

    获取鼠标坐标

    /// <summary>
    /// 获取鼠标坐标
    /// </summary>
    /// <param name="pt"></param>
    /// <returns></returns>
    [DllImport("user32.dll", EntryPoint = "GetCursorPos")]
    public static extern bool GetCursorPos(out Point pt);

    获取指定坐标位置的窗体

    /// <summary>
    /// 获取指定位置所在的窗口句柄
    /// </summary>
    /// <param name="pt"></param>
    /// <returns></returns>
    [DllImport("user32.dll", EntryPoint = "WindowFromPoint")]
    public static extern IntPtr WindowFromPoint(Point pt);

    获取鼠标指向的窗体

    /// <summary>
    /// 获取鼠标位置的窗体
    /// </summary>
    /// <returns></returns>
    public static IntPtr WindowFromCursor()
        if (GetCursorPos(out Point pt))
            return WindowFromPoint(pt);
        return IntPtr.Zero;
    

    移动或调整窗体大小

    /// <summary>
    /// 移动窗体、调整窗体大小
    /// </summary>
    /// <param name="hWnd"></param>
    /// <param name="X">新位置</param>
    /// <param name="Y">新位置</param>
    /// <param name="nWidth">新大小</param>
    /// <param name="nHeight">新大小</param>
    /// <param name="bRepaint">是否重新渲染客户区,推荐始终为true</param>
    /// <returns></returns>
    [DllImport("user32.dll", SetLastError = true)]
    public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint=true);

    使变灰的禁用控件可用

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool EnableWindow(IntPtr hWnd, bool bEnable);

    Winform中使用窗口句柄

    Handle属性

    窗体或控件的Handle属性,可以直接获取对应的窗口句柄。

    var frmHwnd = this.Handle;
    var btnHwnd = button1.Handle;

    获取某控件在Windows中的类名

    Win 32 API 中窗口句柄的操作,很多有窗口或控件句柄的类名 ClassName这一项,比如 GetClassName API用于获取句柄的类名。

    窗口(控件)句柄的类名是Windows系统中的类名,并不是winform/WPF内部C#的控件或元素类名,这一点要注意。

    下面以一个Button按钮为例,通过属性button1.Handle句柄,获取其对应的类名GetClassName

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern int GetClassName(IntPtr hwnd, StringBuilder lpClassName, int nMaxCount);
    private void button1_Click(object sender, EventArgs e)
        int nret;
        var className = new StringBuilder(255);
        nret = GetClassName(button1.Handle, className, className.Capacity);
        if (nret != 0)
            MessageBox.Show("ClassName is " + className.ToString());
            MessageBox.Show("Error getting window class name");
    

    获取Winform中Button控件的C#类名对应的window class name(GetClassName):WindowsForms10.BUTTON.app.0.xxx

    通过Process进程类获取窗口句柄及相关信息【不推荐】

    进程的MainWindowHandleMainWindowTitle

    进程的MainWindowHandle属性可以直接获取主窗口句柄;MainWindowTitle属性为窗口的标题。

    如下,根据窗口标题模糊查找窗口句柄。

    /// <summary>
    /// 根据窗口标题查找窗口句柄【不推荐】
    /// </summary>
    /// <param name="puzze_title">窗口标题,支持模糊查询可指定标题的一部分;为null则返回所有句柄</param>
    /// <returns></returns>
    public List<IntPtr> FindHwndsByTitle(string puzze_title=null)
        //按照窗口标题来寻找窗口句柄
        Process[] ps = Process.GetProcesses();
        var intptrs = new List<IntPtr>();
        foreach (Process p in ps)
            if (puzze_title != null && !p.MainWindowTitle.Contains(puzze_title))
                continue;
            intptrs.Add(p.MainWindowHandle);
        return intptrs;
    

    MainWindow的问题

    Process.MainWindowHandle属性是合成的(synthetic),Windows中并没有main window相关的正式概念;

    一个程序可以创建多个可见的顶层窗口,对于Windows而言,它们都是相同的。

    因此,推荐使用EnumWindows获取(顶层)窗口,即之前介绍到的FindAllWindows方法。

  • C#操作实例总结(二)—— 窗口操作
  • Why can’t FindWindowEx find another program’s window by name?
  • Working with Win32 API in .NET
  • https://www.pinvoke.net/index.aspx
  •