#.net #winapi #pinvoke
Вопрос:
У меня есть следующее .ЧИСТЫЙ код, который обращается к OpenProcessToken
Win32 API для получения имен владельцев всех процессов в системе:
using System.Security.Principal;
using System.Runtime.InteropServices;
public class Test
{
[DllImport("advapi32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle);
[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
private const UInt32 TOKEN_QUERY = 0x0008;
public static List<List<String>> GetProcessWithUsers()
{
var processes = Process.GetProcesses();
var result = new List<List<string>>();
foreach (var proc in processes)
{
result.Add(new List<string> { proc.ProcessName, GetProcessUser(proc) });
}
return resu<
}
public static string GetProcessUser(Process process)
{
IntPtr tokenHandle = IntPtr.Zero;
try
{
OpenProcessToken(process.Handle, TOKEN_QUERY, out tokenHandle);
WindowsIdentity wi = new WindowsIdentity(tokenHandle);
return wi.Name;
}
catch
{
return null;
}
finally
{
if (tokenHandle != IntPtr.Zero)
{
CloseHandle(tokenHandle);
}
}
}
}
Вызов Test.GetProcessWithUsers()
(например, в LINQPad) занимает почти 2 секунды для 280 процессов в моей системе.
Я не считаю это приемлемым количеством времени для выполнения этой задачи.
Process.GetProcesses()
это быстро, new WindowsIdentity()
вклад незначителен, так в чем же задержка OpenProcessToken()
? Существуют ли альтернативные функции Win32 API, которые были бы быстрее?
Комментарии:
1. Процесс. Дескриптор-это проблема (вызывает отказ в доступе). Должно быть намного быстрее, если вы работаете от имени администратора. Вы могли бы написать все это с помощью P/Invoke, чтобы избежать этого, если вам нужна производительность
2. @SimonMourier Я не могу запустить его как администратор, вот в чем дело.
3. Я понимаю, но так ли это быстрее? Если да, то перепишите, используя полный P/Invoke
4.
OpenProcessToken
все в порядке и не создает никаких проблем или замедления, если у вас есть дескриптор процесса сPROCESS_QUERY_LIMITED_INFORMATION
доступом. однако — какой смысл / конечная цель собирать идентификаторы безопасности владельцев ?5. Вот версия, которая должна быть быстрее (полный p/вызов) pastebin.com/raw/ae5Eg9iP
Ответ №1:
Большая часть используемого времени, по-видимому, вызвана . Process
Класс СЕТИ (законно созданное исключение с отказом в доступе и т. Д.), Поэтому вот полная версия P/Invoke, которая его не использует, но использует собственную функцию CreateToolhelp32Snapshot:
[DllImport("advapi32", SetLastError = true)]
private static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, out IntPtr TokenHandle);
[DllImport("kernel32", SetLastError = true)]
private static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32", SetLastError = true)]
private static extern IntPtr CreateToolhelp32Snapshot(int dwFlags, int th32ProcessID);
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool Process32First(IntPtr hSnapshot, ref PROCESSENTRY32 lppe);
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool Process32Next(IntPtr hSnapshot, ref PROCESSENTRY32 lppe);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct PROCESSENTRY32
{
public int dwSize;
public int cntUsage;
public int th32ProcessID;
public IntPtr th32DefaultHeapID;
public int th32ModuleID;
public int cntThreads;
public int th32ParentProcessID;
public int pcPriClassBase;
public int dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szExeFile;
}
public static List<List<string>> GetProcessWithUsers()
{
var result = new List<List<string>>();
const int TH32CS_SNAPPROCESS = 2;
var snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
var entry = new PROCESSENTRY32();
entry.dwSize = Marshal.SizeOf<PROCESSENTRY32>();
if (Process32First(snap, ref entry))
{
do
{
const int PROCESS_QUERY_LIMITED_INFORMATION = 0x00001000;
var handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, entry.th32ProcessID);
result.Add(new List<string> { entry.szExeFile, GetProcessUser(handle) });
if (handle != IntPtr.Zero)
{
CloseHandle(handle);
}
}
while (Process32Next(snap, ref entry));
}
CloseHandle(snap);
return resu<
}
public static string GetProcessUser(IntPtr handle)
{
if (handle == IntPtr.Zero)
return null;
const int TOKEN_QUERY = 0x0008;
if (!OpenProcessToken(handle, TOKEN_QUERY, out var tokenHandle))
return null;
var wi = new WindowsIdentity(tokenHandle);
CloseHandle(tokenHandle);
return wi.Name;
}
На моем компьютере я снизился с 1500 мс до 30 мс (x50).
Комментарии:
1.
CreateToolhelp32Snapshot
( особенноProcess32Next
) очень плохой дизайн и медленное сравнениеNtQuerySystemInformation(SystemProcessInformation..)
. если хотите максимальную скорость, конечно, лучше используйтеNtQuerySystemInformation
2. Здесь много отсутствующих проверок ошибок и
finally
блоков3. @Charlieface — Нет, это не пропущено, меня не интересуют подробности ошибок, я просто сбрасываю все, что могу. Что касается, наконец, здесь возможно не так много бросков (возможно, в конструкторе WindowsIdentity все в порядке), это код взаимодействия. В любом случае, вопрос был не в проверке ошибок.
4. Я думаю, что могу добавить проверку ошибок и предложения finally. «Сбрасываю все, что могу» достаточно хорошо для моего варианта использования.
5. я просто отмечаю, что
NtQuerySystemInformation
это будет быстрее (я не думаю, что ваше решение плохое, наоборот. просто это будет еще быстрее). почемуProcess32Next
недостаточно хорошо ? потому что он внутренне копировал данные (изNtQuerySystemInformation
) в раздел и при каждом вызовеProcess32First/Process32Next
сопоставлял и разматывал этот раздел и копировал данные в пользовательский буфер. на этой карте/снимается основная точка замедления. такжеProcess32Next
по какой-то неизвестной причине отбросьте некоторые нарушения, которые возвращаются по исходномуSystemProcessInformation
(идентификатор терминальной сессии)