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:
- through an application manifest setting
- 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:
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...
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.