You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET获取进程所有者速度过慢,求更高效的Win32 API实现方案

性能瓶颈原因

  • 每次访问Process.Handle属性时,底层会调用OpenProcess申请高权限访问,系统会对每个进程做权限校验,大部分系统进程、保护进程会拒绝高权限访问,单次权限校验的开销叠加后就会拉高总耗时。
  • 原有代码没有判断OpenProcessToken的返回值,失败时直接触发异常捕获流程,异常本身的执行开销远高于正常的返回值判断,进一步拖慢速度。
  • 每个进程都要单独走「打开进程→打开令牌→关闭令牌→关闭进程句柄」的流程,多次用户态/内核态切换的开销累加明显。

优化方案

方案1:最小改动优化现有代码

先替换进程句柄获取逻辑,用最低权限PROCESS_QUERY_LIMITED_INFORMATION打开进程,同时用返回值判断代替异常控制流程,不用改核心逻辑就能提升至少50%的速度:

using System.Security.Principal;
using System.Runtime.InteropServices;
using System.Diagnostics;

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)]
    private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool CloseHandle(IntPtr hObject);
    
    private const uint TOKEN_QUERY = 0x0008;
    private const uint PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;

    public static List<List<string>> GetProcessWithUsers() 
    {
        var processes = Process.GetProcesses();
        var result = new List<List<string>>(processes.Length); // 预分配容量减少GC开销
        foreach (var proc in processes)
        {
            result.Add(new List<string> { proc.ProcessName, GetProcessUser((uint)proc.Id) });
            proc.Dispose(); // 主动释放Process对象资源
        }
        return result;
    }

    public static string GetProcessUser(uint processId)
    {
        IntPtr processHandle = IntPtr.Zero;
        IntPtr tokenHandle = IntPtr.Zero;
        try
        {
            processHandle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, processId);
            if (processHandle == IntPtr.Zero) return null;
            
            if (!OpenProcessToken(processHandle, TOKEN_QUERY, out tokenHandle)) return null;
            
            using WindowsIdentity wi = new WindowsIdentity(tokenHandle);
            return wi.Name;
        }
        catch
        {
            return null;
        }
        finally
        {
            if (tokenHandle != IntPtr.Zero) CloseHandle(tokenHandle);
            if (processHandle != IntPtr.Zero) CloseHandle(processHandle);
        }
    }
}

方案2:用WTS API实现批量查询(性能最优)

如果需要极致性能,用WTSEnumerateProcesses API可以一次性拿到所有进程的所有者SID,不用逐个打开进程,280个进程的查询耗时通常在100ms以内:

using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Collections.Generic;

public class WtsProcessHelper
{
    [DllImport("wtsapi32.dll", SetLastError = true)]
    private static extern bool WTSEnumerateProcesses(IntPtr hServer, int Reserved, int Version, out IntPtr ppProcessInfo, out int pCount);

    [DllImport("wtsapi32.dll")]
    private static extern void WTSFreeMemory(IntPtr pMemory);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct WTS_PROCESS_INFO
    {
        public uint SessionId;
        public uint ProcessId;
        public string pProcessName;
        public IntPtr pUserSid;
    }

    public static List<List<string>> GetAllProcessWithUser()
    {
        var result = new List<List<string>>();
        IntPtr processInfoPtr = IntPtr.Zero;
        int processCount = 0;

        try
        {
            if (!WTSEnumerateProcesses(IntPtr.Zero, 0, 1, out processInfoPtr, out processCount))
                return result;

            long currentPtr = processInfoPtr.ToInt64();
            int structSize = Marshal.SizeOf(typeof(WTS_PROCESS_INFO));

            for (int i = 0; i < processCount; i++)
            {
                WTS_PROCESS_INFO processInfo = Marshal.PtrToStructure<WTS_PROCESS_INFO>((IntPtr)currentPtr);
                string userName = null;
                if (processInfo.pUserSid != IntPtr.Zero)
                {
                    try
                    {
                        SecurityIdentifier sid = new SecurityIdentifier(processInfo.pUserSid);
                        userName = sid.Translate(typeof(NTAccount)).ToString();
                    }
                    catch { }
                }
                result.Add(new List<string> { processInfo.pProcessName, userName });
                currentPtr += structSize;
            }
        }
        finally
        {
            if (processInfoPtr != IntPtr.Zero)
                WTSFreeMemory(processInfoPtr);
        }
        return result;
    }
}

内容的提问来源于stack exchange,提问作者Tomalak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.13 12:33:01