Win32程序在64位Windows下运行的问题:如何正确枚举系统进程? 我的程序需要枚举Windows中当前正在运行的进程,我是用CreateToolhelp32Snapshot实现的,在32位windows下运行一切正常。但当我在64位windows下测试程序的时候,发现该功能失效,枚举时只发现一个进程,就是我自己那个正在运行的程序。求教高手:如何在win32程序中,枚举64位系统下的进程? 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 由于没有VISTA所以无法测试,不过CreateToolhelp32Snapshot中带32可能真是无法处理64位的进程数据,那就使用以下二种方法吧:第一种方法也很常见,通过MSDN就可以找到例子代码,它是通过Psapi.dll提供的API函数EnumProcesses和EnumProcessModules来实现。有一点要说明的是,Visual Studio提供的SDK包里没有提供相应的Psapi.h和与之相对应的导入库,笔者当时就很纳闷,MSDN里的例子居然编译不通,后来才发现,编译时根本找不到“psapi.h”。呵呵,还好,MSDN至少告诉我们他们都包含Psapi.dll里,用VC自带的Depend工具一查,果然。这样就好办了,我们可以自己找到这些函数入口地址。小知识:C也好C++也好,一般的函数名本质上都是一个地址,在Win32的API里,它是指向函数所在Dll模块里函数实现的入口地址。下面是第二种方法的实现过程:首先,先把Psapi.dll里要用到函数都定义好,方便后面显示调用。//在psaipi.dll中的函数EnumProcesses用来枚举进程 typedef BOOL (_stdcall *ENUMPROCESSES)( //注意这里要指明调用约定为-stdcall DWORD* pProcessIds, //指向进程ID数组链 DWORD cb, //ID数组的大小,用字节计数 DWORD* pBytesReturned); //返回的字节//在psapi.dll中的函数EnumProcessModules用来枚举进程模块typedef BOOL (_stdcall *ENUMPROCESSMODULES)( HANDLE hProcess, //进程句柄 HMODULE* lphModule, //指向模块句柄数组链 DWORD cb, //模块句柄数组大小,字节计数 LPDWORD lpcbNeeded); //存储所有模块句柄所需的字节数//在psapi.dll中的函数GetModuleFileNameEx获得进程模块名typedef DWORD (_stdcall *GETMODULEFILENAMEEX)( HANDLE hProcess, //进程句柄 HMODULE hModule, //进程句柄 LPTSTR lpFilename, //存放模块全路径名 DWORD nSize //lpFilename缓冲区大小,字符计算);好,通过每个定义的函数前的注释你可以清楚看到这些原函数名,然后当然要加载包含这些函数模块Psapi.dll啦: hPsDll = LoadLibrary("PSAPI.DLL");接着,通过Dll入口,我们查找每个函数地址:pEnumProcesses = (ENUMPROCESSES)GetProcAddress(hPsDll, "EnumProcesses");pEnumProcessModules =(ENUMPROCESSMODULES)GetProcAddress(hPsDll, "EnumProcessModules");pGetModuleFileNameEx = (GETMODULEFILENAMEEX)GetProcAddress(hPsDll, "GetModuleFileNameExA");注意第三个函数名GetModuleFileNameExA,在Dll里有以A和W结尾区分函数,A指采用的是ANSI字符串方式,W则是UNICODE方式。于是,我们可以用下面的语句枚举进程:pEnumProcesses(processid, sizeof(processid), &needed); processcount=needed/sizeof(DWORD); for (i=0;i<processcount;i++) { //打开进程 hProcess=OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,false, processid[i]); if (hProcess) { pEnumProcessModules(hProcess, &hModule, sizeof(hModule), &needed); pGetModuleFileNameEx(hProcess, hModule, path, sizeof(path)); GetShortPathName(path,path,256); itoa(processid[i],temp,10); printf("%s --- %s\n",path,temp); } else printf("Failed!!!\n"); } 当然,在Google上一搜索,可以找到不少提供的Psapi.h头文件和对应的Psapi.lib库,这样,你就可以更方便用这种方法了。 另一种方法:这是一种最少人用的方法,这种方法利用了Native Api的NtQuerySystemInformation函数来实现。同样没有该函数的导入库,也要自己定义原形。整个实现不难,可是有点烦,下面我们来看看这个方法的实现吧。先是自定义函数原形:typedef NTSTATUS (__stdcall *PZWQUERYSYSTEMINFORMATION) (IN SYSTEM_INFORMATION_CLASS SystemInformationClass, IN OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength);然后,还是和前面一样,要找到ZwQuerySystemInformation在Ntdll.dll模块里的入口地址:hModule = GetModuleHandle((LPCTSTR)"ntdll.dll");pZwQuerySystemInformation =(PZWQUERYSYSTEMINFORMATION)GetProcAddress(hModule, (LPCTSTR)"ZwQuerySystemInformation");获得指向进程信息数组链的第一条进程信息:pSystemProcessInformation = (SYSTEM_PROCESS_INFORMATION *)pvProcessList;和方法一方法二一样,在获得第一条进程信息后,开始循环遍历,列出其余的进程:while (TRUE) { if (pSystemProcessInformation->NextEntryDelta == 0) //如果是最后一条,则终止循环。 break; pSystemProcessInformation=(SYSTEM_PROCESS_INFORMATION*)((PCHAR)pSystemProcessInformation+ pSystemProcessInformation->NextEntryDelta); pProcessName = (char *)malloc(pSystemProcessInformation->ProcessName.Length + 2); }特别注意的是,这里SYSTEM_PROCESS_INFORMATION结构里进程名的类型为UNICODE_STRING, 而UNICODE_STRING里的成员Buffer定义的是Unicode类型。typedef struct _SYSTEM_PROCESS_INFORMATION { DWORD NextEntryDelta; DWORD dThreadCount; DWORD dReserved01; DWORD dReserved02; DWORD dReserved03; DWORD dReserved04; DWORD dReserved05; DWORD dReserved06; FILETIME ftCreateTime; /* relative to 01-01-1601 */ FILETIME ftUserTime; /* 100 nsec units */ FILETIME ftKernelTime; /* 100 nsec units */ UNICODE_STRING ProcessName; //这就是进程名 DWORD BasePriority; DWORD dUniqueProcessId; //进程ID DWORD dParentProcessID; DWORD dHandleCount; DWORD dReserved07; DWORD dReserved08; DWORD VmCounters; DWORD dCommitCharge; PVOID ThreadInfos[1]; } SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; //注意,这里为Unicode类型} UNICODE_STRING, *PUNICODE_STRING;所以,最后我们要调用WideCharToMultiByte()来还原成ANSI字符串以便输出:WideCharToMultiByte(CP_ACP, 0, pSystemProcessInformation->ProcessName.Buffer, pSystemProcessInformation->ProcessName.Length + 1, pProcessName, pSystemProcessInformation->ProcessName.Length + 1, NULL, NULL);具体每个参数可以参照MSDN. 以上方法都没有指定32位,所以在64位上应该可以被兼容运行的,其实还有一种方法:就是通过WMI的COM接口函数CoCreateInstance(),ConnectServer(),ExecQuery()等函数来实现.总之方法还是不少的.楼主再试试看吧. 使用PSAPI遍历,编译程序为64位 我的要求是编译程序为32位,不是64位!已经试过psapi,不能符合我的要求! 你32位程序在64系统上是兼容模式下运行,当然不可以获取的系统级的东东了。具体可参考procexp的实现,它启动先判断操作系统是否为64位,如果是它会从资源里释放出一个64位版本的程序并运行起来。 在高版本的VS(2008,2010)中,EnumProcess其实是可用的,返回的PID也都正确。问题出在枚举具体进程名称时,需要先OpenProcess,这个函数无法获取64-bit的进程的名称,返回的进程句柄都是0x00000000,从而造成整个枚举失败。 做爸爸了,散分咯 什么是内置类型函数样式? 关于串口编程的问题 vc find in files 功能有问题! 事件中的问题:奇怪了!!! 在线程中向文档类发送消息,如何附加参数? 谁知道清零的功能怎么实现? ◇◆◇牛人、专家快来:怎样实现对一个视图的ALPHA(淡入淡出)效果◇◆◇ vc如何将access中的数据导出到word? 我的程序处理大量文本,在运行一段时间后就几乎消耗120M,(无泄露,确实有大量历史文本), Release配置和DEBUG配置是什么??? 关于CWnd::Detach()的用法
第一种方法也很常见,通过MSDN就可以找到例子代码,它是通过Psapi.dll提供的API函数EnumProcesses和EnumProcessModules来实现。有一点要说明的是,Visual Studio提供的SDK包里没有提供相应的Psapi.h和与之相对应的导入库,笔者当时就很纳闷,MSDN里的例子居然编译不通,后来才发现,编译时根本找不到“psapi.h”。呵呵,还好,MSDN至少告诉我们他们都包含Psapi.dll里,用VC自带的Depend工具一查,果然。这样就好办了,我们可以自己找到这些函数入口地址。小知识:C也好C++也好,一般的函数名本质上都是一个地址,在Win32的API里,它是指向函数所在Dll模块里函数实现的入口地址。下面是第二种方法的实现过程:首先,先把Psapi.dll里要用到函数都定义好,方便后面显示调用。//在psaipi.dll中的函数EnumProcesses用来枚举进程
typedef BOOL (_stdcall *ENUMPROCESSES)( //注意这里要指明调用约定为-stdcall
DWORD* pProcessIds, //指向进程ID数组链
DWORD cb, //ID数组的大小,用字节计数
DWORD* pBytesReturned); //返回的字节
//在psapi.dll中的函数EnumProcessModules用来枚举进程模块
typedef BOOL (_stdcall *ENUMPROCESSMODULES)(
HANDLE hProcess, //进程句柄
HMODULE* lphModule, //指向模块句柄数组链
DWORD cb, //模块句柄数组大小,字节计数
LPDWORD lpcbNeeded); //存储所有模块句柄所需的字节数
//在psapi.dll中的函数GetModuleFileNameEx获得进程模块名
typedef DWORD (_stdcall *GETMODULEFILENAMEEX)(
HANDLE hProcess, //进程句柄
HMODULE hModule, //进程句柄
LPTSTR lpFilename, //存放模块全路径名
DWORD nSize //lpFilename缓冲区大小,字符计算
);好,通过每个定义的函数前的注释你可以清楚看到这些原函数名,然后当然要加载包含这些函数模块Psapi.dll啦:
hPsDll = LoadLibrary("PSAPI.DLL");
接着,通过Dll入口,我们查找每个函数地址:pEnumProcesses =
(ENUMPROCESSES)GetProcAddress(hPsDll, "EnumProcesses");
pEnumProcessModules =
(ENUMPROCESSMODULES)GetProcAddress(hPsDll, "EnumProcessModules");
pGetModuleFileNameEx =
(GETMODULEFILENAMEEX)GetProcAddress(hPsDll, "GetModuleFileNameExA");
注意第三个函数名GetModuleFileNameExA,在Dll里有以A和W结尾区分函数,A指采用的是ANSI字符串方式,W则是UNICODE方式。于是,我们可以用下面的语句枚举进程:
pEnumProcesses(processid, sizeof(processid), &needed);
processcount=needed/sizeof(DWORD); for (i=0;i<processcount;i++)
{
//打开进程
hProcess=OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,
false, processid[i]);
if (hProcess)
{
pEnumProcessModules(hProcess, &hModule, sizeof(hModule), &needed);
pGetModuleFileNameEx(hProcess, hModule, path, sizeof(path));
GetShortPathName(path,path,256);
itoa(processid[i],temp,10);
printf("%s --- %s\n",path,temp);
}
else
printf("Failed!!!\n");
}
当然,在Google上一搜索,可以找到不少提供的Psapi.h头文件和对应的Psapi.lib库,这样,你就可以更方便用这种方法了。
先是自定义函数原形:typedef NTSTATUS (__stdcall *PZWQUERYSYSTEMINFORMATION)
(IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength);
然后,还是和前面一样,要找到ZwQuerySystemInformation在Ntdll.dll模块里的入口地址:hModule = GetModuleHandle((LPCTSTR)"ntdll.dll");
pZwQuerySystemInformation =(PZWQUERYSYSTEMINFORMATION)
GetProcAddress(hModule, (LPCTSTR)"ZwQuerySystemInformation");
获得指向进程信息数组链的第一条进程信息:
pSystemProcessInformation = (SYSTEM_PROCESS_INFORMATION *)pvProcessList;和方法一方法二一样,在获得第一条进程信息后,开始循环遍历,列出其余的进程:
while (TRUE)
{
if (pSystemProcessInformation->NextEntryDelta == 0) //如果是最后一条,则终止循环。
break; pSystemProcessInformation=(SYSTEM_PROCESS_INFORMATION*)((PCHAR)pSystemProcessInformation+ pSystemProcessInformation->NextEntryDelta);
pProcessName = (char *)malloc(pSystemProcessInformation->ProcessName.Length + 2);
}特别注意的是,这里SYSTEM_PROCESS_INFORMATION结构里进程名的类型为UNICODE_STRING, 而UNICODE_STRING里的成员Buffer定义的是Unicode类型。
typedef struct _SYSTEM_PROCESS_INFORMATION
{
DWORD NextEntryDelta;
DWORD dThreadCount;
DWORD dReserved01;
DWORD dReserved02;
DWORD dReserved03;
DWORD dReserved04;
DWORD dReserved05;
DWORD dReserved06;
FILETIME ftCreateTime; /* relative to 01-01-1601 */
FILETIME ftUserTime; /* 100 nsec units */
FILETIME ftKernelTime; /* 100 nsec units */
UNICODE_STRING ProcessName; //这就是进程名
DWORD BasePriority;
DWORD dUniqueProcessId; //进程ID
DWORD dParentProcessID;
DWORD dHandleCount;
DWORD dReserved07;
DWORD dReserved08;
DWORD VmCounters;
DWORD dCommitCharge;
PVOID ThreadInfos[1];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer; //注意,这里为Unicode类型
} UNICODE_STRING, *PUNICODE_STRING;所以,最后我们要调用WideCharToMultiByte()来还原成ANSI字符串以便输出:
WideCharToMultiByte(CP_ACP,
0,
pSystemProcessInformation->ProcessName.Buffer,
pSystemProcessInformation->ProcessName.Length + 1,
pProcessName,
pSystemProcessInformation->ProcessName.Length + 1,
NULL,
NULL);
具体每个参数可以参照MSDN.
就是通过WMI的COM接口函数CoCreateInstance(),ConnectServer(),ExecQuery()等函数来实现.总之方法还是不少的.楼主再试试看吧.
你32位程序在64系统上是兼容模式下运行,当然不可以获取的系统级的东东了。具体可参考procexp的实现,它启动先判断操作系统是否为64位,如果是它会从资源里释放出一个64位版本的程序并运行起来。