32位程式在64位系統上呼叫GetModuleFileNameEx失敗
阿新 • • 發佈:2019-02-19
今天客戶打來電話說我們公司的伺服器程式在新裝的64位Windows 2003上以系統服務方式啟動不起來。初步懷疑是我們的32位服務程式哪個地方在64位機上不相容了。結果忙活了一上午,終於找到了問題所在。程式裡有一段程式碼是用來判斷程式是以服務方式啟動還是以視窗形式啟動:在應用程式初始化時獲得父程序的控制代碼。然後通過控制代碼獲得父程序的執行程式全路徑。如果全路徑中存在"service.exe"字串,則程式以服務方式啟動。虛擬碼如下:
於是我Google了一下,終於找到答案了:
當我們呼叫GetModuleFileNameEx的API函式時,為了獲得指定程序的全路徑,它內部需要訪問程序的PEB頭(process environment block),將PEB中的資訊設定到一個叫PROCESS_BASIC_INFORMATION 的結構體中。結構體宣告如下
如果你的32位應用程式是執行在Windows XP或者以上的作業系統上的,推薦的解決方案是使用GetProcessImageFileName來替代GetModuleFileNameEx來取得程序的全路徑,這個函式內部的內部操作不會像GetModuleFileName那樣麻煩,只返回一個全路徑字串而已。但是返回的全路徑是DOS格式的碟符路徑( /Device/HarddiskVolumeX),因此需要自己再轉換一下。
- 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;
- }
- }
於是我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或者以上的作業系統上的,推薦的解決方案是使用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