Re: [問題] *handle的用法?

作者: LPH66 (-6.2598534e+18f)   2014-08-14 15:51:21
※ 引述《sv39933993 (^^)》之銘言:
: typedef void *DEV_HANDLE; //line 1
: DEV_HANDLE WINAPI Device_xOpen(const int nDevIndex); //line 2
: typedef DEV_HANDLE (WINAPI *LP_DevOpen)(const int nDevIndex,
: const char *pDevName); // line 3
: DEV_HANDLE m_hLink; // line 4
: LP_DevOpen m_Open; // line 5
: m_Open = (LP_DevOpen)::GetProcAddress(hDLL, "Device_xOpen"); // line 6
: m_hLink = m_Open(0, ""); // line 7
: if (m_hLink){ ......} // line 8
: 疑問:
: 1. DEV_HANDEL為指標型式,所以 line 2 的意思是函式Device_xOpen的回傳值
: 是一個位址嗎?
: 2. 如果"函式Device_xOpen的回傳值是一個位址",
: 那為何line 7, line 8 看起來 m_hLink 是一個值?
: 3. Device_xOpen函式的輸入值只有一個nDevIndex,
: 經過了line 3, line 6可以多出一個pDevName的原因是?
: pDevName在原本的Device_xOpen沒有,它代表的是甚麼呢?
4.5.兩題在推文差不多回答完了
這裡就只是明確的表示我要呼叫 global 的那個 GetProcAddress 而已
(之所以要明確表示是因為不加的話不一定會呼叫到那一個
這跟編譯器怎麼找到你的函式有關, 這邊表過不提)
1.2.兩題可以仔細看我推的那篇 #1I6t3pwd 的最後一段
DEV_HANDLE 就是那篇文裡談的所謂的 HANDLE 值
它很有機會是一個內部指標, 所以形式上它是一個 void * 這種萬用指標
但外面在用時只要當做一個不透明的值就好 (可以想成跟 C 的 FILE* 很像就行了)
這種形態通常會有一個特殊值表示「這值不代表任何東西」
#8 就是在測試是不是這種特殊值, 如果不是才做事
第三題要連著 Windows 的 DLL 相關 API 一起來談
(其實主要就是 #6 的 GetProcAddress 這個 API)
我們的目的其實是要找出一個 DLL 裡的函數去呼叫它
如果 DLL 本身有一個對應的 .lib 的話
那個 .lib 裡面會有一小段程式碼幫你找出 DLL 裡的函式在哪裡
(這有個專有名詞叫 stub, 不過 stub 的意義稍微廣了點, 這裡也表過不提)
這段 stub 實際上就是使用 GetProcAddress 去找出函式
http://msdn.microsoft.com/en-us/library/windows/desktop/ms683212(v=vs.85).aspx
GetProcAddress 使用兩個參數, 第一個是 DLL 的 HANDLE, 第二個是函式名字串
而它的回傳值上面的說明寫著 FARPROC
深入研究這個名字會發現它是這麼被定義的:
typedef int (FAR WINAPI *FARPROC)();
它是一個不檢查參數,回傳 int,具有 WINAPI (__stdcall) 的 function pointer
(FAR 這東西是歷史遺跡, 現在可以不用理它)
總之, 它回傳一個 function pointer, 指向所找到的函式
所以我們可以用這個 function pointer 來呼叫到那個函式
(其實 stub 裡做的還稍微多了一點
它取一次之後會把結果存起來, 下次再呼叫時就不用再取一次;
這裡還有一個最佳化技巧在裡面不過因為又更進階了所以表過不提)
但是因為 GetProcAddress 是用來取得各種函式的
所以不可能知道目標函式實際上需要何種參數
因此只好折衷用一個相對萬用的函式指標型態 int(*)() 回傳
於是要使用 GetProcAddress 時必須轉型成你呼叫的那個函式的形態才能呼叫
#3 就是將這個函式形態定一個名字叫做 LP_DevOpen
在 #6 裡 GetProcAddress 回來之後就轉成這種函式之後再在 #7 呼叫
這裡就回到了你最初的問題: 為什麼經過那一段之後多了一個參數
原因很簡單: 不是 DLL 作者寫錯了就是 DLL 使用者寫錯了
意思就是要嘛 #2 有問題, 要嘛 #3/#6/#7 有問題
這兩個函式形態必須要一樣呼叫才不會有事
(這種不一樣編譯器抓不出來
因為 DLL 是動態連結的東西, 在靜態連結時編譯器不知道那個 DLL 的函式長怎樣
使用附帶的 .lib 在這裡有個好處就是那裡的 stub 是靜態的, 可以給 DLL 使用者比對
而且因為它是在編 DLL 時附帶一起產生的因此不會有一致性問題)
或許 #3/#6/#7 使用的 DLL 用的是更新版的函數多帶一個字串參數
這樣這裡就又有一個常見的問題叫做 ABI 相容性
ABI 全名 Application Binary Interface 它是指二進位層級上互相呼叫的接口
一個 DLL 的函數介面更新代表對這個 DLL 的 ABI 有變動
這對於程式碼的使用者來說是最糟的狀況
跟同樣也在這個狀況裡出現的原始碼層級 API 變動還糟
ABI 變動代表你不能直接換掉 DLL, 要連 DLL 使用者的程式一起更新才行
使用者必須要去看過所有使用這個 DLL 的地方並更改使得它符合新的 ABI
一不小心忘了改就有可能有慘劇發生
所以你最好去找這個 DLL 的相關資料看看是不是哪裡有問題
(會造成 ABI 變動不只有函數介面更新會造成, 還有很多其他原因
這也是 DLL 作者需要小心的地方)
作者: QQ29 (我愛阿蓉)   2014-08-14 23:21:00
請問L大有abi compatible的資料可以學習嗎,很像知道軟體公司怎樣規範,才能每次update都保證dll 和exe相容
作者: LPH66 (-6.2598534e+18f)   2014-08-15 03:22:00
英文維基百科 Application binary interface 條目當中提到了一些 ABI 所包含的東西, 這些東西都是需要注意的近年一個常見的 ABI 基本規範是 Itanium C++ ABIgcc/g++ 跟的就是這套 ABI 規範走, 所以滿值得參考的不過這個主要是在做編譯器才比較容易碰到這層細節一般的 AP 層主要還是以原始碼層級的 API 相容為主只要編譯時使用的 ABI 相關選項一致那大致上不會有什麼問題
作者: QQ29 (我愛阿蓉)   2014-08-15 18:52:00
可是在dll新增virtual function or改變function prototype這不叫binary imcompatible嗎,還是abi相容跟bin compatible兩碼子事
作者: LPH66 (-6.2598534e+18f)   2014-08-15 19:47:00
我覺得可以說 ABI 講的東西更廣一點這樣

Links booklink

Contact Us: admin [ a t ] ucptt.com