c# Pinvoke for GetWindowDpiAwarenessContext
Asked Answered
B

1

4

I am trying to implement GetWindowDpiAwarenessContext in a C# application with no success.

The relevent header files are:

windef.h

DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);

typedef enum DPI_AWARENESS {
    DPI_AWARENESS_INVALID           = -1,
    DPI_AWARENESS_UNAWARE           = 0,
    DPI_AWARENESS_SYSTEM_AWARE      = 1,
    DPI_AWARENESS_PER_MONITOR_AWARE = 2
} DPI_AWARENESS;

#define DPI_AWARENESS_CONTEXT_UNAWARE              ((DPI_AWARENESS_CONTEXT)-1)
#define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE         ((DPI_AWARENESS_CONTEXT)-2)
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE    ((DPI_AWARENESS_CONTEXT)-3)
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4)

WinUser.h

WINUSERAPI
DPI_AWARENESS_CONTEXT
WINAPI
GetWindowDpiAwarenessContext(
    _In_ HWND hwnd);

I am using:

/// <summary>
/// Class for native methods.
/// </summary>
internal static class NativeMethods {

[DllImport("user32.dll")]
internal static extern IntPtr GetWindowDpiAwarenessContext(IntPtr hWnd);

...

            // Dpi awareness context
            IntPtr dpiAwarenessContext = NativeMethods.GetWindowDpiAwarenessContext(process.Handle);
            if (dpiAwarenessContext == (IntPtr)(-1)) {
                sb.AppendLine("  DPI Awareness Context: DPI_AWARENESS_CONTEXT_UNAWARE");
            } else if (dpiAwarenessContext == (IntPtr)(-2)) {
                sb.AppendLine("  DPI Awareness Context: DPI_AWARENESS_CONTEXT_SYSTEM_AWARE");
            } else if (dpiAwarenessContext == (IntPtr)(-3)) {
                sb.AppendLine("  DPI Awareness Context: DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE");
            } else if (dpiAwarenessContext == (IntPtr)(-4)) {
                sb.AppendLine("  DPI Awareness Context: DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2");
            } else {
                sb.AppendLine("  DPI Awareness Context failed: " + dpiAwarenessContext);
            }

It returns one of the following: 0, 18, 34, 24592, 61457, not the expected -1, -2, -3, or -4. In addition, in subsequent calls to the same window, the return value varies. (The windows are in other processes than this application.)

The question is what is the proper way to define and call GetWindowDpiAwarenessContext.

Thanks in advance.

Blankly answered 28/2, 2018 at 16:12 Comment(8)
why intptr on the return type, isnt it just intDowns
@pm because DPI_AWARENESS_CONTEXT is a handle, and so is pointer sizedUno
@Kenneth it's a little confusing that you included the declaration of DPI_AWARENESS. How is that relevant?Uno
This function is new for SDK v10.0.16299.0. Trying it with a C program on Windows 10 Creators (I tried postpone installing it as long as I could), it just returns garbage. Not much to the function itself, it merely obtains a pointer to the internal window structure and returns the DWORD at offset 0x170. How the heck they could declare it as a handle is hard to guess. This is not ready for prime-time yet.Spearman
@David Heffernan, You are right, it is not really relevant for GetWindowDpiAwarenessContext, but it is related. There is a GetAwarenessFromDpiAwarenessContext that I was thinking about using. There seems to be no item in this enum for per-monitor aware v2.Blankly
@Hans Passant. I am also using GetDpiForWindow, which also is flakey (returns 0 or different values on subsequent calls). The use of DECLARE_HANDLE (Winnt.h) seems to be common for this sort of thing. I could specify the return value is an int and get the same results. (At least for 32-bit which it is using for AnyCpu.) Just observing -- I am not an expert.Blankly
@Hans Passant, I have been unable to find exactly what is in a DPI_AWARENESS_CONTEXT, but I did find there is a function AreDpiAwarenessContextsEqual. If used on the strange return values 18, 34, 24592, 61457, it finds them equal to -3, -4, -1, and -2, respectively. So the answer is that you have to use this to compare, not compare to -1, -2, etc.Blankly
Nice sleuthing, that was not obvious from the MSDN article. Be sure to post it as an answer.Spearman
B
10

I have yet to find exactly what is in a DPI_AWARENESS_CONTEXT. It is implemented as a handle, which will be different for 32-bit and 64-bit systems. Perhaps it points to a structure or perhaps it is a bitmask. If so, the structure is not defined that I can tell.

I do now know you have to use AreDpiAwarenessContextsEqual(context1, context2) to compare two DPI_AWARENESS_CONTEXT's. You can't compare the values. These are the relevant Pinvoke items I am using:

internal enum PROCESS_DPI_AWARENESS {
    PROCESS_DPI_UNAWARE = 0,
    PROCESS_SYSTEM_DPI_AWARE = 1,
    PROCESS_PER_MONITOR_DPI_AWARE = 2
}

internal enum DPI_AWARENESS {
    DPI_AWARENESS_INVALID = -1,
    DPI_AWARENESS_UNAWARE = 0,
    DPI_AWARENESS_SYSTEM_AWARE = 1,
    DPI_AWARENESS_PER_MONITOR_AWARE = 2
}

[DllImport("SHcore.dll")]
internal static extern int GetProcessDpiAwareness(IntPtr hWnd, out PROCESS_DPI_AWARENESS value);

[DllImport("user32.dll")]
internal static extern int GetDpiForWindow(IntPtr hWnd);

[DllImport("user32.dll")]
internal static extern IntPtr GetWindowDpiAwarenessContext(IntPtr hWnd);

[DllImport("user32.dll")]
internal static extern int GetAwarenessFromDpiAwarenessContext(IntPtr dpiContext);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool AreDpiAwarenessContextsEqual(IntPtr dpiContextA,
    IntPtr dpiContextB);

You can use these values from WinDef.h to set or compare:

#define DPI_AWARENESS_CONTEXT_UNAWARE              ((DPI_AWARENESS_CONTEXT)-1)
#define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE         ((DPI_AWARENESS_CONTEXT)-2)
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE    ((DPI_AWARENESS_CONTEXT)-3)
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4)

But if you do a Get after a Set with one of these values, don't expect to get one of these values back. Use AreDpiAwarenessContextsEqual.

Blankly answered 5/3, 2018 at 16:58 Comment(1)
DPI_AWARENESS_CONTEXT is just an IntPtr, it is declared as a pointer to a fake struct in the windef.h as far as I could tell. You can also have a look here https://mcmap.net/q/671317/-visual-studio-2015-community-report-viewer-version-12-error-getting-extra-margin-using-c for a working usage example.Indiscrimination

© 2022 - 2024 — McMap. All rights reserved.