转自: http://blog.csdn.net/anycell/archive/2008/12/12/3505864.aspx
今天客户打来电话说我们公司的服务器程序在新装的64位Windows 2003上以系统服务方式启动不起来。初步怀疑是我们的32位服务程序哪个地方在64位机上不兼容了。结果忙活了一上午,终于找到了问题所在。程序里有一段代码是用来判断程序是以服务方式启动还是以窗口形式启动:在应用程序初始化时获得父进程的句柄。然后通过句柄获得父进程的执行程序全路径。如果全路径中存在"service.exe"字符串,则程序以服务方式启动。伪代码如下:
-
- HANDLE hParentProc; //Parent proccess handle initialize
- BOOL bRet; //Check if we should run it as service
- if(GetModuleFileNameEx(hParentProc, NULL, pszPath, MAX_PATH))
- {
- if(strstr(pszPath, "services.exe") != NULL)
- {
- bRet = TRUE;
- }
- }
通过调试发现问题出在GetModuleFileNameEx函数上。本来它返回的是获取全路径的长度,结果在64位机上返回0,pszPath变量并没有取得父进程的全路径。我用GetLastError()查看返回值为error 299——“Only part of a ReadProcessMemory or WriteProcessMemory request was completed.” 。我开始怀疑是因为32位程序调用API处理64位内存地址时出现的问题。
于是我Google了一下,终于找到答案了:
当我们调用GetModuleFileNameEx的API函数时,为了获得指定进程的全路径,它内部需要访问进程的PEB头(process environment block),将PEB中的信息设置到一个叫PROCESS_BASIC_INFORMATION 的结构体中。结构体声明如下
- typedef struct _PROCESS_BASIC_INFORMATION {
- NTSTATUS ExitStatus;
- PPEB PebBaseAddress;
- ULONG_PTR AffinityMask;
- KPRIORITY BasePriority;
- ULONG_PTR UniqueProcessId;
- ULONG_PTR InheritedFromUniqueProcessId;
- } PROCESS_BASIC_INFORMATION;
如果你的32位应用程序是运行在Windows XP或者以上的操作系统上的,推荐的解决方案是使用GetProccessImageFileName来替代GetModuleFileNameEx来取得进程的全路径,这个函数内部的内部操作不会像GetModuleFileName那样麻烦,只返回一个全路径字符串而已。但是返回的全路径是DOS格式的盘符路径( /Device/HarddiskVolumeX),因此需要自己再转换一下。
出了GetModuleFileNameEx之外,还有EnumProcessModule和 EnumProcessModuleEx 也会出现这样的问题,都是因为访问64位进程的PEB头的原因。CreateToolHelpSnapshot调用失败原因也与上面的原理类似。
参考:http://winprogger.com/?p=26