首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

Delphi DLL 封裝 VCL MDI 子視窗:Application/Screen 重導向與匯出函數完整實作

     編輯:Delphi

前言

將 VCL MDI 子視窗封裝進 DLL 供主程式載入,核心挑戰在於VCL 全域物件共享:DLL 與 EXE 各自擁有獨立的 Application、Screen 實例,若不重導向,子視窗將無法正確處理訊息迴路、提示視窗、MDI 父子關係等。本文提供完整的 DLL 專案範本與關鍵技術細節。

DLL 專案檔關鍵程式碼

1. 重寫 DLL 入口點,備份/還原 VCL 全域物件

library MDIChildDLL;

uses
  SysUtils,
  Classes,
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

var
  DllApp: TApplication; // 備份 DLL 原始 Application
  DllScr: TScreen;      // 備份 DLL 原始 Screen

procedure UnProcDll(Reason: Integer); register;
begin
  if Reason = DLL_PROCESS_DETACH then
  begin
    // 卸載時還原,確保 VCL 正常清理
    Application := DllApp;
    Screen := DllScr;
  end;
end;

begin
  // DLL 載入時:備份原始全域物件
  DllApp := Application;
  DllScr := Screen;
  // 掛鉤自定義入口點
  DllProc := @UnProcDll;
end.

2. 匯出函數:接收主程式 Application/Screen 並建立子視窗

function ShowForm(App: TApplication; Scr: TScreen; Owner: TForm): TForm; export; stdcall;
begin
  // 關鍵:重導向 VCL 全域物件至主程式實例
  Application := App;
  Screen := Scr;

  // 建立 MDI 子視窗 (Owner 為 MDI 父視窗)
  if not Assigned(Form1) then
  begin
    Form1 := TForm1.Create(Owner); // Owner 設為 MDI 父視窗
    Result := Form1;
  end
  else
    Result := Form1;
end;

exports
  ShowForm;

子視窗單元 (Unit1.pas) 關鍵設定

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  // MDI 子視窗必要屬性
  FormStyle := fsMDIChild;
  // 視需要設定
  // BorderIcons := [biSystemMenu, biMaximize, biMinimize];
  // WindowState := wsMaximized;
end;

end.

主程式 (EXE) 呼叫範例

type
  TShowFormFunc = function(App: TApplication; Scr: TScreen; Owner: TForm): TForm; stdcall;

var
  DLLHandle: THandle;
  ShowForm: TShowFormFunc;
  ChildForm: TForm;
begin
  DLLHandle := LoadLibrary('MDIChildDLL.dll');
  if DLLHandle <> 0 then
  try
    @ShowForm := GetProcAddress(DLLHandle, 'ShowForm');
    if Assigned(ShowForm) then
    begin
      // 傳入主程式的 Application、Screen、MDI 父視窗 (Self)
      ChildForm := ShowForm(Application, Screen, Self);
      if Assigned(ChildForm) then
        ChildForm.Show;
    end;
  finally
    FreeLibrary(DLLHandle);
  end;
end;

核心原理解析

問題原因解決方案
子視窗不顯示/閃爍 DLL 獨自擁有 Application.Handle,訊息迴路不通 Application := App 共用主程式訊息泵
提示視窗位置錯誤 Screen 物件不共用,螢幕座標計算錯誤 Screen := Scr 共用螢幕資訊
MDI 父子關係失效 Create(Owner) 時 Owner 需為真實 MDI 父視窗 主程式傳入 Self (MDI 父視窗)
DLL 卸載時崩潰 VCL 釋放順序錯誤,存取已釋放的全域物件 DLL_PROCESS_DETACH 時還原 Application/Screen

進階:多實例管理

若需支援多個子視窗實例,改用類別工廠模式:

function CreateMDIChild(App: TApplication; Scr: TScreen; Owner: TForm): TForm; stdcall;
var
  Child: TForm1;
begin
  Application := App;
  Screen := Scr;
  Child := TForm1.Create(Owner);
  Child.FormStyle := fsMDIChild;
  Result := Child;
end;

注意事項與最佳實務

  • 編譯模式一致:DLL 與 EXE 必須使用相同 Delphi 版本、相同執行期套件設定 (Runtime Packages)
  • 共用記憶體管理器:專案選項勾選 "Build with runtime packages",確保 ShareMem 機制生效 (或在兩端專案檔首行加入 ShareMem)
  • 例外處理跨邊界:DLL 內部例外需自行捕獲,避免跨越 DLL 邊界傳播導致主程式崩潰
  • 資源釋放:主程式關閉時應主動呼叫 DLL 匯出函數釋放子視窗,再 FreeLibrary
  • 除錯技巧:IDE 設定 "Debug DCU"、DLL 專案設定 "Host Application" 指向主程式 EXE,可跨邊界單步除錯

結語

DLL 封裝 VCL MDI 子視窗的關鍵在於 VCL 全域物件重導向 與 正確的擁有者關係建立。掌握此模式,即可實現大型系統的模組化部署:核心框架為 EXE,業務模組為 DLL,按需載入、獨立編譯部署,極大提升維護彈性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved