GetModuleFileNameEx GetLastError()错误299解决方案

如下代码:

GetModuleFileNameEx(hProcess, NULL, strPath.GetBufferSetLength(MAX_PATH), MAX_PATH);

DWORD dError = GetLastError();

在win7 64位系统上执行,dError值为299。

通过调试发现问题出在GetModuleFileNameEx函数上本来它返回的是获取全路径的长度结果在64位机上返回0,pszPath变量并没有取得父进程的全路径。我用GetLastError()查看返回值为error
299——“Only part of a ReadProcessMemory or WriteProcessMemory request was completed.” 。我开始怀疑是因为32位程序调用API处理64位内存地址时出现的问题。


当我们调用GetModuleFileNameEx的API函数时,为了获得指定进程的全路径,它内部需要访问进程的PEB头(process environment block),将PEB中的信息设置到一个叫PROCESS_BASIC_INFORMATION 的结构体中。结构体声明如下

  1. typedef struct _PROCESS_BASIC_INFORMATION {
  2.     NTSTATUS ExitStatus;
  3.     PPEB PebBaseAddress;
  4.     ULONG_PTR AffinityMask;
  5.     KPRIORITY BasePriority;
  6.     ULONG_PTR UniqueProcessId;
  7.     ULONG_PTR InheritedFromUniqueProcessId;
  8. } PROCESS_BASIC_INFORMATION;

    其中PEB的地址被设置在PebBaseAddress中。但是64位进程的PEB头地址是保存在64位长度的地址中的(比如上面说道的系统进程Services.exe),而32位进程的PEB头地址只有32位长度。运行在64位系统上32位应用程序是如何将64位的PEB地址转换成32位地址的呢?如果64位PEB地址的高32位为0,则转换不会出现任何问题。但如果高32位也包含地址信息,那么WOW64(Windows
32-bit on Windows 64-bit,windows 64位系统上兼容32位应用程序的技术,作为由32位向64位程序的过渡方案)只是简单的将低32位的PEB地址赋给32位应用程序中的PebBaseAddress变量,当然就会发生错误了!于是Windows就会出发error 299并返回失败。

    如果你的32位应用程序是运行在Windows XP或者以上的操作系统上的,推荐的解决方案是使用GetProccessImageFileName来替代GetModuleFileNameEx来取得进程的全路径,这个函数内部的内部操作不会像GetModuleFileName那样麻烦,只返回一个全路径字符串而已。但是返回的全路径是DOS格式的盘符路径(
/Device/HarddiskVolumeX),因此需要自己再转换一下。

    出了GetModuleFileNameEx之外,还有EnumProcessModule和EnumProcessModuleEx 也会出现这样的问题,都是因为访问64位进程的PEB头的原因。CreateToolHelpSnapshot调用失败原因也与上面的原理类似。
参考:http://winprogger.com/getmodulefilenameex-enumprocessmodulesex-failures-in-wow64/

你可能感兴趣的:(日常小知识)