5

I'm calling GetClientRect via pinvoke in C# to get dimensions of a panel (on WindowsForm) I'm using as DirectX render target area. I would've thought that WinAPI gives me the unscaled (i.e. returned values should be the same regardless of Windows display DPI settings) dimensions, but it appears to give me the scaled one instead (at least on Windows 8.1 as I have not tested it on other OSes).

The ClientRectangle property also returns the same scaled dimensions as GetClientRect. This remains the same when I've changed the form AutoScaleMode to none.

Is that the expected behavior with GetClientRect? If so, how do I get the unscaled dimensions?

EDIT: This only affects Windows 8.1. Tested it on Windows 7 and GetClientRect returns the unscaled dimensions!

Zach Saw
  • 4,308
  • 3
  • 33
  • 49
  • The actual DPI value matters a great deal. You'll have to declare your app [dpiAware](http://stackoverflow.com/a/13228495/17034) to disable the DPI virtualization feature that kicks in beyond 125%. – Hans Passant Aug 15 '14 at 17:13
  • My Win8.1 DPI is set to 125% (medium / default). So DPI virtualization shouldn't have kicked in? Anyway, see Cody's answer. Win8.1 does DPI virtualization for 125% too apparently. – Zach Saw Aug 16 '14 at 08:10

1 Answers1

3

There's no advantage to P/Invoking GetClientRect. You get exactly the same thing by querying the ClientRectangle property.

And yes, either one of those is going to return the size of the client area in physical pixels, which is apparently not what you want.

You are probably looking for the ID2D1RenderTarget::GetSize method, which returns the size of the render target in device-independent pixels (DIPs).

If you must convert physical pixels (e.g., from mouse events) then you can use something like ID2D1Factory::GetDesktopDpi to get the current scaling factors, which you can use to convert physical pixels into DIPs.

The reason you're seeing different behavior in Windows 8.1 is probably the result of changes to the way it handles DPI scaling. You may find that indicating your application is DPI-aware is an easier solution than futzing with the above. That will stop the UI from being automatically scaled to match the current DPI settings. You do this by adding the following to the application's manifest:

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
    <asmv3:application>
        <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
            <dpiAware>True/PM</dpiAware>
        </asmv3:windowsSettings>
    </asmv3:application>
</assembly>

From Visual Studio, right-click on the project and click "Add" -> "New Item". Choose the "Application Manifest File" and add the above code.

Related reading: Writing DPI-Aware Desktop and Win32 Applications

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • What added to my confusion was the fact that Win8.1 switches DPI-aware **on** for my app the next time it is started when my Full Screen exclusive mode switching failed the first time. It makes it hard to reproduce the problem as it only occurs during the first time the app is started following a system reboot. – Zach Saw Aug 16 '14 at 08:11
  • Yes, that is weird. I don't have Windows 8.1 personally, but I don't believe that it is possible for the operating system to arbitrarily declare an app DPI-aware. You, the programmer, have to do this yourself, as far as I know. If the app does not indicate that it is DPI-aware, then it is not. You get compatibility behavior. – Cody Gray - on strike Aug 16 '14 at 08:53
  • That's the expected behavior but unfortunately that isn't the case. I can empirically show that the second time the app runs after full screen mode switch failed, DPI virtualization gets disabled until the next reboot (or you rename the exe file). – Zach Saw Aug 16 '14 at 12:18
  • Interesting. The only thing that makes any sense is that it's detecting some API you're calling and deeming your app DPI-aware on that basis. Still strange, though. If I had Windows 8.1, I'd ask for repro code. Maybe ask a new question about it, if you still care. – Cody Gray - on strike Aug 16 '14 at 14:51
  • Yes, an educated guess would be the fact that I asked D3D to go full screen with a scaled dimension. All Win8.1 needed to check was if this scaled dimensions translate back to a valid full screen resolution when full screen exclusive mode failed. Another educated guess would be that it is the Win compatibility service that is doing that. I will need to investigate this further when I have some time. – Zach Saw Aug 17 '14 at 01:21