Why SetProcessDPIAware shouldn’t exist


I posted earlier that you shouldn’t call SetProcessDPIAware – and if you shouldn’t call it, why have it?  I’ve had some interest in this post, and looking at it again, I didn’t do a great job of explaining it.
 
Here’s why:
  1. Programmers don’t expect DPI to change. 
  2. This creates bugs when the DPI changes. 
  3. SetProcessDPIAware causes the DPI to change.
Generally, you need to reboot to have the DPI change.  But now, there are two scenarios where it can change right out from underneath you. SetProcessDPIAware is one of them – the other is when the aero glass/DWM encounters performance problems due to a misbehaving app and decides to bail.  You don’t have much control over the second one (which should also be very rare), but you do have control over SetProcessDPIAware (which happens every time you launch).
 
So this is a problem, of course – one that you’re choosing to embrace if you call SetProcessDPIAware.  The correct solution is to keep a cache of the DPI (like you’ve always done), and also cache what GetProcessDPIAware and DwmIsCompositionEnabled return.  When either of these are different from when you built your DPI cache, refresh your cache.  But programmers just aren’t doing this.  Even some of the basic MSFT dlls aren’t doing this right yet – we’re fixing these issues, of course.  But perhaps someone you call won’t.  It’s important to do this before creating other UI, but who knows when you’ve managed to do it early enough?  Perhaps some accessibility application is hooking your process and it’s not doing it right and then performs incorrect physical to logical DPI conversions.  Point being, why ask for more trouble when you don’t need to call SetProcessDPIAware?
 
You can achieve the important bit that you get from calling SetProcessDPIAware by putting an entry in your manifest.  This allows the flag to be set so early on that nothing has cached the DPI yet.  Whew.  Well, then I guess I can just stop worrying about DPI changes for everything outside my code.  Cool.  So why would I call SetProcessDPIAware again?  The best remaining answer I can see would be "because you don’t control the executable that would need to have the dpiAware manifest entry."  Whoa – why would that be?  Perhaps because you’re being loaded via extensibility points – then don’t mess with the process!  Who knows if it (and other extensions) are high DPI aware?  So just use the manifest entry instead.
 
So when does it make sense to call (or even have) SetProcessDPIAware?  Perhaps because you’re being run by rundll32?  Personally, I would write my own loader code and put in the manifest.  Are we really exposing this function just for rundll32-type scenarios?  There’s a real cost to creating, documenting and supporting APIs – maybe this one just shouldn’t exist.
Advertisements
This entry was posted in Vista. Bookmark the permalink.

6 Responses to Why SetProcessDPIAware shouldn’t exist

  1. Unknown says:

    Looks like your original post was taken down… was that intentional?
     
    I was able to find the post in google\’s cache, but nowhere else have I found any information on how to use the manifest for DPI awareness.  Is the manifest technique going to be pulled, is that why your post was taken down? 🙂

  2. James says:

    It\’s not being pulled.  We\’re definitely relying on it – and it\’s totally the right solution.  And although there are a few comments about it on the web, it looks like it\’s not locked down yet and may not even work on publicly available builds.  I\’ll put up more details once that\’s resolved.

  3. Colin Han says:

    Hi, James,

    I’m a component developer. I made a .NET WinForm control that use WM_POINTERDOWN message to process user’s touch input. But the coordinate of WM_POINTER is not based on DPI virtualize, it is based on physical coordinate. So, I need real DPI settings to convert it.

    The question is how do I get it without SetProcessDpiAware? Because you known, I’m component developer, I have no permit to modify the app’s setting. 😦

    • Hi Colin,

      That’s a tough one. As a component, you don’t own the process so you can’t guarantee that you won’t break other stuff.

      Here are some off-the-cuff thoughts:
      * You could have a setting for whether or not to call this. It may still work for some users of your component and it might be easier for them to handle than putting it into the manifest. However, it will likely be misused and DPI testing won’t occur and you’ll end up making the app author look bad. As a component, your goal should be to make the app author look awesome! This approach fails because it ends up exposing bugs in other code the process uses.

      * You could ensure the user sets the flag or else throw an exception (unless you’re running in the designer). This is obnoxious but ensures the right thing gets done. Your exception better make it really easy to fix, or you’ll end up alienating users who decide using your component is too complicated or frustrating. It should ‘just work’.

      * You could assume the user sets the flag, but do a trace on process start-up if it isn’t set. I don’t know what coordinate system you’re converting them into, but there are generally functions that will do what you need (albeit with less precision) when the flag isn’t set. This feels like a reasonable trade-off between developer education and a great ‘it just works’ feel to the component. If the app author doesn’t have time to learn about DPI, it will still work. If they do set the flag, your component will really shine and make their app shine.

      There may be better options, but for the options considered here I’d go with the third one.

      HTH,
      James

  4. I am no longer certain where you are getting your info, however great topic.
    I must spend some time finding out much more or working out more.
    Thanks for wonderful information I used to be
    in search of this info for my mission.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s