2026年6月30日 星期二

Delphi 函式庫發佈方式(提供 Interface PAS + DCU)

 

如果想將程式工具提供他人使用,且不想提供程式原始碼,可以參考以下做法

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 中放置過多內部欄位或實作細節。


九、優點

  1. 不公開原始碼。
  2. 使用方式與一般 Unit 完全相同。
  3. IDE 可正常提供 Code Insight。
  4. 不需要 DLL。
  5. 執行速度與一般 Delphi 程式相同。
  6. 發布方便。

十、缺點

1. 無法跨 Delphi 版本

DCU 為 Delphi 編譯器產生的中間檔。

不同 Delphi 版本的 DCU 格式可能不同,因此:

  • 無法保證相容
  • 通常不可共用

例如:

編譯版本使用版本是否可用
XE10XE10
XE10XE8
XE10Delphi 10.4
XE10Delphi 11
XE10Delphi 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;

就需要重新:

  1. 編譯 DCU。
  2. 更新公開 PAS。
  3. 一起發布。

兩者必須保持一致。


十一、適用情況

適合:

  • 公司內部函式庫。
  • 不希望公開原始碼。
  • 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 等方式封裝功能,以降低版本相依性。