為什么不能通過(guò)GetProcAddress調(diào)用CreateWindow?
有時(shí)候,我看到有些人在折騰這樣一個(gè)問(wèn)題:
“我想使用 GetProcAddress 來(lái)獲取 CreateWindow 或者 ExitWindows 的調(diào)用地址,但是沒(méi)有成功。為什么?”
通常,他們當(dāng)時(shí)是在嘗試編寫平臺(tái)調(diào)用(P/Invoke)相關(guān)的代碼,因?yàn)閺牡讓拥慕嵌葋?lái)看,平臺(tái)調(diào)用是通過(guò) GetProcAddress 來(lái)實(shí)現(xiàn)的。
問(wèn)題來(lái)了:為什么 GetProcAddress 不能用在這些函數(shù)上呢?
原因是:它們(CreateWindow 或 ExitWindows)并非真正的導(dǎo)出函數(shù),如果你查看對(duì)應(yīng)的頭文件,則會(huì)看到這樣的宏定義。
事實(shí)上,CreateWindow 是一個(gè)雙重宏定義,首先它會(huì)根據(jù)當(dāng)前是否定義了 UNICODE 來(lái)展開(kāi)為 CreateWindowA 或者 CreateWindowW。然后,這些類似于函數(shù)的宏會(huì)再次被展開(kāi)為真正的導(dǎo)出函數(shù) CreateWindowExA 或者 CreateWindowExW。
如果包含 winuser.h 頭文件,則所有這些都由編譯器自動(dòng)處理,但如果出于某種原因,您希望為類似函數(shù)的宏(如 CreateWindow)使用 GetProcAddress,則必須手動(dòng)展開(kāi)宏以查看實(shí)際函數(shù)是什么,并將該函數(shù)名稱傳遞給 GetProcAddress。
上述原理也適用于內(nèi)聯(lián)函數(shù)。這些函數(shù)無(wú)法通過(guò) GetProcAddress 獲取,因?yàn)樗鼈兏静粫?huì)導(dǎo)出,它們?cè)陬^文件中作為源代碼提供給您調(diào)用。
請(qǐng)注意,某些內(nèi)容是真正的函數(shù)還是類似函數(shù)的宏(或內(nèi)聯(lián)函數(shù))可能取決于您的目標(biāo)平臺(tái)。例如,GetWindowLongPtrA 在 64 位 Windows 上是真正的導(dǎo)出函數(shù),但在 32 位 Windows 上,它只是一個(gè)解析為 GetWindowLongA 的宏。再舉一個(gè)例子,Interlocked 系列函數(shù)在 x86 版本的 Windows 上是導(dǎo)出函數(shù),但在所有其他 Windows 體系結(jié)構(gòu)上是內(nèi)聯(lián)函數(shù)。
看起來(lái)還挺復(fù)雜的,那怎么能弄清楚這一切?方法是:研究頭文件。
在頭文件中,您將會(huì)看到函數(shù)是重定向宏、類似函數(shù)的宏、內(nèi)聯(lián)函數(shù)、內(nèi)部函數(shù)還是適當(dāng)?shù)膶?dǎo)出函數(shù)。如果你無(wú)法從頭文件中弄清楚,你總是可以只編寫一個(gè)程序來(lái)調(diào)用你感興趣的函數(shù),然后查看反匯編以查看實(shí)際生成的內(nèi)容。
總結(jié)
當(dāng)有不明白的地方的時(shí)候,最好的方法還是去翻閱源文件(頭文件)。
請(qǐng)堅(jiān)信:任何事情(Bug)都是有原因的。