Reputation: 26424
My initial approach to this was using GetSystemMetrics
with SystemMetric.SM_CXSIZE
and some simple math based on which buttons are available (times 3, or times 1), via WindowStyle
.
[DllImport("user32.dll")]
private static extern int GetSystemMetrics(SystemMetric smIndex);
This has an issue on Windows 10, where the calculated width is approximately 70% of actual. So the width covers just two buttons - maximize and close. Windows 7 and 8.1 are fine, same DPI setting, where it covers all buttons.
I checked a few existing questions on Stack Overflow, and had most success with this one from 2011:
Unfortunately, while the suggested approach does work in windows 8.1, it calculates 0 on Windows 10 (latest version, all recommended updates). Is there a way that works on all OS from 7 to 10?
Code was taken from the above answer and modified to calculate total width of window's control buttons, by window handle (hwnd), and changed marshalling to RECT from Rectangle (then I get correct values of left/right).
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;
}
Upvotes: 2
Views: 975
Reputation: 31599
You can use DwmGetWindowAttribute
, the combined width for those 3 buttons should be 185 pixels on Windows 10, at 125% DPI. Note that if your application is not DPI aware, then the result will still be the same, 185 for example.
[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;
}
Upvotes: 2