Re: [問題] DLL問題

看板C_and_CPP作者 (purpose)時間12年前 (2012/07/28 09:25), 編輯推噓4(4014)
留言18則, 6人參與, 最新討論串10/10 (看更多)
早安,早起的鳥兒有蟲吃 繼續昨天的話題,也就是 DLL 的入口函數有沒有分 VC、BCB 版本。 已知 DLL 只是一個 PE/COFF 格式,與程式語言無關,可是 VC 的入口函數 叫我們要用 DllMain。而我不太熟的 BCB,根據原 PO 提供的資訊,其入口函數 卻是使用 DLLEntryPoint。分別觀察兩函數內容,可以發現差別只有名稱不同。 如果把 DllMain 與 DLLEntryPoint 一起暫稱為「Programmer 入口」, 而從 Windows 的 LoadLibrary() 函數,也就是 Loader 所進入的入口點, 稱為「DLL 入口」,則呼叫順序為: LoadLibrary() --> 透過 DLL File Header 找到並調用「DLL 入口」 --> 透過 神祕機制找到並調用「Programmer 入口」。 其中的「DLL 入口」是由 VC, BCB 所提供,這兩家廠商的「DLL 入口」分別選用 不同名稱的「Programmer 入口」,才有今天的狀況。但這都不重要,對於使用 DLL 的用戶來說,反正啟動 DLL 的動作都是透過 LoadLibrary 完成,你在意的只 是後面要用到的 DLL 匯出函數,誰有空理 DllMain 或 DLLEntryPoint 呢?又沒 有程式設計師,需要直接調用這兩類入口函數。 === 做個實驗,順便示範如何用 VC 產生一個名稱不叫 DllMain 的「Programmer 入口」。 以下是呼叫 DLL 端的程式碼 ---------------------------------------------------- #pragma comment(lib, "dllep") int __declspec(dllexport) mysub(int, int); int main() { mysub(10, 3); return 0; } ---------------------------------------------------- 以下為 DLL 的原始碼 ---------------------------------------------------- /* filename: dllep.c compile with: cl.exe dllep.c /Zi /LD /link /entry:foo The file header of dllep.dll contains a field: AddressOfEntryPoint. In Visual C++ environment, this address will point to function _DllMainCRTStartup(...) which is located in crtdll.c. In _DllMainCRTStartup(...), there is a external function pointer: extern BOOL (WINAPI * const _pRawDllMain)(HANDLE, DWORD, LPVOID); This pointer makes the DllMain(..) can be optional, since the final address is filled by VC++ Linker. The "DllMain" is just a default name. We could use alternative one by linker option /entry. */ #include<windows.h> #pragma comment(lib, "user32") BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { if (fdwReason == DLL_PROCESS_ATTACH) { MessageBoxA(NULL, "DllMain(...) of dllep.dll is called.", "dll", MB_OK); } return 1; } BOOL WINAPI foo(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { if (fdwReason == DLL_PROCESS_ATTACH) { MessageBoxA(NULL, "foo(...) of dllep.dll is called.", "dll", MB_OK); } return 1; } int __declspec(dllexport) mysub(int a, int b) { return (a + b + 1024); } ---------------------------------------------------- 就是編譯時用 linker 選項 /entry:foo 來指定新的 DLL 入口名稱為 foo 並保留原始的 DllMain 函數,觀看執行 DLL 時,是否真的會使用 foo 當入口。 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 124.8.128.76

07/28 09:32, , 1F
mysub 的原宣在呼叫 DLL 端不用 dllexport 寫錯但不影響
07/28 09:32, 1F

07/28 10:23, , 2F
hinstDLL = LoadLibrary("DLL.dll");
07/28 10:23, 2F

