探 訪 動 態 連 結 函 式 庫 (Dynamic Linking Libraries,DLLs) 蕭 永 哲 martins1@ms3.hinet.net
DLL 一 二 說 DLLs 的 出 現 提 供 了 程 式 設 計 師 一 個 將 程 式 模 組 化 的 方法 , 別 於C++ 類 別 的 建 構 時 期 模 組 化 ,DLL 乃 是 執 行 時 期 模組 化 , 因 此 程 式 設 計 師 可 以 在 建 構 程 式 時 將 所 需 要 用 到的 函 式 分 門 別 類 的 製 造 成DLLs 的 形 式 。 但 為 什 麼 我 們 要 使用DLLs 呢 ?DLLs 充 其 量 不 就 你 把 程 式 碼 、 資 源 等 獨 立 到 另一 個 檔 案 裡 頭 去 , 到 底 有 什 麼 好 處 值 得 我 們 大 費 周 章 地把 部 份 程 式 寫 成DLLs 的 形 式 呢 ? 使 用DLL 的 好 處 大 致 可 以 如下 歸 類 : 1 、 有 效 率 的 重 複 使 用 程 式 碼 當 程 式 設 計 師 所 撰 寫 的 程 式 碼 一 多 起 來 , 必 然 地 會 發現 有 很 多 程 式 碼 是 在 做 相 同 的 事 情 , 通 常 當 程 式 設 計 師遇 到 這 些 重 複 的 程 式 碼 , 最 平 常 不 過 的 方 法 就 是 把 這 些重 複 的 程 式 碼 大 則 獨 立 成 『 函 式 』 (Function ) 小 則 獨 立成 『 巨 集 』 (Marco ) 。 但 是 , 當 這 些 函 式 不 單 單 只 在 單一 應 用 程 式 內 會 用 到 , 而 是 在 撰 寫 許 多 應 用 程 式 時 都 得用 到 時 , 這 些 常 用 的 函 式 通 常 就 會 被 製 作 成 函 式 庫 來 使用 , 例 如C 語 言 的Runtime Library (RTL ) ; 但 編 譯 器 在 編 譯 應用 程 式 遇 到 函 式 庫 時 , 會 把 這 些 隱 身 在 函 式 庫 裡 頭 的 函式 實 體 內 容 如 同 我 們 在 程 式 裡 頭 撰 寫 這 些 函 式 的 原 始 碼般 地 加 進 應 用 程 式 的 執 行 檔 中 , 也 就 是 說 當 你 所 用 的 函式 庫 越 多 時 , 你 的 執 行 檔 也 就 相 對 的 會 越 來 越 龐 大 , 這個 做 法 也 就 是 我 們 所 謂 的 靜 態 連 結 (Static Linking ) 。 因此 為 了 避 免 應 用 程 式 的 過 分 龐 大 , 有 人 提 出 了 動 態 連 結(Dynamic Linking ) 的 做 法 , 所 謂 動 態 連 結 就 是 提 供 了 一 個做 法 讓 我 們 不 需 要 把 應 用 程 式 的 執 行 檔 變 得 如 此 龐 大 ,但 一 樣 可 以 享 用 這 些 使 用 頻 率 高 的 函 式 。 也 就 是 說 這 些函 式 會 在 程 式 執 行 時 才 被 載 入 , 而 不 是 直 接 編 譯 在 執 行檔 中 。 這 樣 一 來 可 以 讓 我 們 更 有 效 率 使 用 這 些 函 式 。 但相 對 的 , 當 你 所 撰 寫 的 程 式 得 交 給 他 人 使 用 時 , 你 除 了得 把 你 所 編 譯 好 的EXE 檔 交 給 他 之 外 , 還 得 一 併 把 編 譯 好的DLLs 檔 交 給 他 , 否 則 執 行 起 來 一 定 會 產 生 不 可 預 期 的 錯誤 。 2 、 區 分 程 式 碼 依 據 先 前 的 第 一 點 , 若 有 朝 一 日 發 現 這 些 被 包 裝 在DLLs 之 中 的 函 式 的 實 作 方 法 有 點 錯 誤 或 是 發 現 有 更 好 的 做 法時 , 需 要 更 動 僅 只 有 部 份 的DLLs 原 始 碼 , 重 新 將 修 改 過 的DLLs 給 編 譯 後 就 可 以 達 成 更 新 程 式 的 目 的 , 至 於 應 用 程 式 端連 動 都 不 需 要 動 一 下 ; 當 然 了 , 前 提 是 這 些 個 修 改 過 的函 式 名 稱 與 傳 入 的 參 數 型 別 宣 告 都 不 能 夠 更 動 。 根 據 這些 個 特 性 , 咱 們 可 以 把 整 個 應 用 程 式 中 的 函 式 依 照 功 能或 目 的 分 類 , 並 將 這 些 分 類 好 的 函 式 組 合 成 許 多 個DLLs 模組 , 將 執 行 檔 給 分 割 城 數 個 小 檔 案 , 讓 這 些DLLs 模 組 分 工合 作 來 完 成 應 用 程 式 所 要 達 到 的 目 的 。 這 樣 一 來 , 對 於應 用 程 式 的 維 護 以 及 更 新 就 不 需 要 大 費 周 章 地 從 頭 再 編譯 一 次 了 , 僅 需 把 要 有 修 改 到 的DLLs 從 新 編 譯 就 可 以 達 成程 式 的 更 新 。 3 、 節 省 記 憶 體 的 使 用 量 先 前 提 到 :DLLs 的 載 入 是 在 應 用 程 式 執 行 時 才 被 載 入 ,甚 至 還 可 以 是 可 以 在 應 用 程 式 所 需 用 到 函 式 時 才 被 載 入。 此 外DLLs 還 有 一 個 很 重 要 的 特 性 : 若 不 同 的 應 用 程 式 但需 要 相 同 的DLL 中 的 函 式 時 ,DLL 僅 在 第 一 個 使 用 到DLL 的 應用 程 式 執 行 時 載 入 , 只 要 這 個 應 用 程 式 尚 未 結 束 而 其 他的 應 用 程 式 又 正 好 需 要 使 用 到 同 一DLL 中 的 函 式 時 ,DLL 不需 要 再 重 新 被 載 入 到 記 憶 體 中 就 可 以 供 第 一 個 應 用 程 式以 外 需 要 用 到DLL 的 應 用 程 式 使 用 , 一 直 到 沒 有 任 何 應 用程 式 使 用 這 個DLL 時 ,DLL 才 會 跟 著 最 後 一 個 使 用DLL 的 應 用程 式 一 起 從 記 憶 體 裡 頭 消 失 , 因 此 使 用DLLs 來 包 裝 常 用 的函 式 是 個 不 錯 能 夠 節 省 記 憶 體 與 系 統 資 源 的 做 法 。 4 、 將 程 式 推 向 國 際 舞 台 DLL 在 設 計 時 , 就 已 經 被 設 計 不 只 是 能 夠 放 入 函 式 而 已, 還 能 夠 被 放 入 許 多 的 資 源 (Resource ) , 如 : 可 以 放 入選 單 資 料 、 字 串 資 料 、 圖 形 資 料 等 等 … 。 也 因 此DLL 很 常被 拿 來 作 為 應 用 程 式 邁 向 國 際 舞 台 的 一 個 墊 腳 石 。 你 可以 在 應 用 程 式 被 執 行 時 檢 查 執 行 應 用 程 式 的 作 業 系 統 語言 版 本 , 之 後 把 當 地 語 言 版 本 的Resource DLL 給 載 入 , 讓 所有 的 文 字 及 畫 面 都 達 到 當 地 語 言 化 的 目 的 。 而 這 個 功 能在C++Builder 3 當 中 經 由Borland C++Builder 部 門 工 程 師 的 努 力 ,已 經 幫 我 們 把 這 些 煩 人 的 步 驟 給 簡 化 了 許 多 , 我 們 僅 須按 下 選 單 上 的New 並 選 擇Resource DLL Wizard , 並 將 將 需 要 更 改的 文 字 給 更 改 成 不 同 的 語 言 , 並 不 須 在 執 行 時 檢 查 執 行平 台 的 語 言 為 何 。 其 餘 煩 瑣 的 工 作 都 已 經 被Borland 工 程 師給 完 成 了 。 我 們 最 後 只 需 要 重 新 編 譯 這 個Resource DLL 並 附在 應 用 程 式 中 就 可 以 完 成 一 個 國 際 化 的 應 用 程 式 了 。 既 然DLLs 在Windows 上 頭 是 那 麼 的 重 要 且 使 用DLLs 還 有 那 麼多 的 好 處 , 當 然 值 得 咱 們 來 好 好 的 了 解 一 下 。 先 來 討 論DLLs 的 基 本 架 構 。 Import 還 是Export ? 我 們 已 經 知 道 當DLL 被 應 用 程 式 載 入 時 ,DLL 內 所 含 有 的資 料 都 會 成 為 應 用 程 式 所 屬 行 程 中 的 一 部 份 , 這 到 底 怎麼 辦 到 的 ? 其 實 在DLL 被 應 用 程 式 載 入 時 ,DLL 會 被 先 設 定一 個 基 底 位 址 (Base Address ) , 若 這 個 基 底 位 址 並 沒 有 和應 用 程 式 中 的 其 他 資 源 互 相 衝 突 , 則 這 個DLL 檔 會 被 映 射到 載 入 端 行 程 內 的 相 同 位 址 上 讓 載 入 端 應 用 。 那DLL 中 到底 有 多 少 資 訊 可 以 被 載 入 呢 ? 先 看 看 圖 一 : 圖 一 是 使 用C++Builder 內 附 上 的tdump.exe 工 具 列 出 一 個DLL 檔內 所 有 的Section ,tdump 是 個 非 常 好 用 的 工 具 , 之 後 使 用 次會 不 少 , 先 說 明 用 法 , 用 法 很 簡 單 : tdump inputfile outputfile 這 樣 就 可 以 把inputfile 裡 頭 的 資 料 給 格 式 化 輸 出 到outputfile 裡 頭 , 當 然 了 , 你 的inputfile 得 是tdump 認 得 的 檔 案 :DOS 下的 執 行 檔 、PE 格 式 執 行 檔 ( 註1 ) 、.OBJ 檔 、.LIB 檔 , 其 餘的 檔 案 若 為 文 字 格 式 就 直 接 輸 出 , 而Binary 格 式 的 檔 案 就以HEX Dump 格 式 輸 出 。 此 外 , 在Microsoft Visual C++ 裡 頭 的dumpbin.exe 也 提 供 相 同 功 能 來 分 析 這 些 檔 案 。 圖 一 中 的 這 些Section 表 示 著 :
除 了 了 解 這 些section 之 外 , 你 還 必 須 知 道 的 另 一 個 觀 念是 所 謂 的 相 對 虛 擬 位 址 (Relative Virtual Address ,RVA) 。PE 格式 執 行 檔 中 有 許 多 資 料 的 位 址 都 是 以RVA 表 示 。 簡 單 的 來說RVA 是 某 一 項 資 料 從 檔 案 被 映 射 進 來 的 起 點 算 起 的 偏 移值 (offset ) 。 舉 個 例 子 , 我 們 說Windows 載 入 器 把 一 個PE 格式 執 行 檔 檔 映 射 到 虛 擬 位 址 空 間0x400000 處 , 如 果 在 此 執行 檔 中 有 一 個 函 式 的 函 式 指 標 起 始 於0x40C000 , 那 麼 這 個函 式 指 標 的RVA 就 是0x C000 : 虛 擬 位 址 (0x40C000 ) ─ 基 底 位 址 (0x400000 ) = RVA (0xC000 ) 只 要 把 相 對 虛 擬 位 址 加 上 基 底 位 址 , 相 對 虛 擬 位 址 就可 以 被 轉 換 為 一 個 有 用 的 指 標 。 『 基 底 位 址 』(Base Address) 也 是 另 一 個 重 要 名 詞 , 通 常 基 底 位 址 是 用 來 描 述 被 映 射到 記 憶 體 中 的EXE 或DLL 的 起 始 位 址 。 另 外 圖 一 中 『Key to section flags 』 是 這 些section 的 屬 性 旗標 種 類 , 如 : 唯 讀 、 共 享 或 可 寫 入 等 等 … 每 個section 的 屬相 可 以 從 『Object table 』 最 後 一 欄 的 『Flag 』 中 看 出 。 了 解 這 些 基 本 知 識 後 , 再 回 頭 詳 細 看 一 看 在 圖 一 中 的輸 出 輸 入section , 我 們 已 經 知 道 , 製 作DLL 的 主 要 目 的 是 製造 一 個 模 組 化 的 函 式 或 資 料 供 其 他 的 程 式 應 用 , 而 這 種提 供 給 其 他EXE 和DLL 使 用 的 方 式 就 稱 為 輸 出 (export ) , 反之 若 取 用 其 他 的EXE 或DLL 中 的 函 式 , 就 稱 為 輸 入 。 在DLL 中, 你 可 以 輸 出 任 何 想 要 輸 出 的 資 料 , 如 函 式 、 類 別 (class ) 或 是 資 源 等 等 … , 我 們 把 重 點 放 在 輸 出 輸 入 函 式 的 部份 , 我 們 先 來 觀 察 一 般 的 輸 出 函 式 : 一 樣 可 以 使 用 先 前 提 到 的tdump 來 觀 察DLL 中 的 輸 出 表 格, 由 圖 二 中 可 以 看 到 輸 出 函 式 表 格 包 含 了Ordinal 、RVA 以 及Name 三 個 欄 位 表 示 。Name 就 是 輸 出 的 函 式 名 稱 而RVA ─ 相 對 虛 擬位 址 在 前 頭 已 經 介 紹 過 了 , 至 於Ordinal 則 是 輸 出 表 格 中 輸出 函 式 的 序 號 。 這 些 輸 出 函 式 透 過 這 個 表 格 上 的 函 式 名稱 與 函 式 序 號 讓 外 界 認 得 。 當 載 入 端 最 初 在 載 入DLL 時 並不 知 道DLL 內 的 輸 出 函 式 的 正 確 位 址 只 知 道 函 式 的 序 號 與名 稱 , 但 在 動 態 連 結 的 過 程 中 會 建 立 起 一 個 連 連 看 表 格將 載 入 端 的 函 式 呼 叫 與 被 載 入 端 內 的 函 式 正 確 位 址 給 連結 起 來 。 那 我 們 要 怎 樣 才 能 夠 達 成 輸 出 的 動 作 , 其 實 很 簡 單 ,你 只 要 在 你 的 應 用 程 式 中 需 要 輸 出 的 函 式 前 頭 加 上 :__declspec(dllexport) 即 可 , 如 : __declspec(dllexport) void Function(void); 這 樣 一 來 就 會 把Function(void) 這 個 函 式 給 放 到 輸 出 表 格上 頭 了 。 先 前 還 有 提 到 還 可 以 將C++ 類 別 透 過DLL 來 輸 出 , 可 以 的一 樣 是 加 上__declspec(dllexport) , 如 : class __declspec(dllexport) __stdcall MyClass : public TObject{ … …}; 當 我 們 在 看 輸 出 表 格 時 , 會 發 現 函 式 的 輸 出 表 格 前 頭好 像 有 個 如 圖 三 一 般 的 資 料 : 這 就 是 函 式 的 輸 入 表 格 , 這 裡 列 出 來 的 是USER32.DLL 裡 頭被 我 們 使 用 到 的 函 式 。 一 樣 的 怎 樣 建 立 輸 入 表 格 呢 ? 一般 的Win32 API 都 已 經 在 其 所 屬 的Header File 裡 頭 定 義 好 了 ,我 們 只 需 加 上#include __declspec(dllimport) void Function(void); 目 的 是 告 訴 編 譯 器Function(void) 這 個 函 式 是 由 外 部 輸 入的 。 Name Mangling 嗯 ! 經 過 了 先 前 的 說 明 , 讀 者 們 應 該 知 道 若 要 在DLLs 中將 函 式 輸 出 , 僅 需 要 在 函 式 的 宣 告 中 加 上__declspec(dllexport) 即 可 , 若 要 載 入 別 的DLLs 中 的 函 式 則 須 在 函 式 的 宣 告 中 加上__declspec(dllimport) , 但 這 僅 止 於C 編 譯 器 , 在C++ 編 譯 器 裡頭 是 行 不 通 的 , 怎 麼 說 呢 ? 就 拿 一 個 多 載 (overloading )的 例 子 來 說 , 如 果 函 式 的 名 稱 都 相 同 ( 當 然 所 傳 的 參 數型 別 不 同 ) , 編 譯 器 應 該 會 如 何 處 理 ? 到 底 那 個 才 是 我們 真 正 使 用 到 的 函 式 呢 ? 其 實 在 編 譯 器 做 編 譯 動 作 時 ,對 這 些 同 名 的 函 式 都 動 了 點 手 腳 讓 同 名 的 函 式 偷 偷 地 變成 不 同 名 稱 , 以 下 面 同 名 的 三 個 函 式 為 例 : int Func(int X); int Func(float X); void Func(double *d); 使 用C++Builder 3.0 所 編 譯 出 來 的 函 式 名 稱 為 : ( 註2 ) @Func$qf @Func$qi @Func$qpd 而 使 用Visual C++ 6.0 所 編 譯 出 來 的 函 式 名 稱 為 : ( 註3 ) ?Func@@YAHH@Z ?Func@@YAHM@Z ?Func@@YAXPAN@Z 編 譯 器 這 個 偷 偷 修 改 函 式 名 稱 的 行 為 稱 為 『name mangling 』 , 但 除 了 函 式 名 稱 被 改 變 外 你 是 否 發 現 還 現 另 一 件 很嚴 重 的 事 情 , 就 是 同 的 編 譯 器 竟 然 有 著 不 同 的name mangling 做 法 , 這 表 示 著 若 咱 們 若 使 用Borland C++Builder 編 譯 器 來 開發 應 用 程 式 時 將 無 法 使 用 一 個 經 由Microsoft Visual C++ 所 編譯 器 完 成 的 函 式 庫 。 此 外 ,Naming Mangling 的 作 用 不 止 於 多載 的 函 式 上 ,C++ 程 式 中 所 有 的global 函 式 以 及class 中 所 有的 成 員 (members ) 都 會 被name mangling 這 個 動 作 給 整 型 一 下。 那 這 樣 不 就 沒 戲 唱 了 ! 若 要 在C++Builder 下 使 用Visual C++ 所 編 譯 的DLLs 豈 不 都 得 擁 有DLLs 的 原 始 碼 才 能 囉 ? 其 實 不然 , 有 方 法 可 抑 制name mangling 的 作 用 , 就 是 在 函 式 的 宣 告錢 加 上extern “C ” 這 個 修 飾 詞 , 強 制 將 函 式 以C 語 言 的 行台 重 現 , 而 非 以C++ 語 言 的 形 態 出 現 。 但 是 要 注 意 , 多 載函 式 可 不 能 加 上extern ”C ” 這 個 修 飾 詞 , 因 為 這 個 會 造 成一 堆 名 稱 相 同 的 函 式 , 若 你 硬 是 要 使 用extern “C ” 在 多 載含 上 , 編 譯 器 一 定 會 送 你 一 個ERROR ( 如 下 ) 做 獎 品 。 in C++Builder : [C++Error]Project1.cpp(13): Only one of s set of overloaded functions can be “C ”. in Visual C++ error C2733 : Second C linkage of overloaded function ‘Func ’ not allowed 因 此 若 拿 原 先 多 載 的 例 子 給 加 上extern “C ” : extern “C ” int Func(int X); 則 會 被 編 譯 器 給 編 譯 成 : in Visual C++ : _Func in C++Builder : _Func 似 乎 兩 個 編 譯 器 已 經 達 成 了 一 致 的 輸 出 。 嗯 ! 這 樣 一來 就 可 以 把DLLs 互 相 使 用 了 , 不 不 不 ! 還 沒 有 那 麼 簡 單 ,還 有 一 個 重 要 的 議 題 『Call Conventions 』 , 不 過 這 個 問 題 在此 先 不 提 , 放 到 後 頭 在 提 , 先 談 談 怎 樣 使 用C++Builder 來 建立DLLs 吧 !
建 立DLL 使 用C++Builder 來 建 立DLL 並 不 是 什 麼 難 事 , 只 需 要 按 下 幾下 滑 鼠 的 左 鍵 即 可 。 在C++Builder 下 建 立DLLs 大 致 分 成 兩 種方 法 , 先 按 選 單 上 的File|New 後 會 出 現New Items 對 話 盒 (Dialog ) ( 圖 四 ) :
按 完OK 或Finish 後 你 會 看 到DLL Project 與 些 許 程 式 碼 的 產 生, 這 段 由C++Builder 自 動 產 生 的 程 式 碼 中 分 兩 大 部 分 , 第 一部 份 是 個 很 長 一 串 的 註 解 , 最 後 就 是 所 謂 的DLL 進 入 點 。先 來 了 解 這 一 長 串 的 註 解 , 由 方 法 一 與 方 法 二 製 造 出 來的DLL Project 註 解 有 點 不 相 同 , 不 過 內 容 大 致 上 差 不 多 ,內 容 如 是 說 : 如 果 我 們 的DLL 內 使 用 到 了 字 串 物 件 如 :AnsiString , 或 是 在 輸 出 函 式 的 參 數 或 回 傳 值 使 用 到 長 字 串 的 話 ,就 必 須 加 入MEMMGR.LIB 這 個 函 式 庫 。 另 外 , 若 我 們 在 另 一 個模 組 ( 如DLL ) 中 使 用 了 例 如new 或GetMem 等 方 法 來 配 置 記 憶體 , 而 在 不 同 的 模 組 ( 如EXE 應 用 程 式 ) 中 使 用 了 這 塊 記憶 體 或 呼 叫FreeMem 等 方 法 來 釋 放 記 憶 體 , 則MEMMGR.LIB 也 是必 須 被 加 入 的 。 此 外 還 有 一 個 值 得 注 意 的 , 就 是MEMMGR.LIB 必 須 加 在 所 有 要 用 到 函 式 庫 的 最 前 頭 , 以 便 在 其 他 函 式庫 之 前 優 先 載 入 並 接 手 相 關 的 記 憶 體 維 護 。 同 時 要 記 住的 是 若 你 使 用 了MEMMGR.LIB 這 個 函 式 庫 , 那 麼 當 你 移 交DLL 或是 應 用 程 式 時 , 你 必 須 連 同BORLNDMM.DLL 一 併 移 交 給 使 用 者。 不 過 在 這 段 聲 明 的 倒 數 第 二 段 中 有 提 到 , 若 要 避 免 額外 的 檔 案 付 給 使 用 者 ( 越 多 的 檔 案 對 使 用 這 來 說 是 一 種負 擔 ) , 你 可 以 將 有 關 字 串 的 資 料 改 由char * 或 是shortstring 來 傳 送 , 這 樣 可 以 不 動 用 到BORLNDMM.DLL 與MEMMGR.LIB 來 作 記 憶體 的 配 置 。 另 外 , 聲 明 的 最 後 一 段 中 有 提 到 , 若 你 在 Project\Options 裡 頭 的Link 一 頁 勾 選 了Use Dynamic RTL 一 項 時 , 就 不 須 額 外 手動 將MEMMGR.LIB 給 加 到Project 裡 頭 了 , 因 為C++Builder 會 自 動 幫你 做 這 個 動 作 。 緊 接 著 咱 們 來 看 一 下DLL 的 進 入 點 DLL 的 進 入 點 :DLLMain 以 下 是 由 方 法 二 生 產 出 來 的DLL 原 始 碼 :
早 在 討 論 輸 入 輸 出 表 時 就 已 經 知 道 若 不 考 慮 到 各 個 編譯 器 間DLL 的 互 相 引 用 , 我 們 大 可 把 輸 出 函 式 這 麼 寫 : __declspec(dllexport) int MyFunc (void); 若 要 輸 出 整 個 類 別 則 可 以 這 樣 寫 : class __declspec(dllexport) MyClass : public TObject{...}; 但 是 注 意 , 這 若 這 樣 寫 僅 可 以 在 自 己 寫 作DLL 的 編 譯 器中 來 使 用 這 個DLL 了 , 並 沒 辦 法 達 到 其 他 編 譯 器 也 可 以 使用 的 目 的 , 大 大 的 抹 殺 了 軟 體 元 件 的 構 想 , 更 何 況DLL 是採 用 模 組 化 的 設 計 。 當 然 了 ! 類 別 的 輸 出 當 然 不 在 考 慮範 圍 內 , 但 若 對 函 式 的 輸 出 咱 們 還 是 乖 一 點 , 加 上extern “C ” 來 遏 止name mangling 對 我 們 函 式 名 稱 所 動 的 手 腳 : extern “C ” __declspec(dllexport) int MyFunc (void); 有 了 這 些 基 礎 知 識 後 , 咱 們 來 來 真 正 撰 寫 一 個 有 用 的DLL 試 試 看 吧 ! 那 要 做 些 什 麼 呢 ? 咱 們 就 做 個 簡 單 的 訊 息 視窗 即 可 , 怎 麼 做 , 其 實 說 穿 了 就 是 把Windows API MessageBox 給稍 微 包 裝 一 下 , 不 過 還 是 用MessageBox 這 個 函 式 來 實 作 內 容, 讀 者 們 可 能 覺 得 , 這 麼 無 聊 還 在 包 裝 一 次MessageBox 函 式幹 嘛 ? 這 個 嘛 ! 不 過 做 個 測 試 嘛 ! 先 使 用Consol Wizard 來 產 生DLL Project , 並 將 此Project 儲 存 為ShowMsg.bpr , 在DLL 的 進 入 點DllEntryPoint 之 後 加 上 以 下 的 程 式 碼 :
使 用DLLs 中 的 函 式 ─DLLs 的 載 入 知 道 了 怎 樣 建 立 起 一 個DLL 後 , 接 著 就 是 了 解 如 何 使 用DLL 裡 頭 所 提 供 的 函 式 的 時 候 了 , 在 使 用 這 些 函 式 之 前 還 必須 做 個 更 重 要 的 動 作 , 就 是 將DLL 給 載 入 。 載 入DLLs 的 方 法大 致 上 可 以 分 成 兩 個 : Implicit Linking 與Explicit Linking 。 Implicit Linking Implicitly Link ( 隱 式 聯 結 ) 又 稱 靜 態 載 入 , 所 謂 靜 態 載入 是 指 程 式 在 聯 結 時 期 即 與DLLs 所 對 應 的import libraries 做靜 態 鏈 結 , 於 是 可 執 行 檔 中 便 對 所 有 的DLL 函 式 都 有 一 份重 定 位 表 格(relocation table) 和 待 修 正 記 錄(fixup record) 。 當程 式 被Windows 載 入 器 載 入 記 憶 體 中 , 載 入 器 會 自 動 修 正 所有 的fixup records , 而 這 個fixup records 就 是 記 錄 由DLL 中 所 有輸 出 資 源 的 正 確 位 址 , 也 就 是 先 前 提 到 的RVA 加 上DLL 被 載入 的 基 底 位 址 , 經 過 這 樣 的 程 序 動 態 聯 結 便 順 利 產 生 。也 就 是 說 , 程 式 開 始 執 行 時 , 會 用 靜 態 載 入 方 式 所 使 用到 的DLLs 都 載 入 到 行 程 的 記 憶 體 裡 。 先 來 看 看 靜 態 載 入 放是 的 優 點 : 1 、 靜 態 載 入 方 式 所 使 用 到 的 這 個DLL 會 在 應 用 程 式 執行 時 載 入 , 然 後 就 可 以 呼 叫 出 所 有 由DLL 中 匯 出 的 函 式 ,就 好 像 是 包 含 在 程 式 中 一 般 。 2 、 動 作 較 為 簡 單 , 載 入 的 方 法 由 編 譯 器 負 責 處 理 ,咱 們 不 須 動 腦 筋 。 1 、 當 這 個 程 式 靜 態 載 入 方 式 所 使 用 到 的 這 個DLL 不 存在 時 , 這 個 程 式 在 開 始 時 就 出 現 無 法 找 到DLL 的 訊 息 而 導致 應 用 程 式 無 執 行 。 2 、 編 譯 時 需 要 加 入 額 外 的import library 。 3 、 若 是 要 載 入 的DLLs 一 多 , 載 入 應 用 程 式 的 速 度 會 便慢 。 4 、 若 遇 到 不 同 品 牌 的C++ 編 譯 器 時 , 靜 態 載 入 可 就 沒有 這 麼 簡 單 處 理 了 , 因 為 當 函 式 經 過Calling Conventions 的 處理 後 , 若 要 使 用 其 他 品 牌 編 譯 器 所 致 造 出 的DLL 須 得 大 動干 戈 才 行 。 以 先 前 建 立 的ShowMsg.DLL 為 例 子 , 我 們 已 知 這 個DLL 僅 輸出 一 個 函 式 :ShowMsg , 且 知 道 這 個 函 式 的 原 始 定 義 : extern “C ” __declspec(dllexport) int ShowMsg(char *pText,HWND hWnd); 因 此 , 若 我 們 要 載 入 這 個 函 式 則 必 須 在 應 用 程 式 中 加入 此 輸 入 函 式 的 宣 告 : extern “C ” __declspec(dllimport) int ShowMsg(char *pText,HWND hWnd); 此 外 , 還 要 加 上 這 個DLL 的import library File , 要 產 生import library 的 方 法 有 兩 個 : 1 、Project\Options 的Linker 中 的Geretate import library 勾 選 , 在正 常 情 況 下 , 預 設 值 是 勾 選 起 來 的 。 2 、 若 不 小 心 把lib 檔 案 給 刪 除 掉 了 , 也 可 以 利 用implib.exe 這 個C++Builder 所 附 上 的 工 具 來 產 生lib 檔 ,implib 是 文 字 模 式下 的 程 式 , 因 此 必 須 到 文 字 模 式 下 使 用 , 以 我 們 現 在 的例 子 來 說 , 使 用 方 式 為 :implib ShowMsg.lib ShowMsg.dll , 這 樣就 會 產 生ShowMsg.lib 檔 了 。 不 過 在 此 我 們 發 現 一 個 小 問 題 , 若 每 次 要 匯 入DLL 裡 頭的 函 式 , 還 必 須 把 函 式 的 原 始 定 義 給 抄 過 來 ( 雖 然 說 複製 ─ 貼 上 這 個 動 作 很 簡 單 ) , 但 有 沒 有 更 好 的 辦 法 呢 ?有 的 , 咱 們 可 以 在DLL 原 始 碼 的Header File 裡 動 點 手 腳 , 讓要 使 用DLL 的 應 用 程 式 只 需include 這 個Header File 就 可 以 了 ,怎 麼 做 呢 ? 就 是 使 用 前 置 處 理 符 號 , 若 是 使 用Borland C++ 或 是Borland C++Builder 來 編 譯DLL 都 必 須 加 上#define __DLL__ 這 個宣 告 , 但 是 我 們 從C++Builder 所 幫 我 建 立 的Project 裡 面 看 不到 這 個 , 原 因 是C++Builder 已 經 在Make file 裡 幫 我 們 加 上-WD 這個 編 譯 參 數 來 達 成#define __DLL__ 所 要 的 目 的 了 。 所 以 我 們可 以 把ShowMsg.h 給 改 成 : 程 式 列 表 一 、ShowMsg.h
Explicit Linking 而 所 謂Explicitly link ( 顯 式 聯 結 ) 又 稱 動 態 載 入 , 若 是使 用 動 態 載 入 就 是 需 要 時 才 載 入DLL , 然 後 在 使 用 過 後 即釋 放DLL , 嗯 ! 似 乎 是 很 不 錯 的 選 擇 , 這 種 方 法 的 優 點 有: 1 、DLL 只 要 需 要 時 才 會 載 入 到 記 憶 體 中 , 可 以 更 有 效的 使 用 記 憶 體 。 2 、 應 用 程 式 載 入 的 速 度 較 使 用 隱 式 鏈 結 時 快 , 因 為當 程 式 開 始 載 入 時 並 不 需 要 把DLL 給 載 入 到 行 程 中 。 3 、 編 譯 時 不 須 額 外 的import library 檔 。 4 、 讓 我 們 可 以 更 清 楚DLL 的 載 入 流 程 。 Explicit Linking 範 例 : 咱 們 就 拿 一 個 常 見 的DLL ─ 控 制TWAIN32 界 面 的DLL : 『Eztw32.dll 』 來 作 動 態 載 入 的 示 範 : eztw32.dll 裡 頭 有 四 十 多 個 輸 出 函 式 可 以 使 用 , 不 過 只有 以 下 這 四 個 函 式 是 我 們 所 要 用 到 的 : 1 、void __stdcall TWAIN_SelectImageSource(HWND hwnd); 功 能 : 用 來 選 擇 所 要 使 用 的TWAIN 介 面 裝 置 2 、int __stdcall TWAIN_AcquireToClipboard(HWND hwnd, unsigned int pixmask); 功 能 : 經 由TWAIN 介 面 將 資 料 置 放 到 剪 貼 簿 中 3 、int __stdcall TWAIN_LoadSourceManager(void); 功 能 : 呼 叫TWAIN 介 面 裝 置 程 式 4 、int __stdcall TWAIN_UnloadSourceManager(void); 功 能 : 關 閉TWAIN 介 面 裝 置 程 式 再 度 使 用tdump 來 觀 察eztw32.dll 裡 頭 的 輸 出 函 式 表 : Exports from EZTW32.dll 50 exported name(s), 50 export addresse(s). Ordinal base is 1. Ordinal RVA Name ------- -------- ---- 0000 00001000 DllMain ... 0003 000012c0 TWAIN_AcquireToClipboard ... 0029 00001650 TWAIN_LoadSourceManager ... 0038 000010a0 TWAIN_SelectImageSource ... 0046 00001980 TWAIN_UnloadSourceManager ... 先 確 認 函 式 的 名 稱 以 及 函 式 的 序 號 , 等 一 下 會 用 到 。 接 著 就 是 開 始 將DLL 載 入 了 ,Win32 API 有 兩 個 函 式 提 供 了將DLL 載 入 的 功 能 , 分 別 是LoadLibrary 與LoadLibraryEx 。 通 常 都使 用LoadLibrary , 先 看 看LoadLibrary 的 原 始 定 義 : HINSTANCE LoadLibrary( LPCTSTR lpLibFileName // address of filename of executable module ); 嗯 ! 只 需 要 傳 入 檔 案 名 稱 即 可 , 若 以 我 們 所 要 使 用 的Eztw32.dll 為 例 : HINSTANCE hDLL; hDll = LoadLibrary( “Eztw32.dll ”); 若 我 們 沒 有 指 定 副 檔 名 , 則 自 動 會 以 『.DLL 』 為 副 檔 名, 或 許 會 感 到 納 悶 , 那 我 還 沒 有 指 定DLL 的 路 徑 啊 ! 正 確的 做 法 應 該 是 要 指 定 路 徑 的 , 但 是 天 曉 得 使 用 者 會 把DLL 給 放 到 哪 裡 去 呢 ? 所 以 使 用LoadLibrary 這 個 函 式 時 , 若 參數 中 沒 有 指 明 路 徑 , 系 統 會 依 特 定 的 次 序 來 找 尋DLL 的 存在 與 否 , 若 不 存 在 則LoadLibrary 函 式 則 會 回 傳NULL , 以 下 就是 搜 尋 次 序 : 1 、 被 執 行 的 應 用 程 式 所 存 在 的 路 徑 。 2 、 目 前 的 目 錄 。 3 、Windows 系 統 目 錄 , 對Windows 95/98 說 是Windows\System , 而Windows NT 則 是Winnt\System32 。 目 錄 名 稱 可 以 使 用GetSystemDirectory 這個API 來 取 得 。 4 、Windows 目 錄 。 目 錄 名 稱 可 以 使 用GetWindowsDirectory 這 個API 來 取 得 。 5 、 最 後 由 設 定 的PATH 環 境 變 數 來 尋 找 。 把DLL 載 入 記 憶 體 後 最 重 要 的 工 作 就 是 把 函 式 指 標 指 向函 式 在 記 憶 體 中 正 確 的 位 址 , 要 做 到 這 個 動 作 得 透 過GetProcAddress 這 個API 來 幫 忙 : FARPROC GetProcAddress( HMODULE hModule, // handle to DLL module LPCSTR lpProcName // name of function ); GetProcAddres 函 式 的 第 一 個 參 數 是 經 由LoadLibrary 所 取 得 的DLL 的Handle , 而 第 二 個 參 數 是 函 式 的 名 稱 或 是 函 式 的 輸 出 序號 經 由 函 式 名 稱 取 得 函 式 的 指 標 , 以eztw32.dll 中 的TWAIN_SelectImageSource 函 式 為 例 , 應 由 函 式 名 稱 取 得 函 式 的 位 址 的 方 法 為 : : GetProcAddress(hDLL, ”TWAIN_SelectImageSource ”); 若 經 由 函 式 輸 出 序 號 取 得 函 式 的 位 址 則 為 : GetProcAddress(hDLL, MAKEINTRESOURCE (39)); 在 此 要 注 意 序 號 的 起 始 值 是1 不 是0 , 經 由 tdump 所 列 出來 的Ordinal 是 由 起 始 值 開 始 的 位 移 植 , 而Ordinal Base 為1 ,因 此TWAIN_SelectImageSource 的 序 號 是39 而 不 是 38 。 接 著 在 此 先 複 習 一 下 函 式 指 標 的 使 用 方 式 , 一 樣TWAIN_SelectImageSource 函 式 為 例 ,TWAIN_SelectImageSource 函 式 的 原 始 定 義 為 : void __stdcall TWAIN_SelectImageSource(HWND hwnd); 那 就 可 以 用 : void (__stdcall *TWAIN_SelectImageSource)(HWND hwnd); 來 宣 告TWAIN_SelectImageSource 為 一 個 函 式 指 標 , 之 後 再 用: TWAIN_SelectImageSource = (void (__stdcall *)(HWND hwnd)) GetProcAddress(hDLL, ”TWAIN_SelectImageSource ”); typedef void (__stdcall *_TWAIN_SelectImageSource)(HWND hwnd); _TWA IN_SelectImageSource TWAIN_SelectImageSource; TWAIN_SelectImageSource = (_TWAIN_SelectImageSource) GetProcAddress(hDLL, ”TWAIN_SelectImageSource ”); 先 使 用typedef 把_TWAIN_SelectImageSource 給 定 義 成 一 個 特 殊 的型 別 , 之 後 就 可 以 直 接 引 用 , 的 確 是 可 以 少 打 點 字 。 最 後 當DLL 裡 頭 的 函 式 不 再 需 要 使 用 時 , 咱 們 就 得 使 用FreeLibrary 將DLL 從 記 憶 體 裡 頭 卸 下 來 : BOOL FreeLibrary( HMODULE hLibModule // handle to loaded library module ); 使 用 方 法 很 簡 單 只 需 將LoadLibrary 所 傳 回 來 的DLL Handle 當參 數 傳 給FreeLibrary 傳 入 即 可 。 懂 得 這 些 動 態 載 入DLL 的 流 程 後 , 就 可 以 實 際 動 手 來 做做 看 。 筆 者 發 現TWAIN32 這 些 功 能 實 在 很 適 合 包 裝 成 一 個 物件 , 當 物 件 誕 生 時 , 立 即 自 動 去LoadLibrary 並 將 函 式 指 標的 位 址 給 連 結 起 來 , 當 這 個 物 件 被 摧 毀 時 , 就 自 動 去FreeLibrary , 嗯 ! 似 乎 不 錯 , 不 過 詳 細 的 做 法 就 不 多 做 解 釋 了 , 相信 讀 者 看 了 下 面 的 程 式 列 表 應 該 就 懂 了 。 程 式 列 表 三 、CTWAIN.h :
1 、void SelectSource(HWND hWnd); 選 擇TWAIN32 設 備 的 來 源 2 、void Acquire(HWND hWnd); 經 由 選 定 的TWAIN32 設 備 來 源 將 資 料 取 得
Calling Conventions 因 為 不 同 的 語 言 間 有 不 同 的 傳 遞 參 數 的 方 法 , 而C/C++ 編 譯 器 為 了 能 夠 使 用 由 其 他 語 言 開 發 出 來 的 函 式 庫 加 上了 這 些 參 數 傳 遞 方 式 不 同 的 方 式 稱 為calling conventions ( 呼叫 慣 例 ) , 如 在C++Builder 裡 頭 常 常 看 到 的__fastcall ; 一 般常 用 的 呼 叫 慣 例 有 以 下 四 種 :
但 光 是 說 說 很 難 了 解 到 這 些 參 數 傳 遞 方 式 有 何 異 同 ,以 下 函 式 分 別 使 用__cdecl 、__stdcall 與__fastcall 三 種 呼 叫 慣例 當 做 範 例 : void calltype MyFunc(char c, shorty s, int i , double f); 當 我 使 用 這 個 函 式 : MyFunc( “x ”,12,8192,2.7183); 時 會 被 編 譯 器 編 譯 成 如 以 下 四 圖 :
由 圖 八 與 圖 九 中 可 以 看 出__fastcall 在 兩 個 編 譯 器 中 有 顯著 的 不 同 ,C++Builder 使 用 了 三 個 暫 存 器 來 存 放 參 數 , 讓 傳遞 的 速 度 更 為 加 快 , 這 也 就 是VCL 類 別 中 的 預 設 呼 叫 慣 例為__fastcall 的 原 因 。 暫 且 撇 開__fastcall 的 不 同 , 呼 叫 慣 例造 成 的 還 不 只 這 一 樣 差 異 , 還 有 著 與name mangling 有 點 類 似的 麻 煩 , 就 是 函 式 的 名 稱 更 動 問 題 。 當 你 使 用 不 同 的 呼叫 慣 例 時 , 編 譯 器 還 是 對 動 點 手 腳 , 動 什 麼 手 腳 , 筆 者用 以 下 的 範 例 做 觀 察 , 先 定 義 四 個 函 式 分 別 使 用__fastcall 、__stdcall 與__cdecl 與 不 指 定 四 種 呼 叫 慣 例 :
)
經 由 上 述 簡 單 的 實 驗 可 以 將 兩 者 的 差 異 列 出 , 並 且 可以 找 出 編 譯 器 預 設 的 呼 叫 慣 例 :
由 表 上 可 以 看 出 兩 者 間 的 差 異 還 不 少 呢 ! 而 為 什 麼 要討 論 到 這 一 點 呢 ? 因 為 接 著 就 要 討 論 到 如 何 拿Visual C++ 所編 譯 的DLL 到C++Builder 裡 頭 使 用 。 在Borland C++Builder 下 使 用Microsoft Visual C++ 所 編 譯 的DLLs 若 已 經 解 決 了name mangling 與calling convention 的 理 想 狀 況 下, 由C++Builder 下 來 呼 叫Visual C++ 所 編 譯 出 來 的DLLs 應 該 不 是難 事 才 對 。 但 不 幸 的 , 只 對 了 一 半 , 怎 麼 說 , 幸 運 的 那一 半 是 , 咱 們 可 以 利 用 先 前 提 過 的 『 動 態 載 入 』 方 式 來載 入DLLs 中 的 函 式 , 即 使 因 為calling convention 的 問 題 導 致 函式 名 稱 在 編 譯 後 會 被 更 動 , 但 是 只 要 知 道 函 式 的 輸 出 序號 就 可 以 照 樣 載 入 ; 不 幸 的 那 一 半 是 若 採 用 『 靜 態 載 入』 的 方 法 就 又 會 遇 上 了 個 大 難 題 :Borland 與Microsoft 所 使 用的OBJs 檔 案 格 式 不 相 同 ;Borland 採 用Intel 所 訂 定 的OMF (Object Module Format ) 格 式 而Microsoft 採 用COFF (Common Object File Format ) 格 式 , 因 此 若 要 拿Visual C++ 所 編 譯 的DLLs 與LIBs 來 使 用 ,僅 有DLLs 能 夠 用 而 已 ,LIBs 毫 無 用 武 之 地 , 但 謝 天 謝 地 ,Borland 提 供 了 一 個 工 具IMPLIB.EXE , 可 以 直 接 從 任 何 編 譯 器 所 編 譯出 的DLLs 裡 頭 將OMF 格 式 的LIBs 給 製 造 出 來 。 因 此 製 造C++Builder 相 容 的OMF 格 式LIBs 不 算 是 個 大 問 題 了 。 但 對 於Calling Convention 所 產 生 的 問 題 就 比 較 麻 煩 些 , 接 下 來 咱 們 就 來 討 論 如 何使 用 靜 態 載 入 來 載 入Visual C++ 所 製 造 出 來 的DLLs 。 要 將Visual C++ 所 製 造 出 了DLLs 搬 到C++Builder 來 用 大 致 上 分三 個 步 驟 : A 、 檢 驗 輸 出 函 式 的 設 呼 叫 慣 例 由 先 前 呼 叫 慣 例 的 實 驗 裡 看 得 出 來 , 兩 種 編 譯 器 的 預設 呼 叫 慣 例 皆 為__cdecl , 而__cdecl 與__stdcall 的 參 數 傳 遞 方式 兩 個 編 譯 器 也 相 同 , 但 是 在C++Builder 下__fastcall 為VCL 的標 準 呼 叫 慣 例 , 而 且 為 了 加 速 參 數 的 傳 遞 , 參 數 傳 遞 的方 法 與Visual C++ 不 同 , 若 是 硬 搬 到C++Builder 來 使 用 , 恐 有問 題 出 現 ; 因 此 若 要 拿Visual C++ 所 編 譯 的DLL 來 使 用 , 切 記只 能 使 用__cdecl 與__stdcall 這 兩 種 呼 叫 慣 例 。 B 、 查 驗 經 過 編 譯 器 編 譯 後 的 函 式 名 稱 由 呼 叫 慣 例 的 實 驗 結 果 裡 看 出 ,__cdecl 與__stdcall 在 兩 個編 譯 器 下 所 編 譯 出 的 正 式 名 稱 稍 有 不 同 , 以 一 個 簡 單 的 void MyFunction(void); 為 例 :
我 們 必 須 檢 查 看 看 哪 些 函 式 是 使 用__stdcall 而 哪 些 函 式是 使 用__cdecl , 接 下 來 下 一 個 步 驟 就 是 轉 換 這 些 名 稱 , 將名 稱 由C++Builder 不 認 得 變 成 認 得 。 C 、 製 作OMF 格 式 的LIBs 由 於 經 過 編 譯 器 處 理 過 後 的 函 式 名 稱 已 經 與 原 先 函 式名 稱 不 相 同 了 , 因 此 若 直 接 轉 換 成LIBs 檔 也 無 啥 效 用 , 必須 動 點 手 腳 。 先 前 已 經 學 過IMPDEF 的 使 用 方 法 , 先 前 是 用在 觀 察 輸 出 函 式 , 現 在 也 是 , 但 還 多 了 動 手 腳 的 部 份 。 利 用 先 前 測 試 的 那 個VC6TEST.DLL 來 製 造DEF 檔 案 : (__fastcall 的 呼 叫 慣 例 部 份 記 得 要 先 除 去 ) IMPDEF VC6TEST.DEF VC6TEST.DLL 製 造 出 來 的DEF 檔 : LIBRARY VC6TEST.DLL EXPORTS MyFunc_Cdecl @1 MyFunc_Default @2 _MyFunc_Std@8 =_MyFunc_Std @3 這 時 候 我 們 就 來 動 手 腳 , 把C++Builder 不 認 得 給 改 承 認 得的 。 改 成 如 下 : EXPORTS ;use this type of aliasing ;(Borland name) = (Name exported by Visual C++) MyFunc_Std = _MyFunc_Std@8 _MyFunc_Cdecl = MyFunc_Cdecl _MyFunc_Default = MyFunc_Default 最 後 再 將DEF 檔 給 還 原 成LIB 檔 , 什 麼 ? 你 有 沒 有 說 錯 把DEF 這 個 文 字 檔 變 回LIB 檔 ? 是 的 , 其 實 靜 態 載 入 所 需 要 用 到的LIB 檔 案 只 是 個 函 式 表 格 罷 了 , 並 沒 有 真 正 函 式 內 容 在裡 頭 , 因 此 我 們 可 以 經 由 動 過 手 腳 的DEF 檔 給 還 原 成LIB 檔: IMPLIB VC6TEST.lib VC6TEST.def 動 完 手 腳 後 不 忘 還 要 去 檢 查 一 下 是 否 函 式 輸 出 , 可 以使 用TLIB 來 觀 察 最 後 的test.lib 是 否 正 確 輸 出 : TLIB VC6TEST.lib,VC6TEST.txt Publics by module _MyFunc_cdecl size = 0 _MyFunc_cdecl _MyFunc_default size = 0 _MyFunc_default MyFunc_std size = 0 MyFunc_std 可 以 由 觀 察test.txt 來 檢 驗 輸 出 的 正 確 與 否 , 在 確 定 輸出 名 稱 無 誤 後 就 可 以 直 接 拿 進C++Builder 做 先 前 已 經 說 明 過的 靜 態 載 入 測 試 了 。 我 的 測 試 方 法 為 建 立 一 個Console Application 來 載 入 這 個DLL 做 測 試 。
輸 出 結 果 : int x = MyFunc_std("x",123); Result : x = 123 int y = MyFunc_cdecl("x",2323); Result : y = 2323 這 樣 一 來 算 大 功 告 成 , 但 在 筆 者 的 經 驗 中 使 用Visual C++ 來 寫 作DLL 供Borland C++Builder 使 用 , 有 以 下 幾 點 是 值 得 注 意的 :
結 語 這 次 筆 者 大 多 著 墨 於DLLs 的 使 用 , 怎 麼 建 立DLLs 的 方 法卻 只 大 略 一 題 , 因 為 製 造DLLs 的 方 法 有 很 多 , 也 有 很 多 的技 巧 , 若 把 這 些 通 通 都 給 寫 出 來 , 可 能 這 個 主 題 就 要 變成 連 載 小 說 般 分 成 好 幾 期 來 刊 載 了 , 一 期 為 時 一 個 月 ,討 論 這 個 主 題 的 時 間 會 拖 得 太 久 , 且 相 差 時 間 會 過 久 ,造 成 讀 者 的 無 法 連 貫 , 筆 者 只 好 忍 痛 割 捨 , 不 過 筆 者 在此 還 是 列 出 數 本 對 於 製 作DLLs 一 題 著 墨 相 當 詳 細 的 書 籍 : 1 、Adcanced Windows 3/E, Microsoft Press, Jeffrey Richter Chapter 12 2 、Programming Windows 95, Microsoft Press, Charles Petzold Chapter 19 3 、Multithreading Application in Win32, Addison Wesley, JimBeveridge, etc . Chapter 14 4 、Borland C++Builder 3 Unleashed, Sams Publishing, Charlie Calvert, etc. Chapter 35 註1 、PE 格 式 的 執 行 檔 :PE ,Portable Executable , 是Microsoft 設 計 用 於 其 所 有Win32 作 業 系 統 (Win32s 、Windows NT 及Windows 95/98 ) 的 可 執 行 檔 格 式 。 註2 、 在C++Builder 下 觀 看 編 譯 器 產 生 的 真 正 名 稱 方 法 :
註3 、 在Visual C++ 下 觀 看 編 譯 器 產 生 的 真 正 名 稱 方 法 :
參 考 資 料 、
|
Wednesday, July 12, 2006
BCB - VC compatibility : 探訪動態連結函式庫
探訪動態連結函式庫
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment