【问题标题】:How to use PSAPI to get process list in C#?如何使用 PSAPI 在 C# 中获取进程列表?
【发布时间】:2023-03-05 01:48:01
【问题描述】:

我正在尝试获取进程 ID 和文件名的列表,但它给了我很多问题...

这是控制台输出:

success=True
bytesCopied=344
Name '<unknown>' PID '0'
Name '<unknown>' PID '4'
Name '<unknown>' PID '308'
Name '<unknown>' PID '440'
Name '<unknown>' PID '488'
Name '<unknown>' PID '512'
Name '<unknown>' PID '548'
Name '<unknown>' PID '572'
Name '<unknown>' PID '580'
Name '<unknown>' PID '644'
Name '<unknown>' PID '732'
Name '<unknown>' PID '792'
Name '<unknown>' PID '816'
Name '<unknown>' PID '860'
Name '<unknown>' PID '940'
Name '<unknown>' PID '992'
Name '<unknown>' PID '264'
Name '<unknown>' PID '1160'
Name '<unknown>' PID '1220'
Name '<unknown>' PID '1292'
Name '<unknown>' PID '1424'
Name '<unknown>' PID '1452'
Name '<unknown>' PID '1556'
Name '<unknown>' PID '1596'
Name '<unknown>' PID '2044'
Name '<unknown>' PID '1504'
Name '<unknown>' PID '1132'
Name '<unknown>' PID '1912'
Name '<unknown>' PID '1972'
Name '<unknown>' PID '2084'
Name '<unknown>' PID '2124'
Name '<unknown>' PID '2560'
Name '<unknown>' PID '2796'
Name '<unknown>' PID '2808'
Name '<unknown>' PID '3000'
Name '<unknown>' PID '2116'
Name 'DTAgent.exe' PID '2228'
Name 'ISUSPM.exe' PID '2644'
Name 'DTShellHlp.exe' PID '2652'
Name 'Dropbox.exe' PID '2664'
Name 'acrotray.exe' PID '3124'
Name 'RIMBBLaunchAgent.exe' PID '3180'
Name 'vmware-tray.exe' PID '3188'
Name '<unknown>' PID '3520'
Name '<unknown>' PID '3592'
Name '<unknown>' PID '3780'
Name '<unknown>' PID '3964'
Name 'TrueCrypt.exe' PID '3392'
Name '<unknown>' PID '3800'
Name '<unknown>' PID '4680'
Name '<unknown>' PID '680'
Name 'FileZilla server.exe' PID '2240'
Name 'mysqld.exe' PID '4160'
Name 'uTorrent.exe' PID '7796'
Name 'svchost.exe' PID '44412'
Name '<unknown>' PID '10624'
Name '<unknown>' PID '35644'
Name 'httpd.exe' PID '44260'
Name 'httpd.exe' PID '40556'
Name '<unknown>' PID '11488'
Name 'RIMDeviceManager.exe' PID '42832'
Name 'BbDevMgr.exe' PID '45108'
Name '<unknown>' PID '31208'
Name '<unknown>' PID '34812'
Name 'trillian.exe' PID '61420'
Name 'Microsoft Visual C++ 6.0.exe' PID '52212'
Name '<unknown>' PID '33752'
Name '<unknown>' PID '47564'
Name '<unknown>' PID '39952'
Name 'mysqld-opt.exe' PID '61884'
Name 'winamp.exe' PID '42008'
Name 'opera.exe' PID '4560'
Name 'PowerGREP.exe' PID '12860'
Name 'PowerGREP.exe' PID '13280'
Name 'GOLD Parser Builder.exe' PID '32368'
Name 'Server.exe' PID '16396'
Name '<unknown>' PID '50976'
Name 'xampp-control.exe' PID '56084'
Name 'notepad++.exe' PID '27932'
Name 'WinAMP2.exe' PID '23336'
Name '<unknown>' PID '8044'
Name 'devenv.exe' PID '61172'
Name '<unknown>' PID '14780'
Name '<unknown>' PID '52180'
Name 'Sputnik.vshost.exe' PID '3672'
Name '<unknown>' PID '39480'

这里是我想要的控制台输出......(这是使用我的 C++ 代码)

