Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2022年08月31日] [更新 2022年08月31日] Delphi 一般・その他
   
フォームを実装した DLL の作成
動作確認等 Windows 10 ビルド 19043 Pro 64 bit + Delphi XE5(UP2) Pro VCL-32
[整理番号 - 1180]







フォームを実装した DLL の作成


Delphi で,フォームを実装した DLL を作成する手順と,簡単なサンプルです.



[備考]
32 ビットの DLL は,64 ビットの EXE では使用できません.64 ビットの DLL は 32 ビットの EXE では使用できません.




01_DLL を新規作成してフォームを追加


[ファイル] [新規作成] で [その他] を選択します.起動したダイアログの左ペインの [Delphi プロジェクト] を選択して,右ペインの [ダイナミックリンクライブラリ],つまり,DLL を選択します.



図1
[新規作成] [その他] のダイアログ
  • 左ペインの
    [Delphi プロジェクト] を選択
  • 右ペインの
    [ダイナミックリンクライブラリ] を選択

[ファイル] [プロジェクトに名前を付けて保存] で任意のフォルダに保存します.今回は SampleDLL という名前で保存しています.保存したら,[Ctrl] + [F9] でコンパイルします.DLL が作成されます.
下図は,出力先ディレクトリを . に変更してコンパイルした結果です.デフォルトの出力先は [Win21] フォルダの階層下です.これは,EXE ができる場所と同じです.



図2
保存したファイル類
  • [Ctrl] + [F9] でコンパイルしたので DLL がある

次にフォームを作成します.
[ファイル] [新規作成] で,[VCL フォーム] を選択します.この時現れるダイアログで,VCL フレームワークを有効するために [はい] をクリックします.
今回は TButton1 を 1 つだけ配置したフォームでテストしました.DLL を使用する側のフォームと区別するために,フォームの背景色を変更しています.フォームのアイコンも変更しました.

既存のフォームを使用する場合は,[プロジェクト] [プロジェクトに追加] で,既存のフォームのユニットを選択します.



図3
フォーム設計時の画面
  • TButton を 1 つ配置
  • 背景色を変更
  • フォームのアイコンも変更

オブジェクトインスペクタでフォームの名前を FormInDLL にします.
[ファイル] [名前を付けて保存] で,FormInDLLUnit.pas の名前で保存します.下図は,保存後のファイル一覧です.



図4
保存ファイル類
  • Smiley.ico は 16 x 16 のアイコンファイル

フォームのコードを次にようにして上書き保存します.



リスト1
unit FormInDLLUnit;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TFormInDLL = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  FormInDLL: TFormInDLL;

implementation

{$R *.dfm}

procedure TFormInDLL.Button1Click(Sender: TObject);
begin
  ModalResult := mrOK;
end;

end.




02_DLL のコード


次に DLL のコードを作成します.今回は以下の以下のようにしました.uses 部のフォームのユニット名は,既に追加されているものと同じです.
この DLL は,フォームを ShowModal メソッドでモーダル表示する関数だけの実装となっています.
[Ctrl] + [F9] でコンパイルして,[Cstr] + [S] で上書き保存して IDE を閉じます.



リスト2
library SampleDLL;

uses
  System.SysUtils,
  System.Classes,
  Winapi.Windows,
  Vcl.Forms,
  Vcl.Controls,
  FormInDLLUnit in 'FormInDLLUnit.pas' {FormInDLL};

function FormShowModal(AppHandle: HWND): TModalResult; stdcall;
begin
  Application.Handle:= AppHandle;
  FormInDLL := TFormInDLL.Create(Application);
  try
    Result := FormInDLL.ShowModal;
  finally
    FreeAndNil(FormInDLL);
    Application.Handle := 0;
  end;
end;

exports
  FormShowModal;

begin
end.


フォームを,Show メソッドでモードレス表示する場合は,DllProc の機能を使用して,初期化と終了処理が必要です.以下の記事を参考にしてください.
また,Application 変数 (TApplication) や Screen 変数 (TScreen) を利用す場合も,DllProc による初期化と終了の処理が必要です.



DLL 内でフォームを生成する時,次のように Owner を nil にして生成した場合,DLL 内でフォームを表示すると,タスクバーにそのフォームがアイコンが追加されます.


  FormInDLL := TFormInDLL.Create(nil);




03_DLL を使用するプログラムを作成して実行


新規に VCL フォームアプリケーションを作成します.
フォームに TButton と TEdit を配置します.TEdit は,フォームのモーダル表示の動作確認用です.フォームのユニットのコードを,図の下のコードにします.
このプロジェクトを,DLL のプロジェクトのファイルと同じフォルダ内に保存します.[F9] で実行します.下図はその結果です.

今回は,DLL を EXE と同じフォルダ内に置いてテストしています.



図5
EXE を実公して [Button1] をクリック
  • DLL 内のフォームが表示される
図6
DLL 内のフォームを [OK] をクリック
  • DLL 内のフォームが閉じる

リスト3
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function FormShowModal(AppHandle: HWND):  TModalResult; stdcall; external 'SampleDLL.dll';


procedure TForm1.Button1Click(Sender: TObject);
begin
 if FormShowModal(Application.Handle) = mrOK then begin
    MessageBox(Handle, '[OK]', '結果', MB_ICONINFORMATION);
  end;
end;

end.




04_DLL を動的にロードして使用


前項のサンプルは,DLL が存在しないか,DLL が存在しても,その DLL 内に目的の関数や手続きが存在しない場合は,実行時に例外が発生します.
それを回避するには,次のように DLL を実行時にロードして使用します.これを DLLの動的ロード,あるいは動的リンクと言います.前項のように使用する場合を DLL の静的リンクと言うことがあります.

DLL を動的にロードして使用する場合は,DLL を任意のフォルダに置いて利用できます.



リスト4
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

  // 使用する DLL 内の関数や手続きを型として定義して,その型の変数を宣言
  // 型名と変数名は任意
type
  TFormShowModal = function (AppHandle: HWND):  TModalResult; stdcall;
var
  FShowDlg : TFormShowModal;    


//=============================================================================
//  型定義した DLL 内の関数を使用
//
//  (1) DLL を LoadLibrary でロードして,そのハンドルを取得
//  (2) DLL 内の目的の関数のアドレスを GetProcAddress で取得
//      GetProcAddress の第 2 引数が,実際に使用する DLL 内の関数名または手続き名
//  (3) DLL を使い終わったら FreeLibrary で解放処理
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LDllHandle : THandle;
begin
  LDLLHandle := LoadLibrary('SampleDLL.dll');

  if LDllHandle <> 0 then begin
    try
      @FShowDlg := GetProcAddress(LDllHandle, 'FormShowModal');
      if @FShowDlg <> nil then begin
        if FShowDlg(Application.Handle) = mrOK then begin
          MessageBox(Handle, '[OK]', '結果', MB_ICONINFORMATION);
        end;
      end;
    finally
       FreeLibrary(LDllHandle);
    end;
  end;
end;

end.