C++Builder 與 Delphi 共享 VCL 庫,但直接在 C++ 專案中使用 Delphi 編寫的控件(特別是非可視組件)常遇到鏈接與 RTL 兼容問題。成熟方案是將 Delphi 控件封裝為標準 DLL,C++Builder 通過 implib 生成 .lib 靜態導入庫進行動態調用。本文完整演示從 Delphi 建立 DLL 到 C++Builder 調用的全流程。
stdcall 慣例的 C 風格函數(OpenSample、CloseSample、屬性存取函數)。TComponent 的臨時擁有者類(TTempcomp),解決 VCL 控件需 AOwner 的構造需求。implib.exe 將 DLL 轉為 .lib,專案加入 .lib,頭文件聲明 extern "C" __declspec(dllimport) 函數原型。library SampleDLL;
uses
SysUtils, Classes, Sample; {Sample 為原有控件單元}
type
{臨時擁有者類,僅提供構造函數}
TTempcomp = class(TComponent)
public
constructor Create1;
end;
var
Sample1: TSample; {原控件實例}
SaveExit: Pointer; {保存原 ExitProc}
temp1: TTempcomp; {臨時擁有者實例}
constructor TTempcomp.Create1;
begin
{不調用 inherited Create,避免引入額外依賴}
end;
{=== 導出函數 ===}
function OpenSample: Boolean; stdcall; export;
begin
{由臨時擁有者建立控件實例}
Sample1 := TSample.Create(temp1);
Result := Sample1.Actived; {假設 Actived 為 Boolean 屬性}
end;
procedure CloseSample; stdcall; export;
begin
Sample1.Free;
end;
function Actived: Boolean; stdcall; export;
begin
Result := Sample1.Actived;
end;
function Pro1: Integer; stdcall; export;
begin
Result := Sample1.Pro1;
end;
function Pro2: Integer; stdcall; export;
begin
Result := Sample1.Pro2;
end;
{=== DLL 入口/退出處理 ===}
procedure libexit; far;
begin
if Sample1.Actived then
Sample1.Free;
ExitProc := SaveExit;
temp1.Free;
end;
exports
OpenSample, CloseSample, Actived, Pro1, Pro2;
begin
temp1 := TTempcomp.Create1;
SaveExit := ExitProc;
ExitProc := @libexit;
end.
{命令行}
implib SampleDLL.lib SampleDLL.dll
將生成的 SampleDLL.lib 加入 C++Builder 專案(Project → Add to Project)。
// SampleDLL.h
extern "C" __declspec(dllimport) bool __stdcall OpenSample(void);
extern "C" __declspec(dllimport) void __stdcall CloseSample(void);
extern "C" __declspec(dllimport) bool __stdcall Actived(void);
extern "C" __declspec(dllimport) int __stdcall Pro1(void);
extern "C" __declspec(dllimport) int __stdcall Pro2(void);
#include "SampleDLL.h"
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if (OpenSample()) {
bool a = Actived();
int p1 = Pro1();
int p2 = Pro2();
ShowMessage("Delphi 控件屬性讀取成功");
}
CloseSample();
}
constructor Create(AOwner: TComponent) 強制要求擁有者。DLL 無法直接使用 Application(會導致體積膨脹),故建立極簡擁有者類。export,C++ 用 __declspec(dllexport)(構建 DLL 時)與 __declspec(dllimport)(調用時)。TComponent 子類)。可視控件(TControl)涉及視窗句柄、消息泵、父子視窗關係,跨模組極其複雜,不建議此法。| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| DLL 封裝 (本文) | 語言無關、部署獨立、非可視組件完美 | 需手寫導出包裝、可視控件不適用 | 工具類、算法、數據處理組件 |
| C++Builder 直接引用 .pas | 無包裝層、原生調用 | RTL 版本強綁定、名稱修飾問題、調試困難 | 同版本 RAD Studio 專案組 |
| COM/ActiveX 封裝 | 標準化、跨進程、語言無關 | 開發複雜、註冊部署、效能開銷 | 企業級組件、跨進程調用 |
| Package (BPL/Runtime Package) | 共享 VCL RTL、設計時支援 | 版本強綁定、僅限 Delphi/C++Builder 同版本 | 團隊內部組件庫共享 |
將 Delphi 非可視 VCL 控件封裝為標準 DLL,配合 implib 生成導入庫,是 C++Builder 跨語言復用 Delphi 組件最輕量、穩健的方案。關鍵在於臨時擁有者類解決構造依賴、stdcall 確保二進制兼容、ExitProc 保證資源釋放。