【问题标题】:Calling GetProcessDpiAwareness from C# always returns error E_INVALIDARG从 C# 调用 GetProcessDpiAwareness 总是返回错误 E_INVALIDARG
【发布时间】:2016-06-24 09:42:24
【问题描述】:

我想在 Windows 10 Pro 64 位中检索指定进程 ID 的有效 DPI 感知值。我需要的值是我可以通过 WinAPI GetProcessDpiAwareness 函数获得的PROCESS_DPI_AWARENESS 之一。

为了实现我所需要的,我在 VS 2015 中编写了一个简单的单窗口 C# WPF 应用程序。我在 TextBox txtProcessID 中输入我感兴趣的进程 ID,当我按下txtProcessID 按钮:

private const int S_OK = 0;
private enum PROCESS_DPI_AWARENESS
{
    PROCESS_DPI_UNAWARE = 0,
    PROCESS_SYSTEM_DPI_AWARE = 1,
    PROCESS_PER_MONITOR_DPI_AWARE = 2
}
[DllImport("Shcore.dll")]
private static extern int GetProcessDpiAwareness(IntPtr hprocess, out PROCESS_DPI_AWARENESS value);

private void btnGetDPIAwareness_Click(object sender, RoutedEventArgs e)
{
    int procIDint = int.Parse(txtProcessID.Text);
    IntPtr procID = new IntPtr(procIDint);
    PROCESS_DPI_AWARENESS value;
    int res = GetProcessDpiAwareness(procID, out value);
    if (res == S_OK)
        txtResult.Text = value.ToString();
    else
        txtResult.Text = "Error: " + res.ToString("X");
}

但是为任何进程调用 GetProcessDpiAwareness 总是会给我一个错误 E_INVALIDARG。我做错了什么?

【问题讨论】:

标签: c# winapi process interop dpi-aware


【解决方案1】:

GetProcessDpiAwareness 的文档中,第一个参数是进程句柄,而不是进程 ID。你需要OpenProcess,获取你想要的信息,然后CloseHandle。 (在定义 p/Invoke 签名时,任何以 hlp 开头的变量名在托管代码中通常是 IntPtr。)

如:

private const int PROCESS_QUERY_INFORMATION = 0x0400;
private const int PROCESS_VM_READ = 0x0010;

[DllImport("Kernel32.dll", SetLastError = true)]
private static extern IntPtr OpenProcess(uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId);

[DllImport("Kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr handle);

private const int S_OK = 0;
private enum PROCESS_DPI_AWARENESS
{
    PROCESS_DPI_UNAWARE = 0,
    PROCESS_SYSTEM_DPI_AWARE = 1,
    PROCESS_PER_MONITOR_DPI_AWARE = 2
}
[DllImport("Shcore.dll")]
private static extern int GetProcessDpiAwareness(IntPtr hprocess, out PROCESS_DPI_AWARENESS value);

private PROCESS_DPI_AWARENESS GetDpiState(uint processId)
{
    IntPtr handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, processId);
    if (handle != IntPtr.Zero)
    {
        PROCESS_DPI_AWARENESS value;
        int result = GetProcessDpiAwareness(handle, out value);
        if (result == S_OK)
        {
            System.Diagnostics.Debug.Print(value.ToString());
        }
        CloseHandle(handle);
        if (result != S_OK)
        {
            throw new Win32Exception(result);
        }
        return value;
    }
    throw new Win32Exception(Marshal.GetLastWin32Error());
}

虽然更简单的方法是使用System.Diagnostics.Process,例如:

System.Diagnostics.Process proc = Process.GetProcessById(processId);
PROCESS_DPI_AWARENESS value;
int res = GetProcessDpiAwareness(proc.Handle, out value);

【讨论】:

  • PROCESS_VM_READ 不是必需的。
  • 重要提示:如果在服务 (SYSTEM/NT AUTHORITY) 中执行 GetProcessDpiAwareness() 以从另一个进程获取该信息,则该方法不起作用。它总是会返回 E_INVALIDARG
【解决方案2】:

是的,这是我的疏忽。我需要将进程句柄传递给GetProcessDpiAwareness。我刚刚根据我的问题的前 2 个 cmets 编写了自己的工作代码:

int procID = int.Parse(txtProcessID.Text);
Process process = Process.GetProcessById(procID, ".");
PROCESS_DPI_AWARENESS value;
int res = GetProcessDpiAwareness(process.Handle, out value);
if (res == S_OK)
    txtResult.Text = value.ToString();
else
    txtResult.Text = "Error: " + res.ToString("X");
process.Close();

补充一点:最好以管理员权限执行此代码,以避免访问其他进程时出现问题。

【讨论】:

  • 可以说,最好不必以管理员权限运行此代码。只需将适当的access right 传递给OpenProcessPROCESS_QUERY_LIMITED_INFORMATION 应该足够了。
  • @IInspectable,我使用 .NET 托管方式访问另一个进程。你知道如何在这种环境下做到这一点吗?仅供参考:使用管理员权限启动我的迷你工具对我来说不是问题,因为一切都在我的开发电脑中完成:)
  • 在使用 System.Diagnostics.Process 类时,似乎无法提供所请求的访问权限。不过,您可以 P/Invoke OpenProcess,如 theB's answer 中所述。
  • 默认情况下,.Handle 属性返回的句柄以PROCESS_ALL_ACCESS 打开。没有提供任何方法来设置进程访问级别。见reference source
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-21
  • 1970-01-01
  • 1970-01-01
  • 2015-06-25
  • 2014-08-21
相关资源
最近更新 更多