Name '[System Process]' PID '0'
Name 'System' PID '4'
Name 'smss.exe' PID '308'
Name 'csrss.exe' PID '440'
Name 'wininit.exe' PID '488'
Name 'csrss.exe' PID '512'
Name 'services.exe' PID '548'
Name 'lsass.exe' PID '572'
Name 'lsm.exe' PID '580'
Name 'winlogon.exe' PID '644'
Name 'svchost.exe' PID '732'
Name 'nvvsvc.exe' PID '792'
Name 'nvSCPAPISvr.exe' PID '816'
Name 'svchost.exe' PID '860'
Name 'svchost.exe' PID '940'
Name 'svchost.exe' PID '992'
Name 'svchost.exe' PID '264'
Name 'svchost.exe' PID '1160'
Name 'qmserv.exe' PID '1220'
Name 'svchost.exe' PID '1292'
Name 'spoolsv.exe' PID '1424'
Name 'svchost.exe' PID '1452'
Name 'mDNSResponder.exe' PID '1556'
Name 'svchost.exe' PID '1596'
Name 'svchost.exe' PID '2044'
Name 'vmware-usbarbitrator64.exe' PID '1504'
Name 'vmnat.exe' PID '1132'
Name 'vmware-authd.exe' PID '1912'
Name 'vmnetdhcp.exe' PID '1972'
Name 'vmware-hostd.exe' PID '2084'
Name 'WmiPrvSE.exe' PID '2124'
Name 'svchost.exe' PID '2560'
Name 'nvxdsync.exe' PID '2796'
Name 'nvvsvc.exe' PID '2808'
Name 'taskhost.exe' PID '3000'
Name 'dwm.exe' PID '2116'
Name 'DTAgent.exe' PID '2228'
Name 'ISUSPM.exe' PID '2644'
Name 'DTShellHlp.exe' PID '2652'
Name 'Dropbox.exe' PID '2664'
Name 'acrotray.exe' PID '3124'
Name 'RIMBBLaunchAgent.exe' PID '3180'
Name 'vmware-tray.exe' PID '3188'
Name 'nvtray.exe' PID '3520'
Name 'SearchIndexer.exe' PID '3592'
Name 'FNPLicensingService.exe' PID '3780'
Name 'wmpnetwk.exe' PID '3964'
Name 'TrueCrypt.exe' PID '3392'
Name 'svchost.exe' PID '3800'
Name 'daemonu.exe' PID '4680'
Name 'svchost.exe' PID '680'
Name 'FileZilla Server.exe' PID '2240'
Name 'mysqld.exe' PID '4160'
Name 'uTorrent.exe' PID '7796'
Name 'svchost.exe' PID '44412'
Name 'iexplore.exe' PID '10624'
Name 'iexplore.exe' PID '35644'
Name 'httpd.exe' PID '44260'
Name 'httpd.exe' PID '40556'
Name 'svchost.exe' PID '11488'
Name 'RIMDeviceManager.exe' PID '42832'
Name 'BbDevMgr.exe' PID '45108'
Name 'TeamViewer_Service.exe' PID '31208'
Name 'taskhost.exe' PID '34812'
Name 'trillian.exe' PID '61420'
Name 'Microsoft Visual C++ 6.0.exe' PID '52212'
Name 'explorer.exe' PID '33752'
Name 'VisualSVNServer.exe' PID '47564'
Name 'VisualSVNServer.exe' PID '39952'
Name 'mysqld-opt.exe' PID '61884'
Name 'winamp.exe' PID '42008'
Name 'opera.exe' PID '4560'
Name 'PowerGREP.exe' PID '12860'
Name 'PowerGREP.exe' PID '13280'
Name 'GOLD Parser Builder.exe' PID '32368'
Name 'Server.exe' PID '16396'
Name 'conhost.exe' PID '50976'
Name 'xampp-control.exe' PID '56084'
Name 'notepad++.exe' PID '27932'
Name 'WinAMP2.exe' PID '23336'
Name 'CryptoObfuscator.exe' PID '8044'
Name 'devenv.exe' PID '61172'
Name 'hh.exe' PID '14780'
Name 'Sputnik.vshost.exe' PID '62828'
Name 'conhost.exe' PID '37164'

正如您所看到的,很多 PIDS 实际上都缺少文件名,我无法弄清楚为什么在大多数情况下我还从 C++ 复制了不起作用的工作代码,或者我也厌倦了 pinvoke 上的代码并且没有这样做要么。

