2016-06-24 3 views
0

Я хочу получить эффективное значение осознания DPI для указанного идентификатора процесса в 64-разрядной версии Windows 10 Pro. Значение, которое мне нужно, является одним из PROCESS_DPI_AWARENESS, которое я могу получить с помощью функции WinAPI GetProcessDpiAwareness.Вызов GetProcessDpiAwareness из C# всегда возвращает ошибку E_INVALIDARG

Чтобы реализовать то, что мне нужно, я написал простое одно окно C# WPF-приложения в VS 2015. Я ввожу идентификатор процесса, который меня интересует в TextBox txtProcessID, и результат отображается в TextBlock txtResult, когда я нажимаю кнопка 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. Что я делаю не так?

+4

[GetProcessDpiAwareness] (https://msdn.microsoft.com/en-us/library/windows/desktop/dn302113.aspx): * «hprocess: ** Обрабатывать ** процесса, который запрашивается». * - API ожидает дескриптор, тогда как вы вслепую передаете идентификатор. Чтобы получить дескриптор процесса из идентификатора процесса, вызовите [OpenProcess] (https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320.aspx), например. – IInspectable

+2

https://msdn.microsoft.com/en-us/library/system.diagnostics.process.handle(v=vs.110).aspx –

ответ

4

Из документации для GetProcessDpiAwareness первым параметром является дескриптор процесса, а не идентификатор процесса. Вам нужно будет OpenProcess, получить нужную информацию, затем CloseHandle. (При определении ар/Invoke подпись, любое имя переменной, которая начинается с h или lp, как правило, в управляемом коде 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); 
+0

PROCESS_VM_READ не требуется. – Elmue

+0

ВАЖНО: GetProcessDpiAwareness() не работает, если выполняется в службе (SYSTEM/NT AUTHORITY), чтобы получить эту информацию из другого процесса. Он всегда будет возвращать E_INVALIDARG – Elmue

0

Да, это было мое упущение. Мне нужно было передать дескриптор процесса в GetProcessDpiAwareness. Я только что написал свой собственный рабочий код, основанный на первых 2 комментария под мой вопрос:

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

Один дополнительный комментарий: лучше выполнить этот код с правами администратора, чтобы избежать проблем с доступом к другим процессам.

+4

Возможно, лучше не запускать этот код с правами администратора. Просто передайте соответствующее [право доступа] (https://msdn.microsoft.com/en-us/library/windows/desktop/ms684880.aspx) к [OpenProcess] (https://msdn.microsoft.com/en- нас/библиотека/окно/настольный/ms684320.aspx). 'PROCESS_QUERY_LIMITED_INFORMATION' должно быть достаточно. – IInspectable

+0

@ IInpectable, я использую способ .NET для доступа к другому процессу. Вы знаете, как это сделать в этой среде? FYI: запуск моего мини-инструмента с правами администратора для меня не является проблемой, так как все сделано в моем dev pc :) – TecMan

+0

Невозможно предоставить запрашиваемые права доступа при использовании [System.Diagnostics .Process] (https://msdn.microsoft.com/en-us/library/system.diagnostics.process.aspx). Вы могли бы P/Invoke 'OpenProcess', хотя, как объяснено в ответе [theB] (http://stackoverflow.com/a/38012663/1889329). – IInspectable

Смежные вопросы