Delphi 函式庫發佈方式(提供 Interface PAS + DCU)
一、目的
當 Delphi 開發完成一個 Unit,希望提供給其他開發者使用,但又不希望公開原始程式碼時,可以採用:
- 公開 Interface PAS
- 提供編譯完成的 DCU
使用者可以正常 uses 該 Unit,也能使用 IDE 的型別提示及程式碼完成(Code Insight),但無法看到真正的程式實作。
二、發佈內容
假設原始 Unit 名稱為:
MyLib.pas
發佈內容如下:
Release
│
├── MyLib.pas ← 只有 Interface 宣告
└── MyLib.dcu ← 編譯完成的程式
不提供完整原始碼。
三、原始 Unit
例如:
unit MyLib;
interface
uses
System.SysUtils;
type
TMyClass = class
public
constructor Create;
destructor Destroy; override;
function Add(A, B: Integer): Integer;
function Sub(A, B: Integer): Integer;
end;
function GetVersion: string;
implementation
constructor TMyClass.Create;
begin
inherited;
end;
destructor TMyClass.Destroy;
begin
inherited;
end;
function TMyClass.Add(A, B: Integer): Integer;
begin
Result := A + B;
end;
function TMyClass.Sub(A, B: Integer): Integer;
begin
Result := A - B;
end;
function GetVersion: string;
begin
Result := '1.0';
end;
end.
四、編譯產生 DCU
使用 Delphi 編譯後,會產生:
MyLib.dcu
真正執行的程式都在 DCU 內。
五、建立公開 PAS
建立另一份供發布使用的 MyLib.pas。
注意:不要修改原始碼,而是另外建立一份。
例如:
Project
│
├── Source
│ MyLib.pas ← 原始完整程式
│
└── Release
MyLib.pas ← 公開介面
MyLib.dcu
公開 PAS 保留 Interface:
unit MyLib;
interface
uses
System.SysUtils;
type
TMyClass = class
public
constructor Create;
destructor Destroy; override;
function Add(A, B: Integer): Integer;
function Sub(A, B: Integer): Integer;
end;
function GetVersion: string;
implementation
end.
可以看到:
- Class
- Function
- Procedure
- Property
- Event
- Record
- Enum
都需要保留。
但是:
所有 Implementation 內的程式碼全部刪除。
六、使用者如何使用
使用者只要:
uses
MyLib;
即可正常呼叫:
var
M: TMyClass;
begin
M := TMyClass.Create;
try
ShowMessage(IntToStr(M.Add(3,5)));
finally
M.Free;
end;
end;
不需要任何特殊設定。
七、IDE 功能
由於 Interface 仍存在,因此 Delphi IDE 可以提供:
- Code Insight
- Auto Complete
- Parameter Hint
- 型別檢查
- 編譯檢查
使用體驗與一般 Unit 幾乎相同。
八、可以隱藏哪些內容
可以隱藏:
- 所有演算法
- 所有商業邏輯
- SQL
- 加解密流程
- API 呼叫方式
- 所有 Function 實作
- 所有 Method 實作
仍然會看到:
- Class 名稱
- Function 名稱
- Procedure 名稱
- Property 名稱
- Record 定義
- Enum 定義
- Event 定義
如果 Interface 中宣告了 Private 欄位:
private
FData: Integer;
使用者仍然可以看到:
FData
只是無法知道如何使用。
因此,如果希望降低資訊曝光,建議不要在 Interface 中放置過多內部欄位或實作細節。
九、優點
- 不公開原始碼。
- 使用方式與一般 Unit 完全相同。
- IDE 可正常提供 Code Insight。
- 不需要 DLL。
- 執行速度與一般 Delphi 程式相同。
- 發布方便。
十、缺點
1. 無法跨 Delphi 版本
DCU 為 Delphi 編譯器產生的中間檔。
不同 Delphi 版本的 DCU 格式可能不同,因此:
- 無法保證相容
- 通常不可共用
例如:
| 編譯版本 | 使用版本 | 是否可用 |
|---|---|---|
| XE10 | XE10 | ✔ |
| XE10 | XE8 | ✘ |
| XE10 | Delphi 10.4 | ✘ |
| XE10 | Delphi 11 | ✘ |
| XE10 | Delphi 12 | ✘ |
因此,每個 Delphi 版本都需要重新編譯對應的 DCU。
2. 每個版本都需要重新發布
若要支援:
- XE10
- 10.4 Sydney
- 11 Alexandria
- 12 Athens
通常需要:
Release
│
├── XE10
│ MyLib.pas
│ MyLib.dcu
│
├── 10.4
│ MyLib.pas
│ MyLib.dcu
│
├── 11
│ MyLib.pas
│ MyLib.dcu
│
└── 12
MyLib.pas
MyLib.dcu
3. 若公開介面有修改
例如新增:
function Test: Integer;
就需要重新:
- 編譯 DCU。
- 更新公開 PAS。
- 一起發布。
兩者必須保持一致。
十一、適用情況
適合:
- 公司內部函式庫。
- 不希望公開原始碼。
- Delphi 開發團隊使用相同版本。
- 商業 Delphi 函式庫。
- 元件開發。
十二、不適合情況
若需要:
- 支援 Delphi 多個版本
- 支援 C++
- 支援 C#
- 支援 VB
- 支援其他語言
則建議使用:
- DLL
- COM
- Web API
- REST API
而不是 DCU。
十三、建議
若所有使用者皆使用相同 Delphi 版本,採用 Interface PAS + DCU 是一種簡單且成熟的封裝方式,能兼顧開發便利性與原始碼保護。
若需支援不同 Delphi 版本,則必須針對每個版本重新編譯並發布對應的 .dcu;單一 DCU 無法跨 Delphi 版本使用。如果目標是跨 Delphi 版本,建議直接提供相容的原始碼,或改以 DLL、COM、REST API 等方式封裝功能,以降低版本相依性。