15

Setting the "Change the size of all items" slider of Control Panel\Appearance and Personalization\Display to Larger (which changes this registry entry: HKEY_CURRENT_USER\Control Panel\Desktop\DesktopDPIOverride) causes the Control.PointToScreen() method to miscalculate. This can be reproduced using the following Class1 in a Windows Form:

public class Class1 : Control
{
  protected override void OnPaint(PaintEventArgs e)
  {
    base.OnPaint(e);

    Draw(e.ClipRectangle, e.Graphics);
  }

  private void Draw(Rectangle rect, Graphics graphics)
  {
    Pen pen = new Pen(Color.Red);
    pen.Width = 2;

    graphics.DrawRectangle(pen, rect);
  }

  protected override void OnMouseDown(MouseEventArgs e)
  {
    base.OnMouseDown(e);

    Point p = this.PointToScreen(new Point(0, 0));

    ControlPaint.DrawReversibleFrame(new Rectangle(p, new Size(e.X, e.Y)), Color.Yellow, FrameStyle.Dashed);
  }

  protected override void OnMouseUp(MouseEventArgs e)
  {
    base.OnMouseUp(e);
    this.Invalidate();
  }
}

Using this control in a WinForm and clicking on it works as expected. Now change "Change the size of all items" to "Larger" and run the code again - the code no longer runs as expected, the PointToScreen method is returning an erroneous value for (0, 0).

Does anybody know how to resolve this issue? Many thanks.

VVN
  • 1,607
  • 2
  • 16
  • 25
Shunyata Kharg
  • 927
  • 3
  • 14
  • 26
  • 2
    Drawing PaintEventArgs.ClipRectangle never makes any sense. – Hans Passant Jul 01 '14 at 16:18
  • I'm seeing the same behavior. Did you ever find a solution? – adv12 Apr 21 '16 at 13:24
  • @HansPassant, the interesting bit is not the `Draw(e.ClipRectangle, e.Graphics)` but the results of `Control.PointToScreen` on a non-100%-DPI display, as drawn by the call to `DrawReversibleFrame`. I've observed the same behavior: PointToScreen seems to be broken when auto scaling is in effect. Any experience with this/thoughts? – adv12 Apr 21 '16 at 13:44
  • 3
    Since `Control.PointToScreen` is just a wrapper ([reference source](http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs,d74632ce304c7117)) to [MapWindowPoints](https://msdn.microsoft.com/en-us/library/windows/desktop/dd145046(v=vs.85).aspx) API, you are effectively stating that the whole Windows is broken. – Ivan Stoev Apr 21 '16 at 17:55
  • @IvanStoev, copy the above code into a WinForms project and add a `Class1` to the main form. Change your display DPI settings to something above 100%. Then run the project and click in the `Class1` control. `Class1` will create and draw a rectangle starting at screen coordinates calculated via `Control.PointToScreen` from the client location (0, 0). The rectangle drawn on the screen will start above and to the left of the `Class1` control. We are asserting that this happens. It seems like a bug in `Control.PointToScreen`, but I'd be happy to entertain other theories. – adv12 Apr 21 '16 at 19:07
  • An observation: when I set my display to scale by 125%, the dimensions returned from `Control.PointToScreen` need to be scaled by 1.25 to appear in the proper place onscreen. Same goes for width and height of the screen rectangle. – adv12 Apr 21 '16 at 19:48
  • 2
    @adv12 What I'm saying is that the issue is not specific to Windows Forms. `MapWindowPoints` is used in every Windows application (managed, unmanaged, whatever). – Ivan Stoev Apr 21 '16 at 19:53
  • 2
    @adv12 - It is GetDCEx() that is broken right now. ControlPaint() uses that function to get a device context for the desktop window to paint on. DPI virtualization is not applied. Probably highly specific to the Windows version, I'd guess this went wrong at Win8.1 and I see it go wrong on Windows 10 version 10586. Not easy to work around and risky to do If you don't want to declare your app dpiAware then it is rather best to call Microsoft Support. – Hans Passant Apr 21 '16 at 20:45
  • @HansPassant, in my own code I'm not using `ControlPaint()`, but I am using `Graphics.CopyFromScreen()`. Can I assume that also uses `GetDCEx()`? (Just trying to figure out how the OP and I stumbled on the same behavior...) – adv12 Apr 21 '16 at 21:10
  • 3
    Sigh. No, there isn't any way that dpi virtualization is going to give you the correct view on another app's windows. You **must** [declare your app dpiAware](http://stackoverflow.com/questions/13228185/winforms-high-dpi-blurry-fonts/13228495?s=1|0.0000#13228495). – Hans Passant Apr 21 '16 at 21:47
  • @HansPassant, I was actually trying to capture my own window--something drawn with Direct3D that I didn't know how to capture otherwise. Sorry to upset you; I'll look into declaring my app dpiAware. Thanks for the help. – adv12 Apr 21 '16 at 21:54

1 Answers1

2

Sounds like you need to make it DPI aware. You can do it like so

[DllImport("user32.dll")]
private static extern bool SetProcessDPIAware();

static void Main()
{
    SetProcessDPIAware();

}
Alex
  • 552
  • 3
  • 15