作 為 一 種 簡(jiǎn) 單 易 用 的Windows 開(kāi) 發(fā) 環(huán) 境,Visual Basic 從 一 推 出 就 受 到 了 廣 大 編 程 人 員 的 歡 迎。 它 使 程 序 員 不 必 再 直 接 面 對 紛 繁 復 雜 的Windows 消 息, 而 可 以 將 精 力 主 要 集 中 在 程 序 功 能 的 實(shí) 現 上, 大 大 提 高 了 編 程 效 率。 但 凡 事 有 利 必 有 弊。VB 中 高 度 的 封 裝 和 模 塊 化 減 輕 了 編 程 者 的 負 擔, 同 時(shí) 也 使 開(kāi) 發(fā) 人 員 失 去 了 許 多 訪(fǎng) 問(wèn) 低 層API 函 數 和 直 接 與Windows 交 互 的 機 會(huì )。 因 此, 相 比 而 言,VB 應 用 程 序 的 執 行 效 率 和 功 能 比C/C++ 或Delphi 生 成 的 程 序 要 差。 為 了 解 決 這 個(gè) 問(wèn) 題, 在 一 個(gè) 大 型 的VB 開(kāi) 發(fā) 應 用 中, 直 接 調 用Windows API 函 數 幾 乎 是 不 可 避 免 的; 同 時(shí), 還 有 可 能 需 要 程 序 員 自 己 用C/C++ 等 開(kāi) 發(fā) 一 些 動(dòng) 態(tài) 連 接 庫, 用 于 在VB 中 調 用。 本 文 主 要 討 論 在32 位 開(kāi) 發(fā) 環(huán) 境Visual Basic 5.0 中 直 接 調 用Windows 95 API 函 數 或 用 戶(hù) 生 成 的32 位 動(dòng) 態(tài) 連 接 庫 的 方 法 與 規 則。
---- Windows 動(dòng) 態(tài) 連 接 庫 是 包 含 數 據 和 函 數 的 模 塊, 可 以 被 其 它 可 執 行 文 件(EXE、DLL、OCX 等) 調 用。 動(dòng) 態(tài) 連 接 庫 包 含 兩 種 函 數: 輸 出(exported) 函 數 和 內 部(internal) 函 數。 輸 出 函 數 可 以 被 其 它 模 塊 調 用, 而 內 部 函 數 則 只 能 在 動(dòng) 態(tài) 連 接 庫 內 部 使 用。 盡 管 動(dòng) 態(tài) 連 接 庫 也 能 輸 出 數 據, 但 實(shí) 際 上 它 的 數 據 通 常 是 只 在 內 部 使 用 的。 使 用 動(dòng) 態(tài) 連 接 庫 的 優(yōu) 點(diǎn) 是 顯 而 易 見(jiàn) 的。 將 應 用 程 序 的 一 部 分 功 能 提 取 出 來(lái) 做 成 動(dòng) 態(tài) 連 接 庫, 不 但 減 小 了 主 應 用 程 序 的 大 小, 提 高 了 程 序 運 行 效 率, 還 使 它 更 加 易 于 升 級。 多 個(gè) 應 用 程 序 共 享 一 個(gè) 動(dòng) 態(tài) 連 接 庫 還 能 有 效 地 節 省 系 統 資 源。 正 因 為 如 此, 在Windows 系 統 中, 動(dòng) 態(tài) 連 接 庫 得 到 了 大 量 的 使 用。
---- 一 般 來(lái) 說(shuō), 動(dòng) 態(tài) 連 接 庫 都 是 以DLL 為 擴 展 名 的 文 件, 如Kernel32.dll 、commdlg.dll 等。 但 也 有 例 外, 如16 位Windows 的 核 心 部 件 之 一GDI.exe 其 實(shí) 也 是 一 個(gè) 動(dòng) 態(tài) 庫。 編 寫(xiě) 動(dòng) 態(tài) 連 接 庫 的 工 具 很 多, 如Visual C++、Borland C++、Delphi 等, 具 體 方 法 可 以 參 見(jiàn) 相 關(guān) 文 檔。 下 面 只 以Visual C++ 5.0 為 例, 介 紹 一 下 開(kāi) 發(fā) 應 用 于Visual Basic 5.0 的 動(dòng) 態(tài) 連 接 庫 時(shí) 應 注 意 的 問(wèn) 題( 本 文 中 所 有 涉 及C/C++ 語(yǔ) 言 或 編 譯 環(huán) 境 的 地 方, 都 以VC5 為 例; 所 有 涉 及Visual Basic 的 地 方 都 以VB5 為 例)。
---- 作 為 一 種32 位Windows 應 用 程 序 的 開(kāi) 發(fā) 工 具,VB5 生 成 的exe 文 件 自 然 也 都 是32 位 的, 通 常 情 況 下 也 只 能 調 用32 位 的 動(dòng) 態(tài) 連 接 庫。 但 是, 并 不 是 所 有 的32 位 動(dòng) 態(tài) 庫 都 能 被VB 生 成 的exe 文 件 正 確 地 識 別。 一 般 來(lái) 說(shuō), 自 己 編 寫(xiě) 用 于VB 應 用 程 序 調 用 的 動(dòng) 態(tài) 連 接 庫 時(shí), 應 注 意 以 下 幾 個(gè) 方 面 的 問(wèn) 題:
---- 1、 生 成 動(dòng) 態(tài) 庫 時(shí) 要 使 用__stdcall 調 用 約 定, 而 不 能 使 用 缺 省 的__cdecl 調 用 約 定;__stdcall 約 定 通 常 用 于32 位API 函 數 的 調 用。
---- 2、 在VC5 中 的 定 義 文 件(.def) 中, 必 須 列 出 輸 出 函 數 的 函 數 名, 以 強 制VC5 系 統 將 輸 出 函 數 的 裝 飾 名(decorated name) 改 成 普 通 函 數 名; 所 謂 裝 飾 名 是VC 的 編 譯 器 在 編 譯 過(guò) 程 中 生 成 的 輸 出 函 數 名, 它 包 含 了 用 戶(hù) 定 義 的 函 數 名、 函 數 參 數 及 函 數 所 在 的 類(lèi) 等 多 方 面 的 信 息。 由 于 在VC5 中 定 義 文 件 不 是 必 需 的, 因 此 工 程 不 包 含 定 義 文 件 時(shí)VC5 就 按 自 己 的 約 定 將 用 戶(hù) 定 義 的 輸 出 函 數 名 修 改 成 裝 飾 名 后 放 到 輸 出 函 數 列 表 中, 這 樣 的 輸 出 函 數 在VB 生 成 的 應 用 程 序 中 是 不 能 正 確 調 用 的( 除 非 聲 明 時(shí) 使 用Alias 子 句)。 因 此 需 要 增 加 一 個(gè).def 文 件, 其 中 列 出 用 戶(hù) 需 要 的 函 數 名, 以 強 制VC5 不 按 裝 飾 名 進(jìn) 行 輸 出。
---- 3、VC5 中 的 編 譯 選 項" 結 構 成 員 對 齊 方 式(structure member alignment)" 應 設 成4 字 節, 其 原 因 將 在 后 文 詳 細 介 紹。
---- 4、 由 于 在C 中 整 型 變 量 是4 個(gè) 字 節, 而VB 中 的 整 型 變 量 依 然 只 有2 個(gè) 字 節, 因 此 在C 中 聲 明 的 整 型(int) 變 量 在VB 中 調 用 時(shí) 要 聲 明 為 長(cháng) 整 型(long), 而C 中 的 短 整 型(short) 在VB 中 則 要 聲 明 成 整 型(integer); 下 表 針 對 最 常 用 的 C 語(yǔ) 言 數 據 類(lèi) 型 列 出 了 與 之 等 價(jià) 的 Visual Basic 類(lèi) 型( 用 于 32 位 版 本 的 Windows)。
---- C 語(yǔ) 言 數 據 類(lèi) 型 在Visual Basic 中 聲 明 為 調 用 時(shí) 使 用 的 表 達 式
---- ATOM ByVal variable As Integer 結 果 為Integer 類(lèi) 型 的 表 達 式
---- BOOL ByVal variable As Long 結 果 為 Long 類(lèi) 型 的 表 達 式
---- BYTE ByVal variable As Byte 結 果 為 Byte 類(lèi) 型 的 表 達 式
---- CHAR ByVal variable As Byte 結 果 為 Byte 類(lèi) 型 的 表 達 式
---- COLORREF ByVal variable As Long 結 果 為 Long 類(lèi) 型 的 表 達 式
---- DWORD ByVal variable As Long 結 果 為 Long 類(lèi) 型 的 表 達 式
---- HWND, HDC, HMENU ByVal variable As Long 結 果 為 Long 類(lèi) 型 的 表 達 式 等Windows 句 柄
---- INT, UINT ByVal variable As Long 結 果 為 Long 類(lèi) 型 的 表 達 式
---- LONG ByVal variable As Long 結 果 為 Long 類(lèi) 型 的 表 達 式
---- LPARAM ByVal variable As Long 結 果 為 Long 類(lèi) 型 的 表 達 式
---- LPDWORD variable As Long 結 果 為 Long 類(lèi) 型 的 表 達 式
---- LPINT, LPUINT variable As Long 結 果 為 Long 類(lèi) 型 的 表 達 式
---- LPRECT variable As type 自 定 義 類(lèi) 型 的 任 意 變 量
---- LPSTR, LPCSTR ByVal variable As String 結 果 為 String 類(lèi) 型 的 表 達 式
---- LPVOID variable As Any 任 何 變 量( 在 傳 遞 字 符 串 的 時(shí) 候 使 用ByVal)
---- LPWORD variable As Integer 結 果 為Integer 類(lèi) 型 的 表 達 式
---- LRESULT ByVal variable As Long 結 果 為 Long 類(lèi) 型 的 表 達 式
---- NULL As Any 或 ByVal Nothing 或
---- ByVal variable As Long ByVal 0& 或 VBNullString
---- SHORT ByVal variable As Integer 結 果 為Integer 類(lèi) 型 的 表 達 式
---- VOID Sub procedure 不 可 用
---- WORD ByVal variable As Integer 結 果 為Integer 類(lèi) 型 的 表 達 式
---- WPARAM ByVal variable As Long 結 果 為 Long 類(lèi) 型 的 表 達 式
---- 5、VB 中 進(jìn) 行32 位 動(dòng) 態(tài) 庫 的 聲 明 時(shí), 函 數 名 是 大 小 寫(xiě) 敏 感 的。 在 獲 得 了 需 要 的 動(dòng) 態(tài) 連 接 庫 之 后, 就 可 以 在VB 中 進(jìn) 行 調 用 了。 但 是, 由 于VB 不 能 驗 證 應 用 程 序 傳 遞 到 動(dòng) 態(tài) 連 接 庫 中 的 參 數 值 是 否 正 確, 因 此VB 程 序 中 大 量 的API 調 用 可 能 會(huì ) 降 低 整 個(gè) 應 用 程 序 的 穩 定 性, 也 會(huì ) 增 加 以 后 維 護 的 難 度。 所 以, 決 定 在VB 程 序 中 直 接 調 用API 函 數 時(shí) 要 慎 重, 但 適 當 的 使 用API 調 用 確 實(shí) 能 夠 有 效 地 提 高VB 程 序 的 性 能。 這 之 間 的 平 衡 需 要 編 程 人 員 根 據 實(shí) 際 情 況 來(lái) 掌 握。 下 面 就 具 體 介 紹 一 下 在VB 中 調 用API 函 數 時(shí) 需 要 做 的 工 作。
---- 要 聲 明 一 個(gè)DLL 過(guò) 程, 首 先 需 要 在 代 碼 窗 口 的" 通 用(General)" 部 分 增 加 一 個(gè)Declare 語(yǔ) 句。 如 果 該 過(guò) 程 返 回 一 個(gè) 值, 應 將 其 聲 明 為Function:
---- Declare Function publicname Lib "libname" [Alias "alias"] [([[ByVal] variable [As type] [,[ByVal] variable [As type]]...])] As Type
---- 如 果 過(guò) 程 沒(méi) 有 返 回 值, 可 將 其 聲 明 為Sub:
---- Declare Sub publicname Lib "libname" [Alias "alias"] [([[ByVal] variable [As type] [,[ByVal] variable [As type]]...])]
---- 缺 省 情 況 下, 在 標 準 模 塊 中 聲 明 的DLL 過(guò) 程, 可 以 在 應 用 程 序 的 任 何 地 方 調 用 它。 在 其 它 類(lèi) 型 的 模 塊 中 定 義 的DLL 過(guò) 程 則 是 模 塊 私 有 的, 必 須 在 它 們 前 面 聲 明Private 關(guān) 鍵 字, 以 示 區 分。 下 面 分 別 介 紹 聲 明 語(yǔ) 句 的 各 個(gè) 組 成 部 分。
---- 1、 指 定 動(dòng) 態(tài) 庫:
---- Declare 語(yǔ) 句 中 的 Lib 子 句 用 來(lái) 告 訴 Visual Basic 如 何 找 到 包 含 過(guò) 程 的 .dll 文 件。 如 果 引 用 的 過(guò) 程 屬 于 Windows 核 心 庫(User32、Kernel32 或 GDI32), 則 可 以 不 包 含 文 件 擴 展 名, 如:
---- Declare Function GetTickCount Lib "kernel32" Alias "GetTickCount" () As Long
---- 對 于 其 它 動(dòng) 態(tài) 連 接 庫, 可 以 在Lib 子 句 指 定 文 件 的 路 徑:
---- Declare Function lzCopy Lib "c:windowslzexpand.dll" _
---- (ByVal S As Integer, ByVal D As Integer) As Long
---- 如 果 未 指 定 libname 的 路 徑,Visual Basic 將 按 照 下 列 順 序 查 找 該 文 件:
---- ①.exe 文 件 所 在 的 目 錄
---- ② 當 前 目 錄
---- ③Windows 系 統 目 錄
---- ④Windows 目 錄
---- ⑤Path 環(huán) 境 變 量 中 的 目 錄
---- 下 表 中 列 出 了 常 用 的 操 作 系 統 環(huán) 境 庫 文 件。
---- 動(dòng) 態(tài) 鏈 接 庫 描 述
---- Advapi32.dll 高 級 API 服 務(wù), 支 持 大 量 的 API( 其 中 包 括 許 多 安 全 與 注 冊 方 面 的 調 用)
---- Comdlg32.dll 通 用 對 話(huà) 框 API 庫
---- Gdi32.dll 圖 形 設 備 接 口 API 庫
---- Kernel32.dll Windows 32 位 核 心 的 API 支 持
---- Lz32.dll 32 位 壓 縮 例 程
---- Mpr.dll 多 接 口 路 由 器 庫
---- Netapi32.dll 32 位 網(wǎng) 絡(luò ) API 庫
---- Shell32.dll 32 位 Shell API 庫
---- User32.dll 用 戶(hù) 接 口 例 程 庫
---- Version.dll 版 本 庫
---- Winmm.dll Windows 多 媒 體 庫
---- Winspool.drv 后 臺 打 印 接 口, 包 含 后 臺 打 印 API 調 用。
---- 對 于Windows 的 系 統API 函 數, 可 以 利 用VB 提 供 的 工 具API Viewer 查 找 某 一 函 數 及 其 相 關(guān) 數 據 結 構 和 常 數 的 聲 明, 并 復 制 到 自 己 的 程 序 中。
---- 2、 使 用 別 名:
---- Declare 語(yǔ) 句 中 的Alias 子 句 是 一 個(gè) 可 選 的 部 分, 用 戶(hù) 可 以 通 過(guò) 它 所 標 識 的 別 名 對 動(dòng) 態(tài) 庫 中 的 函 數 進(jìn) 行 引 用。 例 如, 在 下 面 的 語(yǔ) 句 中, 聲 明 了 一 個(gè) 在VB 中 名 為MyFunction 的 函 數, 而 它 在 動(dòng) 態(tài) 庫Mydll.dll 中 最 初 的 名 字 是MyFunctionX。
---- Private Declare Function MyFunction Lib "Mydll.dll" _
---- Alias "MyFunctionX" ( ) As Long
---- 需 要 注 意 的 是,Alias 子 句 中 的 函 數 名 是 大 小 寫(xiě) 敏 感 的, 也 就 是 說(shuō), 必 須 與 函 數 在 生 成 時(shí) 的 聲 明( 如 在C 源 文 件 中 的 聲 明) 一 致。 這 是 因 為32 位 動(dòng) 態(tài) 庫 與16 位 動(dòng) 態(tài) 庫 不 同, 其 中 的 函 數 名 是 區 分 大 小 寫(xiě) 的。 同 樣 道 理, 如 果 沒(méi) 有 使 用Alias 子 句, 那 么 在Function( 或Sub) 后 的 函 數 名 也 是 區 分 大 小 寫(xiě) 的。
---- 通 常 在 以 下 幾 種 情 況 時(shí) 需 要 使 用Alias 子 句:
---- A. 處 理 使 用 字 符 串 的 系 統 Windows API 過(guò) 程
---- 如 果 調 用 的 系 統 Windows API 過(guò) 程 要 使 用 字 符 串, 那 么 聲 明 語(yǔ) 句中 必 須 增 加 一 個(gè) Alias 子 句, 以 指 定 正 確 的 字 符 集。 包 含 字 符 串 的 系 統 Windows API 函 數 實(shí) 際 有 兩 種 格 式:ANSI 和 Unicode( 關(guān) 于A(yíng)NSI 和 Unicode 兩 種 字 符 集 的 區 別 將 在 后 面 詳 細 闡 述)。 因 此, 在 Windows 頭 文 件 中, 每 個(gè) 包 含 字 符 串 的 函 數 都 同 時(shí) 有 ANSI 版 本 和 Unicode 版 本。 例 如, 下 面 是 SetWindowText 函 數 的 兩 種 C 語(yǔ) 言 描 述。 可 以 看 到, 第 一 個(gè) 描 述 將 函 數 定 義 為 SetWindowTextA, 尾 部 的"A" 表 明 它 是 一 個(gè) ANSI 函 數:
---- WINUSERAPI BOOL WINAPI SetWindowTextA(HWND hWnd, LPCSTR lpString);
---- 第 二 個(gè) 描 述 將 它 定 義 為 SetWindowTextW, 尾 部 的"W" 表 明 它 是 一 個(gè) Unicode 函 數:
---- WINUSERAPI BOOL WINAPI SetWindowTextW(HWND hWnd, LPCWSTR lpString);
---- 因 為 兩 個(gè) 函 數 實(shí) 際 的 名 稱(chēng) 都 不 是"SetWindowText", 要 引 用 正 確 的 函 數 就 必 須 增 加 一 個(gè) Alias 子 句:
Private Declare Function SetWindowText Lib "user32" _
Alias "SetWindowTextA" (ByVal hwnd As Long, ByVal _
lpString As String) As Long
---- 應 當 注 意, 對 于VB 中 使 用 的 系 統Windows API 函 數, 應 該 指 定 函 數 的 ANSI 版 本, 因 為 只 有 Windows NT 才 支 持 Unicode 版 本, 而 Windows 95 不 支 持 這 個(gè) 版 本。 僅 當 應 用 程 序 只 運 行 在 Windows NT 平 臺 上 的 時(shí) 候 才 可 以 使 用 Unicode 版 本。
---- B. 函 數 名 是 不 標 準 的 名 稱(chēng)
---- 有 時(shí), 個(gè) 別 的 DLL 過(guò) 程 的 名 稱(chēng) 不 是 有 效 的 標 識 符。 例 如, 它 可 能 包 含 了 非 法 的 字 符( 如 連 字 符), 或 者 名 稱(chēng) 是 VB 的 關(guān) 鍵 字( 如 GetObject)。 在 這 種 情 況 下, 可 以 使 用 Alias 關(guān) 鍵 字。 例 如, 操 作 環(huán) 境 DLLs 中 的 某 些 過(guò) 程 名 以 下 劃 線(xiàn) 開(kāi) 始。 盡 管 在 VB 標 識 符 中 允 許 使 用 標 識 符, 但 是 下 劃 線(xiàn) 不 能 作 為 標 識 符 的 第 一 個(gè) 字 符。 為 了 使 用 這 種 過(guò) 程, 必 須 先 聲 明 一 個(gè) 名 稱(chēng) 合 法 的 過(guò) 程, 然 后 用 Alias 子 句 引 用 過(guò) 程 的 真 實(shí) 名 稱(chēng):
Declare Function lopen Lib "kernel32" Alias "_lopen" _
(ByVal lpPathName As String, ByVal iReadWrite _
As Long) As Long
---- 在 上 例 中,lopen 是 VB 中 使 用 的 過(guò) 程 名 稱(chēng)。 而 _lopen 則 是 動(dòng) 態(tài) 連 接 庫 中 可 以 識 別 的 名 稱(chēng)。
---- C. 使 用 序 號 標 識 DLL 過(guò) 程
---- 除 了 使 用 名 稱(chēng) 之 外, 還 可 以 使 用 序 號 來(lái) 標 識 DLL 過(guò) 程。 某 些 動(dòng) 態(tài) 連 接 庫 中 不 包 含 過(guò) 程 的 名 稱(chēng), 在 聲 明 它 們 包 含 的 過(guò) 程 時(shí) 必 須 使 用 序 號。 同 使 用 名 稱(chēng) 標 識 的DLL 過(guò) 程 相 比, 如 果 使 用 序 號, 在 最 終 的 應 用 程 序 中 消 耗 的 內 存 將 比 較 少, 而 且 速 度 會(huì ) 快 些。 但 是, 一 個(gè) 具 體 的API 的 序 號 在 不 同 的 操 作 系 統 中 可 能 是 不 同 的。 例 如 GetWindowsDirectory 在 Win95 下 的 序 號 為 432, 而 在 Windows NT 4.0 下 為 338。 總 而 言 之, 如 果 希 望 應 用 程 序 能 夠 在 不 同 的 操 作 系 統 下 運 行, 那 么 最 好 不 要 使 用 序 號 來(lái) 標 識 API 過(guò) 程。 如 果 過(guò) 程 不 屬 于A(yíng)PI, 或 者 應 用 程 序 使 用 的 范 圍 很 有 限, 那 么 使 用 序 號 還 是 有 好 處 的。
---- 要 使 用 序 號 來(lái) 聲 明 DLL 過(guò) 程,Alias 子 句 中 的 字 符 串 需 要 包 含 過(guò) 程 的 序 號, 并 在 序 號 的 前 面 加 一 個(gè) 數 字 標 記 字 符 (#)。 例 如,Windows kernel 中 的 GetWindowsDirectory 函 數 的 序 號 為 432; 可 以 用 下 面 的 語(yǔ) 句 來(lái) 聲 明 該 DLL 過(guò) 程:
Declare Function GetWindowsDirectory Lib "kernel32" _
Alias "#432" (ByVal lpBuffer As String, _
ByVal nSize As Long) As Long
---- 在 這 里, 可 以 使 用 任 意 的 合 法 名 稱(chēng) 作 為 過(guò) 程 的 名 稱(chēng),VB 將 用 序 號 在 DLL 中 尋 找 過(guò) 程。
---- 為 了 得 到 要 聲 明 的 過(guò) 程 的 序 號, 可 以 使 用 Dumpbin.exe 等 實(shí) 用 工 具(Dumpbin.exe 是 Microsoft Visual C++ 提 供 的 一 個(gè) 實(shí) 用 工 具, 它 的 使 用 說(shuō) 明 可 以 參 見(jiàn)VC 的 文 檔)。 利 用 Dumpbin, 可 以 提 取 出 .dll 文 件 中 的 各 種 信 息, 例 如 DLL 中 的 函 數 列 表, 它 們 的 序 號 以 及 與 代 碼 有 關(guān) 的 其 它 信 息。
---- 3、 使 用 值 或 引 用 傳 遞
---- 在 缺 省 的 情 況 下,VB 以 引 用 方 式 傳 遞 所 有 參 數(ByRef)。 這 意 味 著(zhù) 并 沒(méi) 有 傳 遞 實(shí) 際 的 參 數 值,VB 只 傳 遞 了 數 據 的 32 位 地 址。 另 外 有 許 多 DLL 過(guò) 程 要 求 參 數 以 值 方 式 傳 遞(ByVal)。 這 意 味 著(zhù) 它 們 需 要 實(shí) 際 的 數 據, 而 不 是 數 據 的 內 存 地 址。 如 果 過(guò) 程 需 要 一 個(gè) 傳 值 參 數, 而 傳 遞 給 它 的 參 數 是 一 個(gè) 指 針, 那 么 由 于 得 到 了 錯 誤 的 數 據, 該 過(guò) 程 將 不 能 正 確 地 工 作。
---- 要 使 參 數 以 使 用 值 方 式 傳 遞, 在 Declare 語(yǔ) 句 中 需 要 在 參 數 聲 明 的 前 面 加 上 ByVal 關(guān) 鍵 字。 例 如InvertRect 過(guò) 程 要 求 第 一 個(gè) 參 數 用 傳 值 方 式 傳 遞, 而 第 二 個(gè) 用 引 用 方 式 傳 遞:
Declare Function InvertRect Lib "user32" Alias _
"InvertRectA" (ByVal hdc As Long, lpRect As RECT) As Long
---- 動(dòng) 態(tài) 連 接 庫 的 參 數 傳 遞 是 一 個(gè) 復 雜 的 問(wèn) 題, 也 是VB 中 調 用 動(dòng) 態(tài) 連 接 庫 時(shí) 最 容 易 出 現 錯 誤 的 地 方。 參 數 類(lèi) 型 或 傳 遞 方 式 的 聲 明 錯 誤 都 可 能 導 致 應 用 程 序 出 現GPF( 通 用 保 護 錯 誤), 甚 至 使 操 作 系 統 崩 潰, 因 此 我 們 將 在 后 面 專(zhuān) 門(mén) 詳 細 地 討 論 這 個(gè) 問(wèn) 題。
---- 4、 靈 活 的 參 數 類(lèi) 型
---- 某 些 DLL 過(guò) 程 的 同 一 個(gè) 參 數 能 夠 接 受 多 種 數 據 類(lèi) 型。 如 果 需 要 傳 遞 多 種 類(lèi) 型 的 數 據, 可 以 將 參 數 聲 明 為 As Any, 從 而 取 消 類(lèi) 型 限 制。 例 如, 下 面 的 聲 明 中 的 第 三 個(gè) 參 數 (lppt As Any) 既 可 以 傳 遞 一 個(gè) POINT 結 構 的 數 組, 也 可 以 傳 遞 一 個(gè) RECT 結 構:
Declare Function MapWindowPoints Lib "user32" Alias _
"MapWindowPoints" (ByVal hwndFrom As Long, _
ByVal hwndTo As Long, lppt As Any, _
ByVal cPoints As Long) As Long
---- As Any 子 句 提 供 了 一 定 的 靈 活 性, 但 是, 由 于 它 不 進(jìn) 行 任 何 的 類(lèi) 型 檢 查, 風(fēng) 險 也 隨 之 增 加。 因 此 在 使 用 As Any 子 句 時(shí), 必 須 仔 細 檢 查 所 有 參 數 的 類(lèi) 型。
---- 正 確 的 函 數 聲 明 是 在VB 中 調 用 動(dòng) 態(tài) 連 接 庫 的 前 提, 但 要 想 在VB 中 用 對、 用 好 動(dòng) 態(tài) 庫 中 的 函 數, 僅 僅 有 聲 明 還 是 遠 遠 不 夠 的。 前 面 已 經(jīng) 說(shuō) 過(guò), 由 于VB 不 能 驗 證 應 用 程 序 傳 遞 到 動(dòng) 態(tài) 連 接 庫 中 的 參 數 值 是 否 正 確, 因 此 就 要 求 程 序 員 應 對 參 數 類(lèi) 型 有 非 常 詳 細 的 了 解, 否 則 很 容 易 引 起 應 用 程 序 發(fā) 生 通 用 保 護 錯 或 導 致 潛 在 的Bug, 降 低 軟 件 的 可 靠 性。 下 面 將 參 數 類(lèi) 型 分 為 簡(jiǎn) 單 數 據 類(lèi) 型、 字 符 串、 和 用 戶(hù) 自 定 義 類(lèi) 型 三 種 分 別 進(jìn) 行 討 論。
---- 1、 簡(jiǎn) 單 數 據 類(lèi) 型:
---- 簡(jiǎn) 單 數 據 類(lèi) 型 是 指Numeric 數 據 類(lèi) 型( 包 括Integer、Long、Single、Double、Currency 類(lèi) 型)、Byte 數 據 類(lèi) 型 和Boolean 數 據 類(lèi) 型。 它 們 的 共 同 的 特 點(diǎn) 是 結 構 簡(jiǎn) 單, 操 作 系 統 在 處 理 時(shí) 不 必 進(jìn) 行 特 殊 的 轉 換。
---- 簡(jiǎn) 單 數 據 類(lèi) 型 參 數 的 傳 遞 比 較 簡(jiǎn) 單。 我 們 知 道, 在VB 中 傳 遞 參 數 的 方 式 有 兩 種: 傳 值(Byval) 和 傳 址(ByRef), 缺 省 的 方 式 是 傳 址。 所 謂 傳 值, 就 是 對 一 個(gè) 變 量 的 具 體 值 進(jìn) 行 傳 遞; 而 傳 址 則 是 傳 遞 變 量 的 地 址。 例 如, 在VB 程 序 中 需 要 將 一 個(gè) 整 型 變 量m=10 的 值 傳 進(jìn) 動(dòng) 態(tài) 庫, 如 果 用 傳 值 方 式, 那 么 傳 進(jìn) 動(dòng) 態(tài) 庫 的 值 就 是10, 而 在 傳 址 方 式 下, 傳 入 的 則 是 變 量 m 的 地 址, 相 當 于C/C++ 中 &m 的 值。 需 要 注 意 的 是, 以 傳 值 方 式 傳 進(jìn) 動(dòng) 態(tài) 連 接 庫 的 變 量, 其 值 在 動(dòng) 態(tài) 庫 中 是 不 能 被 改 變 的; 如 果 需 要 在 動(dòng) 態(tài) 連 接 庫 中 修 改 傳 入 參 數 的 值, 則 必 須 使 用 傳 址 方 式。 一 般 來(lái) 說(shuō), 在VB 和 動(dòng) 態(tài) 連 接 庫 之 間 傳 遞 單 個(gè) 的 簡(jiǎn) 單 數 據 類(lèi) 型, 只 要 注 意 了 以 上 幾 個(gè) 方 面 就 可 以 了。 當 需 要 將 一 個(gè) 簡(jiǎn) 單 數 據 類(lèi) 型 的 整 個(gè) 數 組 傳 進(jìn) 動(dòng) 態(tài) 庫 時(shí), 必 須 將 相 應 參 數 聲 明 為 傳 址 方 式, 然 后 把 數 組 的 第 一 個(gè) 元 素 作 為 參 數 傳 入, 這 樣 在 動(dòng) 態(tài) 連 接 庫 中 就 得 到 了 數 組 的 首 地 址, 從 而 可 以 對 整 個(gè) 數 組 進(jìn) 行 訪(fǎng) 問(wèn)。 例 如, 聲 明 了 一 個(gè) 名 為ReadArray 的DLL 過(guò) 程, 要 求 傳 入 一 個(gè) 整 型 數 組aArray:
Declare Function ReadArray Lib "mydll.dll" _
(aArray As Integer) As Integer
在 調 用 時(shí) 可 以 采 用 如 下 方 式:
Dim ret,I(5) as Integer
… …
ret = ReadArray(I(0)) ‘ 將 整 個(gè) 數 組 傳 入 動(dòng) 態(tài) 連 接 庫
---- 2、 字 符 串 參 數 的 傳 遞:
---- 與 簡(jiǎn) 單 數 據 類(lèi) 型 相 比, 字 符 串 類(lèi) 型(String、String * n) 的 參 數 傳 遞 要 復 雜 得 多,這 主 要 是Windows 95 API 和VB 使 用 的 字 符 串 類(lèi) 型 不 同 的 緣 故。VB 使 用 被 稱(chēng) 為 BSTR 的 String 數 據 類(lèi) 型, 它 是 由 自 動(dòng) 化( 以 前 被 稱(chēng) 為 OLE Automation) 定 義 的 數 據 類(lèi) 型。 一 個(gè) BSTR 由 頭 部 和 字 符 串 組 成, 頭 部 包 含 了 字 符 串 的 長(cháng) 度 信 息, 字 符 串 中 可 以 包 含 嵌 入 的 null 值。 大 部 分 的 BSTR 是 Unicode 的, 即 每 個(gè) 字 符 需 要 兩 個(gè) 字 節。BSTR 通 常 以 兩 字 節 的 兩 個(gè) null 字 符 結 束。 下 圖 表 示 了 一 個(gè)BSTR 類(lèi) 型 的 字 符 串。
( 前 綴) a T e s t {content}
頭 部 BSTR指向數據的第一個(gè)字節
---- 另 一 方 面, 大 部 分 的DLL 過(guò) 程( 包 括 Windows 95 API 中 的 所 有 過(guò) 程) 使 用 LPSTR 類(lèi) 型 字 符 串, 這 是 指 向 標 準 的 以 null 結 束 的 C 語(yǔ) 言 字 符 串 的 指 針, 它 也 被 稱(chēng) 為 ASCIIZ 字 符 串。LPSTR 沒(méi) 有 前 綴。 下 圖 顯 示 了 一 個(gè) 指 向 ASCIIZ 字 符 串 的 LPSTR。
---- a T e s t {content}
---- LPSTR 指 向 一 個(gè) 以null 結 尾 的 字 符 串 數 據 的 第 一 個(gè) 字 節
---- 如 果 DLL 過(guò) 程 需 要 一 個(gè) LPSTR( 指 向 以 null 結 束 的 字 符 串 的 指 針) 作 為 參 數, 可 以 在VB 中 將 一 個(gè) 字 符 串 以 傳 值 的 方 式 傳 遞 給 它。 因 為 指 向 BSTR 的 指 針 實(shí) 際 指 向 以 null 值 結 束 的 字 符 串 的 第 一 個(gè) 數 據 字 節, 所 以 對 于 DLL 過(guò) 程 來(lái) 說(shuō), 它 就 是 一 個(gè) LPSTR。 這 樣 傳 入 動(dòng) 態(tài) 連 接 庫 的 字 符 串,DLL 過(guò) 程 也 可 以 對 它 進(jìn) 行 修 改, 盡 管 它 是 以 傳 值 方 式 傳 入 的。 只 有 當DLL 過(guò) 程 需 要 一 個(gè) 指 向LPSTR 的 指 針 時(shí), 才 以 傳 址 的 方 式 傳 入 字 符 串, 這 時(shí)DLL 過(guò) 程 得 到 的 是 一 個(gè) 指 向 字 符 串 指 針 的 指 針( 相 當 于C/C++ 中 的char * *), 而 不 是 通 常 所 用 的 字 符 串 的 首 地 址( 相 當 于C/C++ 中 的char *)。
---- 當 需 要 把 一 個(gè) 字 符 串 數 組 整 個(gè) 傳 入 動(dòng) 態(tài) 連 接 庫 時(shí), 情 況 就 變 得 復 雜 多 了, 用 傳 遞 簡(jiǎn) 單 數 據 類(lèi) 型 數 組 的 方 式 來(lái) 傳 遞 字 符 串 數 組 是 行 不 通 的。 當 我 們 以 傳 值 的 方 式 將 一 個(gè) 字 符 串 數 組 的 第 一 個(gè) 元 素 傳 進(jìn) 動(dòng) 態(tài) 連 接 庫 時(shí),DLL 過(guò) 程 得 到 的 實(shí) 際 上 是 該 元 素 壓 入 堆 棧 段 后 的 地 址, 而 不 是 數 據 段 中 整 個(gè) 數 組 的 首 地 址。 也 就 是 說(shuō), 這 時(shí)DLL 過(guò) 程 只 能 得 到 數 組 的 第 一 個(gè) 元 素, 而 無(wú) 法 訪(fǎng) 問(wèn) 整 個(gè) 數 組。 而 以 傳 址 方 式 傳 入 第 一 個(gè) 元 素 時(shí),DLL 過(guò) 程 只 能 得 到 指 向 該 元 素 在 堆 棧 段 中 地 址 的 指 針, 同 樣 無(wú) 法 訪(fǎng) 問(wèn) 整 個(gè) 數 組。 這 不 能 不 說(shuō) 是VB 的 一 個(gè) 不 足。 因 此, 在 程 序 設 計 中, 如 果 確 實(shí) 需 要 將 整 個(gè) 字 符 串 數 組 傳 入 動(dòng) 態(tài) 庫, 就 必 須 采 取 其 它 方 法。
---- 我 們 知 道, 在VB 中, 有 一 種Byte 數 據 類(lèi) 型。 每 個(gè)Byte 型 變 量 占 一 個(gè) 字 節, 不 含 符 號 位, 因 此 所 能 表 示 的 范 圍 為0 到255。 這 種 數 據 類(lèi) 型 是 專(zhuān) 門(mén) 用 于 存 放 二 進(jìn) 制 數 據 的。 為 了 將 整 個(gè) 字 符 串 數 組 傳 進(jìn) 動(dòng) 態(tài) 庫, 可 以 用 字 節 數 組 來(lái) 保 存 字 符 串。 由 于Byte 是 一 種 簡(jiǎn) 單 數 據 類(lèi) 型, 因 此 字 節 數 組 的 傳 遞 是 非 常 簡(jiǎn) 單 的。 首 先, 需 要 把 一 個(gè) 字 符 串 正 確 地 轉 變 成 一 個(gè) 字 節 數 組。 這 要 涉 及 一 些 字 符 集 的 知 識。Windows 95 和VB 使 用 不 同 的 字 符 集,Windows 95 API 使 用 的 是ANSI 或DBCS 字 符 集, 而VB 使 用 的 則 是Unicode 字 符 集。 所 謂ANSI 字 符 集, 是 指 每 個(gè) 字 符 都 用 一 個(gè) 字 節 表 示, 因 此 最 多 只 能 有28=256 個(gè) 不 同 的 字 符, 這 對 于 英 語(yǔ) 來(lái) 說(shuō) 已 經(jīng) 足 夠 了, 但 不 能 完 全 支 持 其 它 語(yǔ) 言。DBCS 字 符 集 支 持 很 多 不 同 的 東 亞 語(yǔ) 言, 如 漢 語(yǔ)、 日 語(yǔ) 和 朝 鮮 語(yǔ), 它 使 用 數 字 0-255 表 示 ASCII 字 符, 其 它 大 于255 或 小 于0 的 數 字 表 明 該 字 符 屬 于 非 拉 丁 字 符 集; 在 DBCS 中,ASCII 字 符 的 長(cháng) 度 是 一 個(gè) 字 節, 而 漢 語(yǔ)、 日 語(yǔ) 和 其 它 東 亞 字 符 的 長(cháng) 度 是 2 個(gè) 字 節。 而Unicode 字 符 集 則 完 全 用 兩 個(gè) 字 節 表 示 一 個(gè) 字 符, 因 此 最 多 可 以 表 示216=65536 個(gè) 不 同 字 符。 也 就 是 說(shuō),ANSI 字 符 集 中 所 有 的 字 符 都 只 占 一 個(gè) 字 節,DBCS 字 符 集 中ASCII 字 符 占 一 個(gè) 字 節, 漢 字 占 兩 個(gè) 字 節,Unicode 字 符 集 中 每 個(gè) 字 符 都 占 兩 個(gè) 字 節。 由 于VB 與Windows API 使 用 的 字 符 集 不 同, 因 此 在 進(jìn) 行 字 符 串 到 字 節 數 組 的 轉 換 時(shí), 當 用Asc 函 數 取 得 一 個(gè) 字 符 的 字 節 碼 后, 需 要 判 斷 它 是 否 是 一 個(gè)ASCII 字 符; 如 果 是ASCII 字 符, 則 在 轉 換 后 的 字 節 數 組 中 就 只 占 一 個(gè) 字 節, 否 則 要 占 兩 個(gè) 字 節。 如 下 流 程 圖 表 示 了 轉 換 一 個(gè) 字 符 串 的 過(guò) 程。
---- 下 面 給 出 了 轉 換 函 數:GetCharByte 得 到 一 個(gè) 字 符 的 高 字 節 或 低 字 節, 它 的 第 一 個(gè) 參 數 是 一 個(gè) 字 符 的ASCII 碼, 第 二 個(gè) 參 數 是 標 志 取 高 字 節 還 是 低 字 節;StrToByte 按DBCS 或ANSI 格 式 將 一 個(gè) 字 符 串 轉 換 成 一 個(gè) 字 節 數 組, 第 一 個(gè) 參 數 是 待 轉 換 的 字 符 串, 第 二 個(gè) 參 數 是 轉 換 后 的 一 個(gè) 定 長(cháng) 字 節 數 組, 若 該 數 組 長(cháng) 度 不 足 以 存 放 整 個(gè) 字 符 串, 則 截 去 超 長(cháng) 的 部 分;ChangeStrAryToByte 利 用 前 兩 個(gè) 函 數 將 字 符 串 數 組 轉 換 成 字 節 數 組, 第 一 個(gè) 參 數 是 定 長(cháng) 的 字 符 串 數 組, 其 中 每 個(gè) 元 素 都 是 一 個(gè) 字 符 串( 各 個(gè) 元 素 包 含 的 字 符 數 可 以 不 同), 第 二 個(gè) 參 數 是 一 個(gè) 變 長(cháng) 的 字 節 數 組, 保 存 轉 換 后 的 結 果。
---- Function GetCharByte(ByVal OneChar As Integer, ByVal IsHighByte As Boolean) As Byte ‘ 該 函 數 獲 得 一 個(gè) 字 符 的 高 字 節 或 低 字 節
If IsHighByte Then
If OneChar >= 0 Then
GetCharByte = CByte(OneChar 256)
‘右移8位,得到高字節
Else
GetCharByte = CByte((OneChar
And &H7FFF) 256) Or &H80
End If
Exit Function
Else
GetCharByte = CByte(OneChar And &HFF)
‘屏蔽掉高字節,得到低字節
Exit Function
End If
End Function
Sub StrToByte(StrToChange As String, ByteArray() As Byte)
‘該函數將一個(gè)字符串轉換成字節數組
Dim LowBound, UpBound As Integer
Dim i, count, length As Integer
Dim OneChar As Integer
count = 0
length = Len(StrToChange)
LowBound = LBound(ByteArray)
UpBound = UBound(ByteArray)
For i = LowBound To UpBound
ByteArray(i) = 0 ‘初始化字節數組
Next
For i = LowBound To UpBound
count = count + 1
If count <= length Then
OneChar = Asc(Mid(StrToChange, count, 1))
If (OneChar > 255) Or (OneChar < 0) Then
‘該字符是非ASCII字符
ByteArray(i) = GetCharByte(OneChar, True) ‘得到高字節
i = i + 1
If i <= UpBound Then ByteArray(i)
= GetCharByte(OneChar, False)
‘得到低字節
Else
‘該字符是ASCII字符
ByteArray(i) = OneChar
End If
Else
Exit For
End If
Next
End Sub
Sub ChangeStrAryToByte(StrAry()
As String, ByteAry() As Byte)
‘將字符串數組轉換成字節數組
Dim LowBound, UpBound As Integer
Dim i, count, StartPos, MaxLen As Integer
Dim TmpByte() As Byte
LowBound = LBound(StrAry)
UpBound = UBound(StrAry)
count = 0
ReDim ByteAry(0)
For i = LowBound To UpBound
MaxLen = LenB(StrAry(i))
ReDim TmpByte(MaxLen + 1)
ReDim Preserve ByteAry(count + MaxLen + 1)
Call StrToByte(StrAry(i), TmpByte) ‘轉換一個(gè)字符串
StartPos = count
Do
ByteAry(count) = TmpByte(count - StartPos)
count = count + 1
If ByteAry(count - 1) = 0 Then Exit Do
Loop ‘將每一個(gè)字符串對應
的字節數組按順序填入結果數組中
ReDim Preserve ByteAry(count - 1)
Next i
End Sub
---- 下 面 看 一 個(gè) 轉 換 的 例 子:
Dim ResultAry() as Byte
Dim SomeStr(2) as String
SomeStr(0) = " 測 試1"
SomeStr(1) = " 測 試222"
SomeStr(2) = " 測 試33"
Call ChangeStrAryToByte
(SomeStr,ResultAry) ‘ 轉 換 字 符 串 數 組
---- 當 轉 換 完 成 以 后, 查 看 字 節 數 組ResultAry, 其 中 包 含 了21 個(gè) 元 素, 依 次 是:178,226,202,212,49,0,178,226,202,212,50,50,50,0,178,226,202,212,51,51,0。 其 中,[178,226] 是" 測" 的 字 節 碼,[202,112] 是" 試" 的 字 節 碼,49,50,51 分 別 為 字 符1、2、3 的ASCII 碼。 可 見(jiàn), 經(jīng) 過(guò) 轉 換 后, 字 符 串 數 組 中 的 各 個(gè) 元 素 按 順 序 放 在 了 字 節 數 組 中, 相 互 間 以 終 止 符0 分 隔。
---- 這 樣, 字 符 串 數 組 就 全 部 轉 換 成 了 字 節 數 組, 然 后 只 要 將 字 節 數 組 的 第 一 個(gè) 元 素 以 傳 址 的 方 式 傳 入 動(dòng) 態(tài) 連 接 庫,DLL 過(guò) 程 就 可 以 正 確 地 訪(fǎng) 問(wèn) 數 組 中 的 所 有 字 符 串 了。 但 是, 使 用 這 種 方 法, 當DLL 過(guò) 程 處 理 結 束 返 回VB 時(shí),VB 得 到 的 仍 然 是 字 節 數 組。 如 果 需 要 在VB 中 再 次 得 到 該 字 節 數 組 表 示 的 字 符 串, 還 要 把 整 個(gè) 字 節 數 組 重 新 以0 為 分 割 符 分 成 多 個(gè) 子 數 組( 每 個(gè) 子 數 組 都 對 應 原 來(lái) 字 符 串 數 組 中 的 一 個(gè) 元 素), 然 后 使 用VB 函 數StrConv 將 每 個(gè) 子 數 組 轉 換 成 字 符 串( 轉 換 時(shí) 第 二 個(gè) 參 數 選vbUnicode), 就 可 以 顯 示 或 進(jìn) 行 其 它 操 作 了。 例 如, 其 中 一 個(gè) 子 數 組 的 名 字 是SubAry, 則 函 數StrConv(SubAry,vbUnicode) 就 返 回 了 它 所 對 應 的 字 符 串。
---- 總 之,VB 應 用 程 序 和 動(dòng) 態(tài) 庫 間 字 符 串 參 數 的 傳 遞 是 一 個(gè) 比 較 復 雜 的 過(guò) 程, 使 用 時(shí) 要 非 常 謹 慎。 同 時(shí) 應 盡 可 能 避 免 傳 遞 字 符 串 數 組 類(lèi) 型 的 參 數, 因 為 這 很 容 易 引 起 下 標 越 界、 堆 棧 溢 出 等 嚴 重 錯 誤。
---- 3、 用 戶(hù) 自 定 義 類(lèi) 型(User-defined Type) 參 數 的 傳 遞
---- 用 戶(hù) 自 定 義 類(lèi) 型 在VB 中 是 一 種 重 要 的 數 據 類(lèi) 型, 它 為 編 程 者 提 供 了 很 大 的 靈 活 性, 使 開(kāi) 發(fā) 人 員 可 以 根 據 需 要 構 造 自 己 的 數 據 結 構。 它 相 當 于C/C++ 中 的 結 構 類(lèi) 型(structure)。 在VB 中, 允 許 程 序 員 以 傳 址 的 方 式 將 自 定 義 數 據 類(lèi) 型 參 數 傳 入 動(dòng) 態(tài) 庫,DLL 過(guò) 程 也 可 以 將 修 改 后 的 參 數 返 回VB 程 序。 但 是, 在VB 中 仍 然 不 支 持 以 傳 值 的 方 式 傳 遞 用 戶(hù) 自 定 義 類(lèi) 型 參 數。
---- 傳 遞 用 戶(hù) 自 定 義 類(lèi) 型 參 數 時(shí), 必 須 確 保VB 中 的 數 據 類(lèi) 型 的 成 員 與 動(dòng) 態(tài) 庫 中 的 結 構 成 員 是 一 一 對 應 的, 所 占 空 間 也 必 須 嚴 格 一 致。 這 里 所 說(shuō) 的 一 一 對 應, 不 僅 是 指VB 中 的 所 有 結 構 成 員 在 動(dòng) 態(tài) 庫 的 結 構 中 都 必 須 有 對 應 的 元 素, 而 且 它 們 在 數 據 結 構 中 定 義 的 順 序 也 必 須 嚴 格 一 致, 這 是VB 中 使 用 的" 數 據 結 構 成 員 對 齊 方 式" 決 定 的。 在VB 中, 數 據 結 構 使 用 雙 字 對 齊 方 式(4-byte alignment), 因 此, 在 用 戶(hù) 自 己 生 成 用 于VB 調 用 的 動(dòng) 態(tài) 連 接 庫 時(shí), 也 必 須 把 編 譯 選 項"structure member alignment" 設 為4 字 節( 如 前 文 所 述)。
---- 所 謂 結 構 成 員 對 齊 方 式 是 指 一 個(gè) 數 據 結 構 內 部, 其 成 員 的 排 列 方 式。 譬 如, 在VB 中, 其 對 齊 方 式 是4 字 節, 這 就 好 象 在 一 個(gè) 數 據 結 構 內 部 分 成 了 很 多 個(gè)4 字 節 大 小 的 小 單 元, 如 果 相 鄰 兩 個(gè) 或 多 個(gè) 數 據 成 員 的 大 小 可 以 放 在 一 個(gè) 單 元 中, 那 么 就 放 在 一 起; 否 則 這 些 小 單 元 中 可 能 會(huì ) 出 現 未 用 的 空 字 節。 我 們 來(lái) 看 下 面 一 個(gè) 數 據 類(lèi) 型:
Type TestType
m1 as Integer
m2 as Byte
m3 as Long
End Type
---- 它 的 三 個(gè) 成 員 的 大 小 加 起 來(lái) 是2+1+4=7。 但 是, 由 于m1 和m2 的 字 節 總 長(cháng) 度 是3, 小 于4, 它 們 就 存 放 于 一 個(gè) 單 元 中; 但 該 單 元 剩 下 的 一 個(gè) 字 節 不 足 以 放 下 一 個(gè)Long 型 的 成 員m3, 于 是m3 就 被 放 在 下 一 個(gè) 單 元 中, 它 們 之 間 就 有 了 一 個(gè) 未 用 的 空 字 節; 因 此, 整 個(gè) 結 構 所 占 實(shí) 際 長(cháng) 度 是8 字 節。 同 理, 如 果 將m3 和m2 的 位 置 交 換 一 下, 它 所 占 的 尺 寸 就 變 成 了9 字 節。 可 見(jiàn), 成 員 在 結 構 中 的 聲 明 順 序 也 是 非 常 重 要 的。
---- 通 常, 當 一 個(gè) 用 戶(hù) 自 定 義 類(lèi) 型 中 不 包 含 字 符 串 時(shí), 向 動(dòng) 態(tài) 連 接 庫 中 傳 遞 該 類(lèi) 型 的 參 數 是 沒(méi) 有 什 么 問(wèn) 題 的。 如 果 只 傳 遞 一 個(gè) 自 定 義 類(lèi) 型 變 量, 則 既 可 以 傳 遞 該 變 量 名, 也 可 以 傳 遞 該 變 量 的 第 一 個(gè) 成 員, 它 們 的 效 果 是 一 樣 的, 都 是 將 該 變 量 的 地 址 傳 進(jìn) 了 動(dòng) 態(tài) 庫; 同 樣, 如 果 要 傳 遞 一 個(gè) 自 定 義 類(lèi) 型 的 數 組, 則 既 可 以 傳 遞 該 數 組 的 第 一 個(gè) 元 素, 也 可 以 傳 遞 第 一 個(gè) 元 素 的 第 一 個(gè) 成 員。 但 是, 如 果 用 戶(hù) 自 定 義 類(lèi) 型 中 包 含 字 符 串 類(lèi) 型 時(shí), 又 該 如 何 與 動(dòng) 態(tài) 連 接 庫 傳 遞 參 數 呢 ? 答 案 是 令 人 遺 憾 的: 在VB 中, 你 無(wú) 法 將 一 個(gè) 包 含 字 符 串 成 員 的 用 戶(hù) 自 定 義 類(lèi) 型 變 量 或 數 組 安 全、 正 確 地 傳 入 動(dòng) 態(tài) 庫 中。 如 果 你 這 樣 做 了, 即 使 某 次 僥 幸 得 到 了 正 確 的 結 果, 在 其 背 后 也 隱 藏 著(zhù) 許 多 致 命 的 危 險。 因 此, 如 果 一 定 要 在 用 戶(hù) 自 定 義 類(lèi) 型 中 包 含 字 符 串 變 量, 并 且 該 類(lèi) 型 的 變 量 又 要 作 為 參 數 傳 入 動(dòng) 態(tài) 庫 時(shí), 你 最 好 修 改 類(lèi) 型 定 義, 把 其 中 的 字 符 串 成 員 用 相 應 的 字 節 數 組 類(lèi) 型 替 換 掉( 轉 換 方 法 可 參 見(jiàn) 前 文), 這 樣 就 可 以 在VB 和 動(dòng) 態(tài) 庫 間 傳 遞 這 種 類(lèi) 型 的 參 數 了。
---- 另 外, 在VB 中 還 可 以 把 一 個(gè) 函 數 的 指 針 傳 遞 到 動(dòng) 態(tài) 庫 中, 方 法 也 并 不 復 雜。 但 筆 者 強 烈 建 議 最 好 不 要 這 么 做, 因 為 這 樣 一 來(lái)VB 應 用 程 序 就 幾 乎 完 全 喪 失 了 它 所 應 有 的 安 全 性。 如 果 確 實(shí) 需 要 傳 遞 函 數 指 針 的 話(huà), 那 么 還 是 編 一 個(gè)C/C++ 的 程 序 來(lái) 完 成 這 項 工 作 吧。
---- 總 之, 在VB 中 調 用DLL 過(guò) 程 是 一 個(gè) 比 較 復 雜 的 問(wèn) 題, 編 程 人 員 必 須 很 好 地 把 握, 才 能 達 到 既 提 高 了 程 序 效 率, 開(kāi) 拓 了 程 序 功 能, 又 不 降 低 程 序 安 全 性 的 目 的。 另 外 需 要 特 別 指 出 的 一 點(diǎn) 是, 在 本 文 中 提 到 的 所 有 動(dòng) 態(tài) 連 接 庫, 都 是 指 沒(méi) 有 使 用 自 動(dòng) 化(OLE Automation) 技 術(shù) 的 動(dòng) 態(tài) 庫,Windows API 和 大 多 數 用 戶(hù) 自 編 的 動(dòng) 態(tài) 連 接 庫 都 是 這 種 類(lèi) 型 的。 對 于 使 用 了OLE Automation 技 術(shù) 的 動(dòng) 態(tài) 連 接 庫, 其 參 數 傳 遞 的 方 式 有 所 不 同, 讀 者 可 以 參 閱 有 關(guān)OLE 技 術(shù) 的 書(shū) 籍, 在 此 不 再 涉 及。
聯(lián)系客服