Search code examples
winapidpi

Process unexpectedly DPI aware


I have a service that launches an executable into a user session with CreateProcessAsUser, specifying the desktop in the STARTUPINFO parameter. It works well.

My executable is not manifested, nor does it call any DPI-related API.

When I launch my executable manually by double-clicking or via cmd.exe, Task Manager correctly shows the DPI Awareness as "Unaware".

However, when my executable is launched by the service, Task Manager shows DPI Awareness as "Per Monitor" - and indeed, it behaves as such.

Setting the default DPI awareness for a process says:

There are two main methods to specify the default DPI awareness of a process:

  1. through an application manifest setting
  2. programmatically through an API call

I am doing neither of these things.

I confirmed that the .exe is not manifested by using mt.exe. I set function breakpoints on the following:

  • user32.dll!SetProcessDpiAwarenessContext
  • user32.dll!SetThreadDpiAwarenessContext
  • shcore.dll!SetProcessDpiAwareness

No breakpoint is hit; however when launched from the service, I can only attach my debugger once I'm already inside main - and it seems that the DPI awareness is already set at that point.

Is there anywhere else the DPI awareness can be getting set?

This is a hybrid rust / C application - there is no (for example) .NET dependency referenced.


EDIT:

Using the JIT debugger, I can break at mainCRTStartup and see the DPI Awareness is already "PerMonitor" at that point. Calling SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE) or SetProcessDpiAwareness(PROCESS_DPI_UNAWARE) have no effect.


EDIT:

When launched from my service with CreateProcessAsUser; the executable has this environment variable:

__COMPAT_LAYER=HighDpiAware

The environment passed to CreateProcessAsUser is created by calling:

CreateEnvironmentBlock with my user handle. The rest of the environment is as expected. Where is this coming from? There are no compatibility options set on the executable when I inspect it's properties in explorer...


Solution

  • My service is running as SYSTEM. When I call CreateProcessAsUser, in this case, the executable is also run as SYSTEM. I pass nullptr for the lpEnvironment parameter. MSDN says this:

    A pointer to an environment block for the new process. If this parameter is NULL, the new process uses the environment of the calling process.

    However, when I inspect the environment of my executable, I see:

    __COMPAT_LAYER=HighDpiAware

    This is forcing per-monitor DPI awareness. This is mysterious because, indeed - the AppCompatFlag is set for that executable in the registry for S-1-5-18 (SYSTEM), but I don't know how or where this value is coming from.

    The variable is not set on my service (which also runs as SYSTEM) - presumably services don't get the AppCompat environment? But why does my child process have it, despite supposedly inheriting the environment of it's parent? I suppose these compatability flags must have special handling.

    Anyway, the answer to my question is: remove HighDpiAware from the __COMPAT_LAYER environment variable.