Win32程序在64位Windows下运行的问题:如何正确枚举系统进程?

maohbao 2009-05-22 12:20:38
我的程序需要枚举Windows中当前正在运行的进程,我是用CreateToolhelp32Snapshot实现的,在32位windows下运行一切正常。但当我在64位windows下测试程序的时候,发现该功能失效,枚举时只发现一个进程,就是我自己那个正在运行的程序。

求教高手:如何在win32程序中,枚举64位系统下的进程?
...全文
1578 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
souledge1983 2010-10-15
  • 打赏
  • 举报
回复
在高版本的VS(2008,2010)中,EnumProcess其实是可用的,返回的PID也都正确。问题出在枚举具体进程名称时,需要先OpenProcess,这个函数无法获取64-bit的进程的名称,返回的进程句柄都是0x00000000,从而造成整个枚举失败。
biweilun 2009-05-23
  • 打赏
  • 举报
回复
32位的程序操作64位本来就不行,得不到是肯定的。
WaistCoat18 2009-05-23
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 maohbao 的回复:]
我的要求是编译程序为32位,不是64位!已经试过psapi,不能符合我的要求!
[/Quote]

你32位程序在64系统上是兼容模式下运行,当然不可以获取的系统级的东东了。

具体可参考procexp的实现,它启动先判断操作系统是否为64位,如果是它会从资源里释放出一个64位版本的程序并运行起来。
mmxqq 2009-05-22
  • 打赏
  • 举报
回复

学习
TianChong 2009-05-22
  • 打赏
  • 举报
回复
以上方法都没有指定32位,所以在64位上应该可以被兼容运行的,其实还有一种方法:
就是通过WMI的COM接口函数CoCreateInstance(),ConnectServer(),ExecQuery()等函数来实现.总之方法还是不少的.楼主再试试看吧.
TianChong 2009-05-22
  • 打赏
  • 举报
回复
另一种方法:

这是一种最少人用的方法,这种方法利用了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.
TianChong 2009-05-22
  • 打赏
  • 举报
回复
由于没有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库,这样,你就可以更方便用这种方法了。
shuirh 2009-05-22
  • 打赏
  • 举报
回复
帮你顶起来一下,呵呵!
maohbao 2009-05-22
  • 打赏
  • 举报
回复
我的要求是编译程序为32位,不是64位!已经试过psapi,不能符合我的要求!
石头--- 2009-05-22
  • 打赏
  • 举报
回复
还没在64位系统下测试过...帮顶
WaistCoat18 2009-05-22
  • 打赏
  • 举报
回复
使用PSAPI遍历,编译程序为64位
maohbao 2009-05-22
  • 打赏
  • 举报
回复
都没有测试过啊....

16,548

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • AIGC Browser
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