1. 程式人生 > >32位程式在64位系統上呼叫GetModuleFileNameEx失敗

32位程式在64位系統上呼叫GetModuleFileNameEx失敗

今天客戶打來電話說我們公司的伺服器程式在新裝的64位Windows 2003上以系統服務方式啟動不起來。初步懷疑是我們的32位服務程式哪個地方在64位機上不相容了。結果忙活了一上午,終於找到了問題所在。程式裡有一段程式碼是用來判斷程式是以服務方式啟動還是以視窗形式啟動:在應用程式初始化時獲得父程序的控制代碼。然後通過控制代碼獲得父程序的執行程式全路徑。如果全路徑中存在"service.exe"字串,則程式以服務方式啟動。虛擬碼如下:
  1. HANDLE hParentProc; //Parent proccess handle initialize
  2. BOOL bRet; //Check if we should run it as service
  3. if(GetModuleFileNameEx(hParentProc, NULL, pszPath, MAX_PATH))
  4. {
  5. if(strstr(pszPath, "services.exe") != NULL)
  6.     {
  7.         bRet = TRUE;        
  8.     }
  9. }
通過除錯發現問題出在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 的結構體中。結構體宣告如下
  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或者以上的作業系統上的,推薦的解決方案是使用GetProcessImageFileName來替代GetModuleFileNameEx來取得程序的全路徑,這個函式內部的內部操作不會像GetModuleFileName那樣麻煩,只返回一個全路徑字串而已。但是返回的全路徑是DOS格式的碟符路徑( /Device/HarddiskVolumeX),因此需要自己再轉換一下。

    除了GetModuleFileNameEx之外,還有EnumProcessModule和EnumProcessModuleEx 也會出現這樣的問題,都是因為訪問64位程序的PEB頭的原因。CreateToolHelpSnapshot呼叫失敗原因也與上面的原理類似。

參考:

https://msdn.microsoft.com/en-us/library/ms682631(VS.85).aspx

http://winprogger.com/getmodulefilenameex-enumprocessmodulesex-failures-in-wow64/

https://social.msdn.microsoft.com/Forums/vstudio/en-US/d63cc18c-f54d-4e5a-964f-dda789b1f318/createtoolhelp32snapshot-on-a-64-bit-machine?forum=vcgeneral