C#中的Pinvoke用于获取窗口的DPI感知上下文

4

我正在尝试在C#应用程序中实现GetWindowDpiAwarenessContext,但一直没有成功。

相关的头文件为:

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);

我正在使用:

/// <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);
            }

它返回以下之一:0、18、34、24592、61457,而不是预期的-1、-2、-3或-4。此外,在对同一窗口进行后续调用时,返回值会有所变化。(这些窗口位于其他进程而不是该应用程序中。)
问题在于如何正确定义和调用GetWindowDpiAwarenessContext。
提前感谢您的帮助。

1
为什么返回类型要用 intptr,不只是 int 吗? - pm100
@pm 因为 DPI_AWARENESS_CONTEXT 是一个句柄,指针大小也是如此。 - David Heffernan
@Kenneth,你包含了DPI_AWARENESS的声明有点令人困惑。它与什么相关呢? - David Heffernan
@Hans Passant,我无法确定DPI_AWARENESS_CONTEXT中确切的内容,但我发现有一个函数AreDpiAwarenessContextsEqual。如果将其用于奇怪的返回值18、34、24592、61457上,则会发现它们分别等于-3、-4、-1和-2。因此,答案是你必须使用它来进行比较,而不是与-1、-2等进行比较。 - Kenneth Evans
不错的侦探工作,这在MSDN文章中并不明显。一定要将其发布为答案。 - Hans Passant
显示剩余3条评论
1个回答

9

我还没有找到DPI_AWARENESS_CONTEXT中确切的内容。它是一个处理句柄,对于32位和64位系统来说是不同的。或许它指向一个结构体或者是一个位掩码。如果是这样,那么我无法看出这个结构体的定义。

我现在知道你必须使用AreDpiAwarenessContextsEqual(context1, context2)来比较两个DPI_AWARENESS_CONTEXT。你不能直接比较这些值。这些是我正在使用的相关Pinvoke项:

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);

您可以使用WinDef.h中的这些值进行设置或比较:
#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)

但是如果在使用这些值进行设置之后进行Get操作,不要期望能够得到这些值之一。请使用AreDpiAwarenessContextsEqual。


1
DPI_AWARENESS_CONTEXT 只是一个 IntPtr,就我所知,它在 windef.h 中被声明为指向假结构的指针。您也可以在 https://dev59.com/wK3la4cB1Zd3GeqPSc3I#61359877 查看工作用例。 - Andreas Reiff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接