6

I'm new to WPF (and to DPI-awareness APIs) and am writing an application for running in Windows 7, 8.1, and 10. I use multiple monitors with different per-monitor DPI settings, and am interested in making my application as compatible as possible across desktop configurations.

I already know that is possible to add a Manifest to a WPF application, uncomment the section for DPI awareness, and set it to True/PM. This successfully allows the program to be Per-Monitor DPI-aware in Windows 8.1 and 10 (thus looking clean and sharp over various monitors), but run as System-aware in Windows 7.

But can we do one step better? Microsoft provides a neat tutorial here that shows how to create a Per-Monitor DPI-Aware WPF application. What they do is actually create a new object in C++ to replace <Window> that uses Windows 8.1 APIs to not only detect the DPI change between the monitors, but then to re-size the application at runtime, effectively matching the change in DPI. The end result is that not only is the app Per-DPI aware and sharp looking, but when switching between large and small monitors of various sizes, the app looks to the user to change the same physical size (in inches or centimeters) on screen.

The downside to Microsoft's Win32 code is that it is not backwards compatible with Windows 7. Trying to run in 7 results in the application crashing.

I've gone through this article on Dr.Dobbs and this one by Kenny Karr but don't understand the APIs well enough to make things work.

Does anyone know of a way to do something similar to Microsoft's code, where in Windows 8.1 and newer, an application that is Per-Monitor aware will actually change size when moving between monitors, but runs as System-Aware in Windows 7?

Cœur
  • 37,241
  • 25
  • 195
  • 267
ohioDeveloper
  • 366
  • 3
  • 13

1 Answers1

-1

I haven't looked at the code, but my guess is that the crash is caused because of missing native methods in win 7. The way to fix this is to modify the native methods wrapper, for example (pseudo code):

public int GetDPIOfMonitor(IntPtr monitorHandle)
{
   if (Enviroment.OSVersion >= "6.2") // win 8 and above
   {
      return NativeMethods.PInvoke.GetMonitorDPI(monitorHandle);
   }
   else return systemDPI;
}

This way you don't crash under 7, because you are not calling the missing native methods.

Marko
  • 2,266
  • 4
  • 27
  • 48
  • It's been a while since I've been over this problem, but I've learned a little bit more... I believe your solution will fail at runtime. My theory is that when the application enters `public int GetDPIOfMonitor(IntPtr monitorHandle)` that the CLR will attempt to load `NativeMethods` into memory, which would still result in the crash on Windows 7. One way around this would be to put `return NativeMethods.PInvoke.GetMonitorDPI(monitorHandle);` itself into another method, which means the CLR won't load the necessary code should the if statement fail. I'd like to test both to be sure. – ohioDeveloper Dec 29 '15 at 21:23
  • emoacht has been developing a [library that can handle a lot of this work and runs on Windows 7 and up](https://github.com/emoacht/WpfMonitorAware). I haven't used it because I currently don't understand Window Chrome well enough to combine it with libraries like [FluentRibbon](https://github.com/fluentribbon/Fluent.Ribbon). – ohioDeveloper Dec 29 '15 at 21:27
  • @ohioDeveloper - you are mistaken. That is not how p/invoke works. p/invoke is just a string defintion. It is completely arbitrary. It will only be checked for validity once you actually call the p/invoke method. As such you can have p/invoke definitions in your app for all sorts of things, doesn't matter if they don't exist. I have developed apps that had to run in XP and 7. Of course XP didn't contain native methods that 7 had. Using the above pseudo code worked perfectly and did not fail. – Marko Dec 30 '15 at 09:06
  • @ohioDeveloper: You can also simply `try{}catch(DllNotFoundException){}` to check for missing PInvoke methods. – caesay Jun 17 '16 at 00:50