3

When my WinForm application is moved from a primary monitor to a secondary monitor, the bounds (Height and Width of the Window) value returned by these properties and winapi function does not change :

  • Form.Bounds
  • Form.Size
  • GetWindowRect()

But the value returned by DwmGetWindowAttribute with DWMWA_EXTENDED_FRAME_BOUNDS changes drastically.

Edits after suggestion from comments: For instance, in the primary monitor DwmgetWindowAttribute returns a rect of width and height 292, 100 and the other values return 306, 107. This is coherent considering the drop shadow takes up 7 pixels. But when the window is moved to the secondary monitor, DwmgetWindowAttribute returns 437, 150 and the other methods return the same 306, 107

In fact both my monitors are of resolution 1920 * 1080 (but the scale differs though if it matters)

Question: Why does this happen this way? Is it only me or have anyone else faced similar issues? Ultimately I want to calculate the drop shadow size. Is there any other way?

To Reproduce:

If you wish to reproduce this, create a winform project. Allocate console using AllocConsole() in the constructor. Add an event handler for LocationChanged and add the following code to the event handler:

private void AgentMainForm_LocationChanged(object sender, EventArgs e)
{
      Console.WriteLine("bounds: {0}, {1}", Bounds.Width, Bounds.Height);
      Console.WriteLine("Size: {0}, {1}", Size.Width, Size.Height);
      Console.WriteLine("Window Rect: {0}, {1}", GetSizeWithShadow().Width, GetSizeWithShadow().Height);
      Console.WriteLine("Window Rect: {0}, {1}", GetSizeWithoutShadow().Width, GetSizeWithoutShadow().Height);
}

[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);

[DllImport("dwmapi.dll")]
public static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out RECT pvAttribute, int cbAttribute);

public Size GetSizeWithoutShadow()
{
      RECT regionWithoutShadow;
      IntPtr hWnd = this.Handle;
      if (Environment.OSVersion.Version.Major < 6) //DwmGetWindowAttribute does not work in XP, compatible only from Vista
      {
            GetWindowRect(hWnd, out regionWithoutShadow);
      }
      else
      {
            if (DwmGetWindowAttribute(hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, out regionWithoutShadow, Marshal.SizeOf(typeof(RECT))) != 0)
            {
                 //Handle for failure
            }
      }

      return new Size(regionWithoutShadow.right - regionWithoutShadow.left, regionWithoutShadow.bottom - regionWithoutShadow.top);
}

public Size GetSizeWithShadow()
{
     RECT regionWithoutShadow;
     IntPtr hWnd = this.Handle;

     GetWindowRect(hWnd, out regionWithoutShadow);

     return new Size(regionWithoutShadow.right - regionWithoutShadow.left, regionWithoutShadow.bottom - regionWithoutShadow.top);
}

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int left;
    public int top;
    public int right;
    public int bottom;
}
Shameel Mohamed
  • 607
  • 5
  • 23
  • 2
    There is no question in your, well, question. Please read [ask]. – IInspectable Feb 27 '18 at 10:25
  • You didn't specify what kind of results you expected and what you measured. `GetWindowRect()` returns logical coordinates, while `DwmGetWindowAttribute()` returns a phisical reference (which take into count the window shadow, as in your code). The computed size of a window is, however, the same. The DPI-awareness of the application can change the results in a High-DPI scaled viewport. – Jimi Feb 28 '18 at 14:43
  • 1
    Actually I'm not expecting any results. Like I said in the question I am just trying to understand why it happens this way. I believe the bound values I observe are irrelevant that's why did not mention them. Anyways I'll add and clarify things up. – Shameel Mohamed Mar 01 '18 at 11:53
  • 1
    You are surely seeing the side-effects of DPI virtualization. Which pretends that the window has the size you asked for, but actually automatically scales the window so it doesn't become too small. DWM values are probably not virtualized. If you don't want to be lied to then you have to declare your app [to be dpiAware](https://stackoverflow.com/a/13228495/17034), selecting `True/PM` to also suppress per-monitor virtualization. Hard to deal with in a winforms app because it is now your job to scale the window. https://msdn.microsoft.com/en-us/library/windows/desktop/mt843498(v=vs.85).aspx – Hans Passant Mar 01 '18 at 12:41

1 Answers1

1

You get this answer because your DPI scale is 1.5 on one monitor and 1.0 on the other. It might be possible to reconcile using GetDpiForWindow.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Nixon
  • 11
  • 1