GetEnvironmentStrings() 未遵照 generic-text-mapping 慣例
今天在寫 code 用到 GetEnvironmentStrings(),雖然處在 UNICODE 環境下,但我希望使用 ANSI 版本,所以依照 generic-text-mapping 的慣例,直接呼叫 GetEnvironmentStringsA(),沒想到卻怎麼樣也找不到 function declaration。
所謂的 generic-text-mapping 技術,在這篇《Writing libraries with Generic-Text-Mapping》裡有詳細的介紹。簡單講就是,因為不確定呼叫 library 的程式,會是 UNICODE 還是 ANSI 版本,故 library 就兩者皆提供,然後在 header file 裡做特殊處理,選擇真正呼叫的函式版本。因為 header file 是被呼叫端 #include,與呼叫端程式一起編譯的,故利用如下的寫法,就可以選擇真正呼叫的函式版本:
int StrToIntA(const char* str); int StrToIntW(const wchar_t* str); #ifdef _UNICODE # define StrToInt(str) StrToIntW(str) #else # define StrToInt(str) StrToIntA(str) #endif
其中,依據 generic-text-mapping 建立的命名慣例,後綴字(postfix)為 W 時,表示是 wchar_t 的版本;後綴字為 A 時,則表示是 ANSI,即 char 的版本。
所以,依照慣例,GetEnvironmentStrings() 應該會有三個版本:
| 函式原型 | 版本 |
|---|---|
TCHAR* GetEnvironmentStrings() |
Generic-text-mapping 版 |
char* GetEnvironmentStringsA() |
ANSI 版 |
wchar_t* GetEnvironmentStringsW() |
UNICODE 版 |
而根據 MSDN 的說明,也是如此:
Implemented as
GetEnvironmentStringsW(Unicode) andGetEnvironmentStringsA(ANSI).
只好追蹤原始碼,看看 GetEnvironmentStrings() 是怎麼被宣告的。在 WinBase.h 裡發現,GetEnvironmentStrings() 是這麼被宣告的:
WINBASEAPI
LPSTR
WINAPI
GetEnvironmentStrings(
VOID
);
WINBASEAPI
LPWSTR
WINAPI
GetEnvironmentStringsW(
VOID
);
#ifdef UNICODE
#define GetEnvironmentStrings GetEnvironmentStringsW
#else
#define GetEnvironmentStringsA GetEnvironmentStrings
#endif // !UNICODE
原本 ANSI 版的實作應該是 GetEnvironmentStringsA(),卻變成了 GetEnvironmentStrings(),然後在後面的 macro 裡,又補上將 GetEnvironmentStringsA 對應到 GetEnvironmentStrings()。
造成的結果是,如果呼叫端的程式是 ANSI 版,那一切太平,GetEnvironmentStrings()、GetEnvironmentStringsA() 與 GetEnvironmentStringsW() 三者皆可以使用。但如果呼叫端的程式是 UNICODE 版,那事情就麻煩了,只有 GetEnvironmentStringsW() 可以使用:
- 若呼叫
GetEnvironmentStringsA(),會沒有這個函式原型; - 若呼叫
GetEnvironmentStrings(),則會被對應到GetEnvironmentStringsW(),讓接收回傳值到char*變數的部份,產生編譯錯誤。
基本上我認為,這應該是 Microsoft 的筆誤,只是我翻遍了 Visual C++ 6、Visual Studio 2005 與 Visual Studio 2008,以及手上有的好幾個 Windows Platform SDK 版本,裡面都含有同樣的錯誤。這樣的問題,可以存活這麼久,還真是不可思議啊。
解決方法?當然是另外用一個獨立的 compilation unit,也就是獨立的 .cpp 檔,包一組 MyGetEnvironmentStrings()、MyGetEnvironmentStringsA() 與 MyGetEnvironmentStringsB(),然後特別注意,在這個特別的 compilation unit,使用 ANSI 版本,也就是不要宣告 UNICODE 與 _UNICODE,或在 #include <windows.h> 之前,#undef UNICODE 與 #undef _UNICODE。
Random Posts
- None Found
Similar Posts
- None Found
2 Comments
補上GetEnvironmentStringsA prototype是否可行?
(kernel32.dll 還是有export GetEnvironmentStringsA)
可以自己宣告。但如果已經 #include 過 <windows.h> 了,那還是得另外開一個 compilation unit 才行。
Post a Comment