【问题标题】:Compute total width of title bar buttons for 3rd party window on windows 10计算 Windows 10 上第 3 方窗口的标题栏按钮的总宽度
【发布时间】:2020-07-03 11:25:08
【问题描述】:

我最初的方法是使用 GetSystemMetricsSystemMetric.SM_CXSIZE 以及一些简单的数学运算,基于哪些按钮可用(乘以 3,或乘以 1),通过 WindowStyle

[DllImport("user32.dll")]
private static extern int GetSystemMetrics(SystemMetric smIndex);

这在 Windows 10 上存在问题,计算出的宽度约为实际宽度的 70%。所以宽度只包括两个按钮——最大化和关闭。 Windows 7 和 8.1 很好,相同的 DPI 设置,它涵盖了所有按钮。

我检查了一些关于 Stack Overflow 的现有问题,并且从 2011 年开始,我在这个问题上取得了最大的成功:

不幸的是,虽然suggested approach 在 Windows 8.1 中确实有效,但它在 Windows 10 上计算为 0(最新版本,所有推荐的更新)。有没有一种适用于从 7 到 10 的所有操作系统的方法?

代码取自上述答案并修改为通过窗口句柄 (hwnd) 计算窗口控制按钮的总宽度,并将编组从 Rectangle 更改为 RECT(然后我得到正确的左/右值)。

public static int GetControlButtonsWidth(IntPtr hwnd)
{
    // Create and initialize the structure
    TITLEBARINFOEX tbi = new TITLEBARINFOEX();
    tbi.cbSize = Marshal.SizeOf(typeof(TITLEBARINFOEX));

    // Send the WM_GETTITLEBARINFOEX message
    SendMessage(hwnd, WM_GETTITLEBARINFOEX, IntPtr.Zero, ref tbi);

    int sum = tbi.rgrect.Sum(r => r.right - r.left);

    // Return the filled-in structure
    return sum;
}

internal const int WM_GETTITLEBARINFOEX = 0x033F;
internal const int CCHILDREN_TITLEBAR = 5;

[StructLayout(LayoutKind.Sequential)]
internal struct TITLEBARINFOEX
{
    public int cbSize;
    public RECT rcTitleBar;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHILDREN_TITLEBAR + 1)]
    public int[] rgstate;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHILDREN_TITLEBAR + 1)]
    public RECT[] rgrect;
}

[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessage(
        IntPtr hWnd,
        int uMsg,
        IntPtr wParam,
        ref TITLEBARINFOEX lParam);

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int left, top, right, bottom;
}

【问题讨论】:

  • 根据文档TITLEBARINFOEX结构需要RECT
  • 这个措施可能很偶然。您可以从 Win32 或标准 WinForm 窗口(不使用 DWM 组合)获取 sound 值。其他 Windows,你不知道你得到了什么度量。例如,Chrome WebBrowser:它没有 TitleBar。或者,它确实有一个,但它被挤压在主框架的左上角,并且不包含那些按钮。 Min/Max/Close 按钮​​在别处创建(上部波段的子面板)。这同样适用于许多其他应用程序。实际的 Window 是合成的。但是您可以使用 UIAutomation 获得这些按钮。
  • @DrakeWu-MSFT 谢谢,我在写完这个问题后不久就改成了 RECT。
  • @Jimi 它目前适用于我的 chrome,按钮相对于最小化按钮的中间错位了几个像素,但我的用例可以接受左右几个像素的错误。这是为了修复github上的开源软件,稍微提高可用性。
  • 是的,那是因为你向窗口发送了一个WM_GETTITLEBARINFOEX,它负责回复一个值。因此,Window 将返回一个值 compatible 与标准 Caption 的布局(这些按钮占据特定位置 - DWM 做同样的事情)。返回的度量是相对精确的:按钮实际上并不存在。当然,我不知道您将这些信息用于什么目的,因此,如果出于任何原因(可能是因为您需要访问这些元素)。

标签: c# winforms winapi window pinvoke


【解决方案1】:

您可以使用DwmGetWindowAttribute,在 Windows 10 上,这 3 个按钮的组合宽度应为 185 像素,DPI 为 125%。请注意,如果您的应用程序不支持 DPI,则结果仍将相同,例如 185。

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int left;
    public int top;
    public int right;
    public int bottom;
}

[DllImport("dwmapi.dll")]
public static extern int DwmGetWindowAttribute(
    IntPtr hwnd, int attr, out RECT ptr, int size);

public void foo()
{
    int DWMWA_CAPTION_BUTTON_BOUNDS = 5;
    RECT rc;
    if (0 != DwmGetWindowAttribute(this.Handle, DWMWA_CAPTION_BUTTON_BOUNDS,
        out rc, Marshal.SizeOf(typeof(RECT))))
    {
        //error
    }
    int width = rc.right - rc.left;
}

【讨论】:

  • 它在 win 8.1 和 10 上都有效,DPI 也为 150%。非常感谢!
  • 顺便说明一下,如果代码在W10上运行,客户区必须正确放置透明的resize边框,否则标题按钮的RECT会被边框的左移厚度。
猜你喜欢
  • 1970-01-01
  • 2018-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-19
  • 1970-01-01
相关资源
最近更新 更多