07/28 10:24, , 3F
getID = (ptr_getID)GetProcAddress(hinstDLL, "getI
07/28 10:24, 3F

07/28 10:24, , 4F
你呼叫dll的部份不需要這樣子嗎??
07/28 10:24, 4F

07/28 10:24, , 5F
不這樣你怎麼能夠指定到dll func?
07/28 10:24, 5F
如你所說,需要 GetProcAddress() 來取得匯出函數的位址,因為 DLL.dll 是 使用 LoadLibrary() 載入,是 explicit linking。 是原文哪段我的陳述有問題嗎?不懂為何提出此疑問 ※ 編輯: purpose 來自: 124.8.137.220 (07/28 13:24)

07/28 13:56, , 6F
LoadLibrary沒問題,主要是看到你呼叫dll那段
07/28 13:56, 6F

07/28 13:56, , 7F
沒有GetProcAddress怎麼能使用呢?
07/28 13:56, 7F
你是說 mysub(10, 3); 這段吧 這裡就是用 implicit linking 的方式來載入 dllep.dll,所以不需要在原始碼中 明確使用 LoadLibrary 跟 GetProcAddress。 一開始編譯器無法 DLL 呼叫端的原始碼內找到 mysub 這個符號,所以會把其位址 隨便設,比如變成 call 0x0000 0000。 然後由 Linker 在產生 .exe 時,找出 mysub 符號來,因為此時其他 *.obj 跟 *.lib 裡,也不會有可能找到 mysub 符號,所以產生 dllep.dll 時,會附送一個 匯入用靜態函式庫,就是假的 dllep.lib。 Linker 透過 dllep.lib 找到 mysub 的位址,然後就能順利結連結出可以使用 xx.exe。 這裡的 pragma comment(lib, "dllep") 就是假指令,告訴 Linker 有 dllep.lib 這個 檔案的存在。 然後呢 Linker 從 dllep.lib 這件事得知,產生後的 xx.exe 在被人執行時, 需要相依於 dllep.dll,因此 Linker 還會在 xx.exe 檔案裡面,暗藏指示內容給 作業系統的 Loader。 當 Loader 載入 xx.exe 發現這些暗藏的指示時,就會自動把 dllep.dll 透 過 LoadLibrary() 函數去載入。 所以 xx.exe 的原始碼中,沒有也不必手動執行 LoadLibrary。 同理,這個 Loader 在順利載入 dllep.dll 成功後,還會自動更正 mysub 的位址。 該位址原本是來自 dllep.lib,沒有真正指到 dllep.dll。 此時會被 Loader 修正成相當於 GetProcAddresss() 所得到的位址。 所以 Loader 也把 GetProcAddress 省略掉了。 ※ 編輯: purpose 來自: 124.8.137.220 (07/28 14:12) ※ 編輯: purpose 來自: 124.8.137.220 (07/28 14:24)

07/28 14:43, , 8F
所以一切的關鍵就是pragma comment(lib, "dllep")
07/28 14:43, 8F

07/28 14:43, , 9F
會自動做LoadLibrary 跟 GetProcAddress?
07/28 14:43, 9F

07/28 14:48, , 10F
另外請問.a跟.lib是一樣的嗎?
07/28 14:48, 10F

07/28 14:49, , 11F
我用gcc編出來的裡面有.a .dll .def
07/28 14:49, 11F

07/28 15:15, , 12F
我剛剛查了一下發現load dll有兩種方式...
07/28 15:15, 12F

07/28 15:15, , 13F
抱歉誤會了
07/28 15:15, 13F

07/28 15:59, , 14F
google 吧
07/28 15:59, 14F

07/28 20:48, , 15F
我可以偷偷問一下 #pragma 是幹嘛的嗎 好像是BC再用的@@
07/28 20:48, 15F
The #pragma directive is the method specified by the C standard for providing additional information to the compiler. 1. 這是 C 語言標準規定的假指令,gcc, bcb, vc 都有在用 2. 提供額外資訊給 compiler 用以控制其行為用的,廣義來說 linker 也包含在內。 3. pragma 好像是來自 pragmatic? 隨便啦 4. 最常用的 pragma 指令就是 #pragma comment(lib, "xxx") 5. 微軟: http://msdn.microsoft.com/en-us/library/d9x1s805.aspx GNU: http://gcc.gnu.org/onlinedocs/gcc/Pragmas.html ※ 編輯: purpose 來自: 124.8.137.220 (07/28 21:06)

07/28 21:30, , 16F
好久沒看 purpose 發文了, 推一個 !!
07/28 21:30, 16F

07/29 17:24, , 17F
好詳細,這查書應該要查一小時了= =
07/29 17:24, 17F

07/29 22:04, , 18F
推:)
07/29 22:04, 18F
文章代碼(AID): #1G4p-bip (C_and_CPP)
討論串 (同標題文章)
文章代碼(AID): #1G4p-bip (C_and_CPP)