这是 c# 代码

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace testie
{
    public class EnumerateProcesses
    {
        #region APIS
        [DllImport("psapi")]
        private static extern bool EnumProcesses([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U4)] [In][Out] IntPtr[] processIds,UInt32 arraySizeBytes,[MarshalAs(UnmanagedType.U4)] out UInt32 bytesCopied);

        [DllImport("kernel32.dll")]
        static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, IntPtr dwProcessId);

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool CloseHandle(IntPtr hObject);

        [DllImport("psapi.dll")]
        static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, [In] [MarshalAs(UnmanagedType.U4)] int nSize);

        [DllImport("psapi.dll", SetLastError = true)]
        public static extern bool EnumProcessModules(IntPtr hProcess,
        [Out] IntPtr lphModule,
        uint cb,
        [MarshalAs(UnmanagedType.U4)] out uint lpcbNeeded);

        [DllImport("psapi.dll")]
        static extern uint GetModuleBaseName(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, [In] [MarshalAs(UnmanagedType.U4)] int nSize);
        #endregion
        #region ENUMS

        [Flags]
        enum ProcessAccessFlags : uint
        {
            All = 0x001F0FFF,
            Terminate = 0x00000001,
            CreateThread = 0x00000002,
            VMOperation = 0x00000008,
            VMRead = 0x00000010,
            VMWrite = 0x00000020,
            DupHandle = 0x00000040,
            SetInformation = 0x00000200,
            QueryInformation = 0x00000400,
            Synchronize = 0x00100000
        }
        #endregion

        static string PrintProcessName(IntPtr processID)
        {
            string sName = "";
            bool bFound = false;
            IntPtr hProcess = OpenProcess(ProcessAccessFlags.QueryInformation | ProcessAccessFlags.VMRead, false, processID);
            if (hProcess != IntPtr.Zero)
            {
                StringBuilder szProcessName = new StringBuilder(260);
                IntPtr hMod = IntPtr.Zero;
                uint cbNeeded = 0;
                EnumProcessModules(hProcess, hMod, (uint)Marshal.SizeOf(typeof(IntPtr)), out cbNeeded);
                if (GetModuleBaseName(hProcess, hMod, szProcessName, szProcessName.Capacity) > 0)
                {
                    sName = szProcessName.ToString();
                    bFound = true;
                }

                // Close the process handle
                CloseHandle(hProcess);
            }
            if (!bFound)
            {
                sName = "<unknown>";
            }
            return sName;
        }
        public static void Testy()
        {
            UInt32 arraySize = 9000;
            UInt32 arrayBytesSize = arraySize * sizeof(UInt32);
            IntPtr[] processIds = new IntPtr[arraySize];
            UInt32 bytesCopied;

            bool success = EnumProcesses(processIds, arrayBytesSize, out bytesCopied);

            Console.WriteLine("success={0}", success);
            Console.WriteLine("bytesCopied={0}", bytesCopied);

            if (!success)
            {
                Console.WriteLine("Boo!");
                return;
            }
            if (0 == bytesCopied)
            {
                Console.WriteLine("Nobody home!");
                return;
            }

            UInt32 numIdsCopied = bytesCopied >> 2; ;

            if (0 != (bytesCopied & 3))
            {
                UInt32 partialDwordBytes = bytesCopied & 3;

                Console.WriteLine("EnumProcesses copied {0} and {1}/4th DWORDS...  Please ask it for the other {2}/4th DWORD",
                    numIdsCopied, partialDwordBytes, 4 - partialDwordBytes);
                return;
            }

            for (UInt32 index = 0; index < numIdsCopied; index++)
            {
                string sName = PrintProcessName(processIds[index]);
                IntPtr PID = processIds[index];
                Console.WriteLine("Name '" + sName + "' PID '" + PID + "'");
            }
        }
    }
}

正如您通过调用 Testy 函数所看到的,它会列出系统上的所有进程,但无法获取所有进程的名称......有人有解决方案吗?谢谢:)

为了记录,我还尝试将模块作为数组获取,这也产生完全相同的结果。

【问题讨论】:

  • 哦,所有带有 的进程都在 OpenProcess 上返回 0 的 hProcess...也许这有助于缩小范围? -- 另外记录一下,我知道 C# 中的 Process.List,但是我不能让它在文件名上给出 .exe 等,并且没有完整的文件名它对我没用.....
  • 我猜想在提升为管理员的情况下运行您的代码,您可能会看到该过程,但无权打开它们或询问他们的姓名。
  • 好主意 VirtualBlackFox 但是我已经尝试过了...没有效果 C++ 程序也不需要管理员权限才能工作,我的代码几乎是 C++ 的 1:1 副本,所以我真的不是确定如何处理它,哈哈,一定有办法让它工作;/

标签: c# pinvoke


【解决方案1】:

感谢大家的尝试,但是我决定使用“CreateToolhelp32Snapshot”,它似乎可以在 32 位和 64 位模式下与我的应用程序一起使用,并且两者都准确,不需要管理员权限即可使用,而且速度很快,而且它提供了文件名和 pid。

