問題背景
Delphi 插件 (主程序 + DLL) 使用第三方控件/庫時,編譯需「帶包」。發布時若目標環境已註冊相同包會報錯。需解決:
- 如何確定項目**確切依賴**哪些運行時包?
- 如何將多個依賴包**合並為單個包** 簡化部署?
確定依賴包
方法一:IDE 內置檢查 (最直觀)
- 打開項目 → **Project → Options → Packages → Runtime Packages**。
- 勾選 **「Link with runtime packages」**。
- IDE 會自動分析 `uses` 子句,在下方列表顯示**所有需要的運行時包名** (如 `vclx`, `indy`, `cxLibrary` 等)。
- 此列表即為編譯時必須勾選的包。
方法二:代碼級分析 — `GetPackageInfo` API
Delphi RTL 提供 `GetPackageInfo` (在 `System.SysUtils` 中),可枚舉包內部信息:
- 包名、描述、版本。
- **包含的單元** (`*.pas` 編譯後的 `.dcu`)。
- **依賴的其他包** (靜態鏈接的 `requires` 子句)。
編寫小工具遞歸調用,可生成完整依賴樹。
方法三:第三方工具 — 「狂牛插件框架」包合並向導
- 狂牛框架內置「包合並向導」。
- 指定主程序/插件 DLL,自動掃描所有依賴包。
- 一鍵生成**合並後的單一運行時包** (`.bpl`),包含所有依賴單元。
- 發布時只需部署這一個合並包 + 主程序/插件 DLL,極大簡化部署。
包合並原理與步驟
- 收集單元:將所有依賴包中的 `.dcu` (編譯後單元) 解包提取。
- 去重與衝突解決:同名單元保留一份 (通常取版本最高或指定版本);解決符號重複 (如兩個包定義同名全局變量)。
- 重新打包:將合併後的單元列表生成新的 `.dpk` 項目文件,編譯產出單一 `.bpl`。
- 修正引用:主程序/插件項目移除原多個 `requires`,改為 `requires MergedPackage;`。
最佳實踐建議
- 版本鎖定:第三方控件升級時,重新運行合並向導,生成新版合並包,避免「DLL 地獄」。
- 分層合並:核心公共庫 (VCL, RTL, Indy) 合並為 `Core.bpl`;業務控件庫合並為 `BizControls.bpl`。便於獨立升級。
- 自動化構建:在 CI/CD 流水線中集成合並腳本 (調用 `msbuild` 編譯生成的 `.dpk`),確保每次發布包一致。
- 數字簽名:對合並後的 `.bpl` 進行代碼簽名,提升信任度,防篡改。
常見坑點
| 問題 | 原因 | 對策 |
| 合並包體積過大 | 包含大量未用單元 | 使用智能鏈接 (Smart Linking),或手動精簡 `.dpk` 的 `contains` 列表 |
| 運行時「找不到包」 | 合並包未部署到系統路徑或應用目錄 | 將 `.bpl` 放在 EXE/DLL 同級目錄,或註冊到系統註冊表 `Known Packages` |
| 符號衝突 | 兩個包定義同名全局變量/類 | 重命名單元、使用命名空間隔離、或放棄合並該衝突包 |
| 調試符號丟失 | 合並編譯未生成 `.tds`/`.map` | 合並項目啟用 Debug 信息 (Project Options → Linking → Debug information) |