进程查看器






4.94/5 (99投票s)
列出了系统中正在运行的进程的详细信息、加载的驱动程序、加载的 DLL、每个 DLL 和进程的版本、进程时间、命令行、所有者、优先级、GDI 资源使用情况、权限、加载的符号、窗口层次结构、自动启动应用程序查找等。
下载 ProcViewer.zip - 195.9 KB
下载 Exe ProcessViewer.zip - 118.1 KB
选项对话框
权限管理器对话框
引言
一个实用程序,列出了系统中所有正在运行的进程以及它们加载的 DLL,还显示了加载 DLL 的路径、它们的加载地址、DLL 的基址、映像大小以及每个进程的信息。
此工具的功能
- 显示进程加载的所有 DLL
- 每个 DLL 中的符号,您可以点击其中任何一个
- 目录
- 名称
- 地址
- 大小
- 每个 DLL 的版本
- DLL 的描述
- 公司名称
- 如果启用了依赖关系查看,则使用依赖关系查看器查看每个 DLL
- 完整模块路径,即它从何处加载?
- 加载地址
- 入口点
- 映像大小
- 加载顺序
- 每个 DLL 中的符号,您可以点击其中任何一个
- 如果您点击主内核进程,将显示系统中加载的所有驱动程序
- 进程 ID
- 进程所有者
- 进程命令行
- 进程自动启动信息
- 应用程序类型,即应用程序是控制台、Windows 还是 MS-DOS 应用程序。
- 进程优先级
- GUI 资源使用情况统计(未来版本将扩展)
- 每个进程的大小
- 每个进程中的符号
- 窗口详细信息(如果有)
- 以正确的层次结构显示窗口,即父窗口的所有子窗口都包含在其节点下。
- 文本或标题
- HWND
- 窗口过程
- 图标(如果有)
- 光标
- 窗口类,例如工具栏的 Toolbarwindow32。
- 拥有该窗口的线程
- 窗口状态,即最大化、最小化、还原或隐藏
- 窗口边界
- 窗口 ID
- 父窗口 HWND
- 窗口类样式
- 窗口样式
- 所有子窗口将以层次结构显示
- 进程时间
- 创建时间
- 退出时间,即杀死一个进程然后您会看到此时间已更新
- 内核模式时间,使用了多少内核时间
- 用户模式时间
- 文件时间
- 创建时间
- 上次访问时间
- 上次写入时间
- IO 计数器
- 输入和输出计数器值
- 内存详细信息,页面错误计数等
- 进程版本,您还可以通过工具提示获得一些信息
- 进程的权限以及权限管理器
- 视图排序
- 布局切换
- 杀死一个进程并杀死该进程的所有实例
- 搜索进程或加载的 DLL/驱动程序
- 选项对话框用于关闭不需要的详细信息
- 您有一个关于框。 ;)
- 还有一个退出按钮。 ;))
使用代码
我使用了 Microsoft
提供的 PSAPI
函数来枚举进程加载的模块。
使用的 API 是:
EnumProcesses
-- 获取系统中所有正在运行的进程EnumProcessModules
-- 获取进程加载的所有模块/DLLGetModuleBaseName
-- 获取模块的基本名称...例如:mydll.dll 而不是 C:\AnyLongPath\mydll.dll。GetModuleFileNameEx
-- 获取模块的长路径GetModuleInformation
-- 检索有关模块的信息GetProcessMemoryInfo
-- 获取进程的内存详细信息。EnumDeviceDrivers
-- ID 为 4 (System) 的进程是加载驱动程序的进程,因此我们使用此函数来枚举这些设备驱动程序GetDeviceDriverBaseName
-- 获取设备驱动程序的基本名称GetDeviceDriverFileName
-- 获取设备驱动程序的完整路径GetProcessTimes
-- 获取每个进程花费的时间、启动时间、内核模式和用户模式时间。SearchPath
-- 搜索PATH
环境变量。文档详细描述了搜索过程。GetFileVersionInfo
-- 用于提取版本。GetFileVersionInfoSize
-- 版本大小VerQueryValue
-- 查询版本组件。有一个名为FileVersionInfo
的类为此目的而编写。查阅该类以获取有关这些版本函数及其使用方法的更多信息。
这是主函数...
void GetProcessDetails() { // Clear previous associated item data if any ClearItemData(); // Clear tree view m_ctvProcess.DeleteAllItems(); // Clear list view m_clvProcessDetails.DeleteAllItems(); #define MAX 10000 DWORD dwSize = 0; DWORD dwProcIdentifiers[MAX] = { 0 }; EnumProcesses( dwProcIdentifiers, sizeof( dwProcIdentifiers ), &dwSize ); if( !dwSize ) { return; } // Process count dwSize /= sizeof( DWORD ); HTREEITEM hItemRoot = m_ctvProcess.InsertItem( _T( "Process list" ), 60, 60 ); TCHAR szName[MAX_PATH] = { 0 }; for( DWORD dwIndex = 0; dwIndex < dwSize; ++ dwIndex ) { HANDLE hProcModule = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcIdentifiers[dwIndex] ); if( !hProcModule ) { continue; } DWORD dwModuleSize = 0; HMODULE hModules[MAX] = { 0 }; // Get all modules for a process EnumProcessModules( hProcModule, hModules, sizeof( hModules ), &dwModuleSize ); dwModuleSize /= sizeof( HMODULE ); if( !dwModuleSize ) { continue; } // Structure for storing process information PPROC_INFO_t pstProcInfo = new PROC_INFO_t; if( !pstProcInfo ) { return; } // Fill out process information pstProcInfo->dwProcId = dwProcIdentifiers[dwIndex]; GetModuleFileNameEx( hProcModule, hModules[0], szName, MAX_PATH ); pstProcInfo->csFullPath = szName; GetModuleBaseName( hProcModule, hModules[0], szName, MAX_PATH ); pstProcInfo->csBaseName = szName; // Get full path display status, if checked then we will display full path const BOOL bFullPathChecked = m_ToolBar.GetToolBarCtrl().IsButtonChecked( ID_OPTIONS_SHOWPATH ); CString csProc; csProc.Format( _T( "%s, PID: %lu" ), SCAST( LPCTSTR , ( bFullPathChecked ? pstProcInfo->csFullPath : pstProcInfo->csBaseName )), dwProcIdentifiers[dwIndex] ); HTREEITEM hItem = m_ctvProcess.InsertItem( csProc, 20, 20, hItemRoot ); m_ctvProcess.SetItemData( hItem, RCAST( DWORD, pstProcInfo )); // Process details PROCESS_MEMORY_COUNTERS& pmcProcMemCounter = pstProcInfo->stpmcMemCounters; GetProcessMemoryInfo( hProcModule, &pmcProcMemCounter, sizeof( pmcProcMemCounter )); // Insert process memory related values HTREEITEM hPInfoItem = m_ctvProcess.InsertItem( _T( "Details" ), 7, 7, hItem ); CString csTempStr; const int nIconIndex = 63; // Page fault count csTempStr.Format( _T( "Page fault count: %lu" ), pmcProcMemCounter.PageFaultCount ); m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem ); // Page file usage csTempStr.Format( _T( "Page file usage: %.02lf KB" ), pmcProcMemCounter.PagefileUsage/1024.0f ); m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem ); // Peak page file usage csTempStr.Format( _T( "Peak page file usage: %.02lf KB" ), pmcProcMemCounter.PeakPagefileUsage/1024.0f ); m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem ); // Peak working set size csTempStr.Format( _T( "Peak working set size: %.02lf KB" ), pmcProcMemCounter.PeakWorkingSetSize/1024.0f ); m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem ); // Quota non pages pool usage csTempStr.Format( _T( "Quota non paged pool size: %.02lf KB" ), pmcProcMemCounter.QuotaNonPagedPoolUsage/1024.0f ); m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem ); // Quota pages pool usage csTempStr.Format( _T( "Quota paged pool size: %.02lf KB" ), pmcProcMemCounter.QuotaPagedPoolUsage/1024.0f ); m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem ); // Quota peak non pages pool usage csTempStr.Format( _T( "Quota peak non paged pool size: %.02lf KB" ), pmcProcMemCounter.QuotaPeakNonPagedPoolUsage/1024.0f ); m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem ); // Quota peak pages pool usage csTempStr.Format( _T( "Quota peak paged pool size: %.02lf KB" ), pmcProcMemCounter.QuotaPeakPagedPoolUsage/1024.0f ); m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem ); // Working set size csTempStr.Format( _T( "Working set size: %.02lf KB" ), pmcProcMemCounter.WorkingSetSize/1024.0f ); m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem ); // Prepare module list PMODULEINFO_LIST_t* ppModuleInfo = &pstProcInfo->pstModuleInfoList; for( DWORD dwModIndex = 1; dwModIndex < dwModuleSize; ++ dwModIndex ) { // Allocate new module *ppModuleInfo = new MODULEINFO_LIST_t;; if( !*ppModuleInfo ) { continue; } // Get module related information GetModuleInformation( hProcModule, hModules[dwModIndex], &( *ppModuleInfo )->stmiModInfo, sizeof( MODULEINFO )); // Get full path of module GetModuleFileNameEx( hProcModule, hModules[dwModIndex], szName, MAX_PATH ); ( *ppModuleInfo )->csModuleFullPath = szName; // Get base name of module GetModuleBaseName( hProcModule, hModules[dwModIndex], szName, MAX_PATH ); ( *ppModuleInfo )->csModuleBaseName = szName; // Move forward ppModuleInfo = &( *ppModuleInfo )->pstNextModuleInfo; }// End for // Close process handle CloseHandle( hProcModule ); }// End for // Expand root node m_ctvProcess.Expand( hItemRoot, TVE_EXPAND ); }
PSAPI 函数用法
EnumProcesses
// A large array of DWORDs for storing process ids of processes
DWORD dwProcIds[2048];
DWORD dwProcRunning = 0;
// Get all running processes
EnumProcesses( dwProcIds, sizeof( dwProcIds ) /sizeof( dwProcIds[0] ),
&dwNeeded );
// To get the count of processes running use
dwProcRunning /= sizeof( DWORD );
EnumProcessModules
// Pass in a large array of modules just to be safe
HMODULE hModuleArray[2048];
DWORD dwModuleCount = 0;
// Open process to get it's handle, for some processes
// we may be denied access.
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE,
dwAnyProcessId );
// Get processes
EnumProcessModules( hProcess, hModuleArray,
sizeof( hModuleArray )/ sizeof( hModuleArray[0] ),
&dwModuleCount );
// Now get actual count of modules loaded
dwModuleCount /= sizeof( HMODULE );
返回时,数组中的第一个模块是进程的可执行文件。其余模块是实际加载的模块。
GetModuleBaseName
返回模块的基本名称,例如:如果模块路径是 C:\MyPath\Averylongpath\Basename.dll
,则此 API
将返回 Basename.dll
TCHAR szName[MAX_PATH] = { 0 }
GetModuleBaseName( hProcess, hModule, szName, MAX_PATH )
GetModuleFileNameEx
与上述函数非常相似,只是它返回所请求模块的完整路径。
GetModuleInformation 和 GetProcessMemoryInfo
这两个 API
易于使用。只需传入一个结构,它就会返回信息。
// Get module related information
MODULEINFO miModInfo = { 0 };
GetModuleInformation( hProcModule, hModule, &miModInfo, sizeof( MODULEINFO ));
// Get process related information
PROCESS_MEMORY_COUNTERS pmcProcMemCounter = { 0 };
GetProcessMemoryInfo( hProcModule,
&pmcProcMemCounter,
sizeof( pmcProcMemCounter ));
这些是我使用的 API
。主代码可以在 ProcessViewerDlg.cpp
和 ProcessViewerDlg.h
中找到
EnumDeviceDrivers
枚举由 System 进程加载的设备驱动程序。尽管进程名称显示为 System
,但其真实名称是 %systemroot%\System32\ntoskrnl.exe
。
const int nSize = 2048;
DWORD dwCount = 0;
LPVOID lpvImageBases[nSize] = { 0 };
// Get drivers
EnumDeviceDrivers( lpvImageBases, nSize, &dwCount );
// Get count of drivers loaded
dwCount /= sizeof( LPVOID );
请注意,数组中的第一个项目是进程的名称,其余是驱动程序。
GetDeviceDriverBaseName
提取设备驱动程序的基本名称。
TCHAR szName[MAX_PATH] = { 0 };
// Returns main process name, since we are accessing the first element of the
// array
GetDeviceDriverBaseName( lpvImageBases[0], szName, MAX_PATH );
GetDeviceDriverFileName
提取设备驱动程序的完整路径。不过,这对我的帮助不大。返回的大部分路径是 \Windows\System32\Some.sys 等或 \??\C:\..\Some.sys。所以我不得不使用 SearchPath
API 来找出这些设备驱动程序的确切位置,当然,我修改了 PATH
变量以迫使此 API 也搜索 Drivers 文件夹。
TCHAR szName[MAX_PATH] = { 0 };
// Returns full path of device driver loader process, since we are accessing
// first element of the array.
GetDeviceDriverFileName( lpvImageBases[0], szName, MAX_PATH );
GetProcessTimes
返回指定进程的时间信息。
HANDLE hProcess; // Handle returned by OpenProcess
// Prepare filetime structures
FILETIME ftStartTime = { 0 },
ftExitTime = { 0 },
ftKernelTime = { 0 },
ftUserTime = { 0 }
// Get time
GetProcessTimes( hProcess,
&ftStartTime,
&ftExitTime,
&ftKernelTime,
&ftUserTime );
您可以直接使用 FILETIME,
但应小心,除了开始时间和退出时间之外,其他时间可能无法按预期工作(请阅读文档)。您需要对内核时间和用户时间做一些额外的工作。
最好的方法是调用 FileTimeToSystemTime
或将其放入 COleDateTime
。DavidCrow 撰写了一篇关于此主题的精彩 文章。我建议您阅读它。
SearchPath
仔细阅读此函数的文档。在这里描述此 API 的工作原理是浪费时间,因为它已经有了很好的文档。
const int nAllocLength = MAX_PATH * 2;
TCHAR szPathBuffer[ nAllocLength ];
// Search for file
if( SearchPath( 0, _T( "Anyfile.anytype" ), 0<;/span>, nAllocLength, szPathBuffer, 0 ))
{
csFilePath_o = szPathBuffer;
return true;
}// End if
ProcessPriority
要更改进程的优先级,请调用 SetPriorityClass,要检索进程的优先级,请调用 GetPriorityClass。
六个优先级类如下...
- IDLE_PRIORITY_CLASS
- BELOW_NORMAL_PRIORITY_CLASS
- NORMAL_PRIORITY_CLASS
- ABOVE_NORMAL_PRIORITY_CLASS
- HIGH_PRIORITY_CLASS
- REALTIME_PRIORITY_CLASS
Usage: // For setting priority use VERIFY( SetPriorityClass( hProcess, ABOVE_NORMAL_PRIORITY_CLASS )); // For retrieving priority use VERIFY( GetPriorityClass( hProcess ) != 0 );
搜索
进程查看器支持搜索加载的模块。您可以搜索特定进程或 DLL。搜索不支持通配符搜索,只进行“StrStrI
”搜索。因此,如果您搜索“shell”,则会命中“shell32.dll”、“someshell.dll”、“myShelldll.dll”等。
按 Ctrl + F 进行搜索。
依赖关系查看
双击任何模块或进程即可查看其依赖关系。您应该在系统上安装了依赖关系查看器。只有在启用依赖关系查看时才会发生这种情况。工具栏按钮之一(第四个)就是为此目的。
您可以从 这里 下载依赖关系查看器。
完整路径
要查看加载进程的完整路径,只需单击工具栏上的绿色勾号按钮(第三个)即可启用完整路径。
刷新
要刷新,请单击工具栏上的第一个按钮或按 F5。
交换视图
要从垂直视图切换到水平视图,或反之亦然,请按 F6 或单击工具栏上的第二个按钮。
杀死进程
选择要杀死的特定进程。按 F8 或单击杀死按钮。当进程被杀死时,它不会从进程列表中移除,而是将进程名称设置为粗体。按 F5/刷新以从树中删除已杀死的进程。主要是为了保留该进程的快照。如果您有木马在运行,并且您想查看有关该木马的详细信息而不必运行木马,则很有帮助。
注意:不要杀死内核进程,我尝试过,结果整个系统崩溃了。 :)
选项
按 F7 显示选项对话框。您可以设置各种进程查看选项。选项将包含更多内容。
CSV 模块列表和符号列表
要保存进程的模块列表,请按 Ctrl + A 选择所有模块,然后按 Ctrl + C。需要记住的事情...
- 如果光标停在某个项目上,则该子项(或子项)将被复制到剪贴板。因此,要复制多个子项,请先选择所需的子项,然后将光标放在该子项列上以复制到剪贴板。
- 要复制所有内容,请将光标移出列表视图。
- 打开记事本并粘贴。
- 另存为任意文件名.csv。
- 使用 CSV 查看器(如 Excel)打开。
符号列表也是如此。
视图方面保存
将您的设置保存到 ini 文件。例如:列顺序、分隔窗格大小、主窗口大小。这使您可以根据自己的喜好更改列表视图的顺序。甚至列的大小也会被保存。
实用程序
有一个 FileVersionInfo
类和一个 DividerWnd
类可以重用。一个 WindowCollection
类用于枚举所有打开的窗口。
FileVersionInfo
从模块中提取版本信息。DividerWnd
是一个简单轻量级的分割器。
致谢
- 您在此应用程序的树状视图和列表视图中看到的位图来自 Dominik 的 Keepass 应用。
- 嘿 Ralph,感谢您提供的 评论。我已经实现了其中大部分,但重写(可重用性)正在进行中。再次感谢。
- 感谢 DavidCrow 撰写的关于 进程时间 的文章。
- 感谢 Todd C Wilson 撰写的关于 激进优化 的文章,我正在使用他的头文件(我已注释掉合并节的链接器命令)。
- 感谢 CodeProject。感谢所有通过发布精彩文章帮助过我的开发人员。如果没有他们,这一切都不会发生。 :)
历史
- 创建于 2007 年 5 月 8 日。
- 于 2007 年 5 月 14 日添加了“搜索”。
- 于 2007 年 5 月 16 日添加了应用程序描述。
- 更新了应用程序以显示加载的设备驱动程序、版本信息、大小、GUI 更改。
- 于 2007 年 5 月 28 日添加了进度条、与进程相关的图标显示、进程类型(控制台、Windows、MS-DOS)。
- 于 2007 年 5 月 31 日添加了排序、列重新排序和保存此顺序的支持。
- 于 2007 年 6 月 2 日添加了从正在运行的进程中提取 HWND。更改了工具栏格式。
- 于 2007 年 6 月 5 日添加了 HWND 样式列表、父列表。
- 于 2007 年 6 月 7 日使工具栏透明,启用窗口样式、ID 查看。
- 由于某些问题禁用了窗口样式查看,正在修复中。于 2007 年 6 月 8 日。
- 于 2007 年 6 月 9 日添加了选项对话框和各种不同的调整。
- 于 2007 年 6 月 10 日添加了杀死进程选项,修复了一些保存设置的问题。
- 于 2007 年 6 月 13 日添加了进程优先级和 IO 计数器。
- 现在正确显示窗口的子项。于 2007 年 6 月 18 日。
- 于 2007 年 6 月 25 日为进程的每个加载模块添加了符号列表。
- 于 2007 年 6 月 26 日添加了进程权限显示。
- 于 2007 年 10 月 14 日添加了进程所有者、进程命令行和总窗口计数显示。
- 于 2007 年 10 月 24 日添加了动态进程优先级更新支持。
- 于 2007 年 10 月 26 日向窗口详细信息添加了窗口图标、字符集、启用/禁用状态,并添加了进程优先级提升支持。
- 将根节点文本更改为机器名和用户名 ;)。于 2007 年 11 月 2 日。
- 于 2007 年 11 月 12 日找出进程是否已注册自动启动。
- 在修复了一些 bug 后重新启用了窗口样式查看。于 2007 年 11 月 27 日。
- 修复了 VS2005 中的编译错误(注意:在 VS2005 中您会遇到清单错误,作为修复,请在资源编辑器中删除清单资源)。
- 添加了杀死应用程序所有实例的支持。还为进程添加了“打开父文件夹”、“属性”支持。
- 添加了权限管理器,可帮助动态启用/禁用/删除权限(尽管需要微调)。于 2008 年 1 月 1 日(新年快乐!)。
- 针对速度进行了一些优化!