Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SetProcessDpiAwareness not having effect

I've been trying to disable the DPI awareness on a ClickOnce application.
I quickly found out, it is not possible to specify it in the manifest, because ClickOnce does not support asm.v3 in the manifest file.

The next option I found was calling the new Windows function SetProcessDpiAwareness.

According to this tutorial,

Call SetProcessDpiAwareness before you create the application window.

And this tutorial,

you must call SetProcessDpiAwareness prior to any Win32API call

You have to call the function pretty early on. So, to test, I have created an entirely blank WPF application, and made this my entire App class:

[DllImport("SHCore.dll", SetLastError = true)]
private static extern bool SetProcessDpiAwareness(PROCESS_DPI_AWARENESS awareness);

[DllImport("SHCore.dll", SetLastError = true)]
private static extern void GetProcessDpiAwareness(IntPtr hprocess, out PROCESS_DPI_AWARENESS awareness);

private enum PROCESS_DPI_AWARENESS
{
    Process_DPI_Unaware = 0,
    Process_System_DPI_Aware = 1,
    Process_Per_Monitor_DPI_Aware = 2
}

static App()
{
    var result = SetProcessDpiAwareness(PROCESS_DPI_AWARENESS.Process_DPI_Unaware);
    var setDpiError = Marshal.GetLastWin32Error();
    MessageBox.Show("Dpi set: " + result.ToString());

    PROCESS_DPI_AWARENESS awareness;
    GetProcessDpiAwareness(Process.GetCurrentProcess().Handle, out awareness);
    var getDpiError = Marshal.GetLastWin32Error();
    MessageBox.Show(awareness.ToString());

    MessageBox.Show("Set DPI error: " + new Win32Exception(setDpiError).ToString());
    MessageBox.Show("Get DPI error: " + new Win32Exception(getDpiError).ToString());
}

The 3 message boxes show this content:

Dpi set: True
Process_System_DPI_Aware
Set DPI error: System.ComponentModel.Win32Exception (0x80004005): Access is denied
System.ComponentModel.Win32Exception (0x80004005): The operation completed successfully

Why is the application still set to DPI_Aware? Is this call not early enough?
The application does indeed experience DPI scaling.

When I use the manifest definition:

<application xmlns="urn:schemas-microsoft-com:asm.v3">
  <windowsSettings>
    <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">false</dpiAware>
  </windowsSettings>
</application>

It does return Process_DPI_Unaware.

EDIT 1:
Now grabbing Marshal.GetLastWin32Error() directly after pInvoke methods, this now actually returns an error.

like image 755
René Sackers Avatar asked Aug 21 '15 19:08

René Sackers


1 Answers

Beware with SetLastError and GetLastWin32Error, any call in between such as MessageBox.Show will affect its result. Make sure to always get the last error right after calling your native method.

So it could be very well that you are getting the expected behavior but being misled by the error code.

See this blog post for a complete explanation : http://blogs.msdn.com/b/oldnewthing/archive/2015/08/19/10636096.aspx

EDIT

Not quite sure about what's causing access denied ...but there's a simple and effective trick that disables DPI awareness :

Edit your AssemblyInfo.cs and add the following:

[assembly: DisableDpiAwareness]

Source: https://code.msdn.microsoft.com/windowsdesktop/Per-Monitor-Aware-WPF-e43cde33 (comments in PerMonitorAwareWPFWindow.xaml.cs)

like image 153
aybe Avatar answered Oct 11 '22 02:10

aybe