所以我想它会做的。

【讨论】:

    【解决方案2】:

    您可以尝试使用 System.Diagnostics 命名空间

        foreach (Process theProcess in Process.GetProcesses())
        {
            StringBuilder sb = new StringBuilder();
            try
            {
                sb.AppendLine(theProcess.Modules[0].FileName);
            }
            catch { }
            Console.WriteLine("Process: {0}  ID:  {1}", sb, theProcess.Id);
        }
    

    这不会显示系统或空闲进程可执行文件名称,因为它们不加载模块。

    http://msdn.microsoft.com/en-us/library/system.diagnostics.process.modules.aspx

    【讨论】:

    • 可以,但是 try catch 非常非常慢,需要大约 10 秒才能完成该功能,有什么想法可以解决这个问题吗?
    【解决方案3】:

    使用 System.Diagnostics,我试过这个:

    foreach(Process theprocess in Process.GetProcesses())
    {
                Console.WriteLine("Process: {0} ID: {1}", theprocess.ProcessName, theprocess.Id);
    }
    

    得到了这个结果:

    进程:AgentMon ID:2948
    流程:OUTLOOK ID:3144
    进程:ctfmon ID:3136
    ...

    现在,我是 Win XP 机器上的管理员...YMMV,就像它一样。

    我知道您曾声明要使用 PSAPI,但您是否有某些理由必须使用它,而不仅仅是常规托管 C#?

    【讨论】:

    • 是的 "foreach(Process theprocess in Process.GetProcesses())" 没有告诉你文件的 .exe 或 .bat .dat whateva 名称,它没有给你它的目录,我需要带有 PID 的完整路径名。我已经在上面说过这种方法对我不起作用,因为它不会产生文件名和目录。
    【解决方案4】:
    1. 如果你想要完整的路径,你真的应该尝试使用 System.Diagnostics 命名空间:

      process.MainModule.FileName
      
    2. 要让它在其他用户进程上工作,您需要成为管理员(真正的管理员,因此没有 UAC 或 UAC 提升),因为您需要启用 SeDebugPrivilege 的权限。来自 C#:

      try { Process.EnterDebugMode(); } catch(Win32Exception) { /* Not admin */ }
      

      请参阅 MSDN 上的 the documentation。但似乎至少在 C#4.0 上,只要您有权这样做,它现在就会自动完成。

    3. EnumProcessModules 不会为您提供来自 32 位 1 的 64 位进程或来自 64 位进程的 32 位进程。为此,您需要从 64 位进程调用 EnumProcessModulesEx 在每个架构中创建 2 个进程。 (请参阅 SO 上的 How to enum modules in a 64bit process from a 32bit WOW process
      System.Diagnostics 已经为您做到了
    4. 无论如何GetModuleBaseName 不会给你程序的完整路径它的GetModuleFileName(挑剔)

    还有另一种使用QueryFullProcessImageName(自Vista 起可用)的方法,它可以在没有OpenProcess 权限的情况下工作,因此无需成为管理员。

    【讨论】:

    • 这很好,但我如何在没有管理员权限的情况下获取模块名称?
    • 你不能为其他用户进程这是一项安全功能,设法绕过它意味着你破坏了 Windows 安全系统,并且无论如何都会在下一个微软补丁中修复......
    • 查看例如 Win7 上的任务管理器,其中仅列出当前用户进程并显示所有其他有一个受 UAC 保护的按钮(这不会在默认中等安全性上生成提示,但在高安全性设置中生成一个)
    • 好吧,如果不可能获得所有进程,我如何获得那些我能够看到的进程?例如,目前我必须使用 try/catch,否则它会在获取模块名称时崩溃(没有足够的权限)但是 try catch 正在大大降低代码速度有没有办法检查我是否能够获取模块姓名?
    • 第三点:System.Diagnostics 没有这样做。如果您有 64 位进程,它将为您提供进程模块、ntdll 和 3 个 wow64 模块。甚至 Powershell 都不会使用 Get-Process 列出 32 位进程的模块,这是非常错误的。
    【解决方案5】:

    也可能是......在玩具调用 EnumProcesses 并将您的列表构建到数组中之后,当您调用 PrintProcesses 时,机器上的真实(即原始)进程列表已经改变并且您的数组索引(进入您认为仍然是“该”列表的内容)已更改(基于您的数组索引)。量子力学浮现在脑海中——当你观察到它时,它已经改变了。只是一个想法-

    【讨论】:

      猜你喜欢
      • 2020-08-15
      • 2011-07-26
      • 2012-10-23
      • 1970-01-01
      • 2020-03-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多