Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2005年04月02日] [更新 2017年11月04日] Delphi サンプルプログラム集
   
DLL 使用サンプルの利用方法
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
001_UseDLLProject.zip [4,378 KB] 2017年11月04日版(EXE同梱)







01_DLL のプロジェクトを開いてコンパイル ( [ Ctrl ] + [ F9 ] )


このサンプルプログラム集には,既成の DLL を使用するのではなく,サンプルとして DLL を作成して,その DLL を使用するサンプルがあります.
DLL を使用するプログラムではまず,以下の手順で DLL を作成した後で,本体のプロジェクトをコンパイル.実行して下さい.DLL を同梱しているサンプルもありますが,その DLL のコードを修正変更した場合も同様に修正変更した DLL をコンパイルし直す必要があります.
  • (1) DLL のプロジェクトを IDE で開く
  • (2) [Ctrl] + [F9] でコンパイルする
  • (3) サンプルプログラムの本体プロジェクトを開いて [F9] で実行する


図1
plHookCBT.dpr を開く
図2
[Ctrl] + [F9]
  • DLL と dcu が作成される




02_プロジェクトグループを利用


DLL を使用したサンプルの様に,複数のプロジェクトを使用するアプリケーションでは,プロジェクトグループを作成すると便利な場合があります.既存のプロジェクトに,[プロジェクト] [既存のプロジェクト追加] で追加できます.もちろん,新規のプロジェクトも追加可能です.

次回からは,このプロジェクトグループを IDE で開けば,同時の複数のプロジェクトの編集ができることになります.


[備考]
DLL のプロジェクトもプロジェクトグループに追加して IDE 上でデバックできますが,1 つのプロジェクトは 1 つのプログラムです.つまり,DLL は,アプリケーションで言えば,全く別のアプリケーションです.このことに注意する必要があります.



図3
プロジェクトグループの例
  • 編集対象となるのは太字のプロジェクト
  • プロジェクトグループでの保存もできる




03_サンプルで使用している DLL 内の処理コードについて


当サイトのサンプルプログラムでは,DLL にしなければ動作しないサンプル以外は基本的に DLL を使用していません.これは,Delphi の特徴である,ランタイム等を必要としないアプリケーションが作成できる,ということにもつながります.

各サンプルの DLL 内のコードは,汎用性があるわけではありません.DLL を使用しているサンプルは,ほとんどフック関数の関係ですが,フックの処理内容によっては,他のプロセスにメッセージを送らない方がいい場合もあります.また一方では他のプロセスにメッセージを送らないと,システム全体に影響を与えることもあります.
したがって,汎用性のある DLL のコードを作成するには,それなりの工夫が必要となります.





04_DLL 内の関数で外部に文字列を渡す


もし DLL 側に文字列を渡したり,DLL 側から文字列を受け取る必要がある場合は,String 型ではなく,PChar 型の変数とした方が確実で間違いがありません.

DLL 内の関数の引数で外部に文字列を渡すには,例えば以下のようにします.
Delphi の String 型では,文字列の長さの設定をしなくても文字列を扱えます.Windows API の文字列型である PChar 型はバッファとしての文字列の領域の確保が必要です.そこで,引数のバッファの値を nil にすると必要なバッファサイズが取得できるようにしています.この方法は,Windows API で文字列を扱う際によく使用されている方法です.



図4
DLL 内の関数に文字列を渡す
  • このダイアログは DLL 内部で表示している
図5
DLL から文字列を取得

リスト1
DLL のコード
外部から文字列を受け取る関数と文字列を外部に渡す関数だけを実装
library DllTest_PChar;

uses
  Windows,
  SysUtils;

//-----------------------------------------------------------------------------
//  DLL内に文字列を渡す
//-----------------------------------------------------------------------------
procedure SetText2Dll(Buff: PChar); stdcall;
var
  LText : string;
begin
  LText := string(Buff);
  MessageBox(0, PChar(LText), 'DLL 受信文字列', MB_ICONINFORMATION);
end;

//-----------------------------------------------------------------------------
//  DLL内の文字列を外部に渡す関数
//  第1引数をnilにすると必要なバッファの確保サイズの取得となる
//-----------------------------------------------------------------------------
function GetDllText(Buff: PChar; Len: DWORD): DWORD; stdcall;
var
  LText : string;
begin
  LText := 'DLL の文字列取得';

  //+1はnull終端文字の分
  Result := Length(LText) + 1;
  if Buff <> nil then begin
    //呼出し側で確保した(メモリまたは文字列の)領域に文字列をコピーする
    SysUtils.StrPLCopy(Buff, LText, Len);
    Result := Length(LText);
  end;
end;

//-----------------------------------------------------------------------------
exports
  SetText2Dll,
  GetDllText;

end.


DLL を使用する外部のプログラムからは,例えば以下のようにして文字列を取得します.
このコードは PChar 型を使用していますが,参考リンクに String 型で取得する例があります.



リスト2
DLL を使用するプログラムのコード
implementation

{$R *.dfm}

procedure SetText2Dll(Buf: PWChar); stdcall; external 'DllTest_PChar.dll';
function GetDllText(Buf: PChar; Len: DWORD): DWORD; stdcall; external 'DllTest_PChar.dll';

//=============================================================================
//  DLLに文字列を渡す
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LText : string;
begin
  LText := '日本語';
  SetText2Dll(PChar(LText));
end;

//=============================================================================
//  DLL内の関数から文字列を取得
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LLen   : DWORD;
  LPChar : PChar;
  LText  : string;
begin
  LLen := GetDllText(nil, 0);
  if LLen > 0 then begin
    //文字列を格納するメモリ領域を確保
    LPChar := StrAlloc(LLen);
    try
      GetDllText(LPChar, LLen);

      //取得した文字列を使用する処理
      LText := LPChar;
      MessageBox(Handle, PChar(LText), '結果', MB_ICONINFORMATION);
    finally
      StrDispose(LPChar);
    end;
  end;
end;


当然ですが,DLL 内の文字列を取得する際,以下のように十分大きな領域を確保すれば必要なバッファサイズを取得する必要はありません.ファイルのフルパス名の取得のように,取得すべき長さが限定されている場合も同じように考えることができます.
取得文字列の長さを限定する場合,文字列のバッファとして Char 型の静的配列も使用できます.



//-----------------------------------------------------------------------------
//  DLL内の関数から文字列を取得
//-----------------------------------------------------------------------------
procedure TForm1.Button2Click(Sender: TObject);
var
  LPChar : PChar;
  LLen   : DWORD;
begin
  LLen := 100;
  //文字列を格納するメモリ領域を確保
  LPChar := StrAlloc(LLen);
  try
    GetDllText(LPChar, LLen);

    //取得した文字列を使用する処理
    MessageBox(Handle, LPChar, '結果', MB_ICONINFORMATION);
  finally
    StrDispose(LPChar);
  end;
end;

//=============================================================================
//  DLL内の関数から文字列を取得
//  取得可能な文字列の長さが限定されている場合
//  文字列の初期化のFillCharの第2引数はバイト数のSizeOfとする
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LBuffer : array [0..MAx_PATH - 1] of Char;
  LText   : string;
  LLen    : DWORD;
begin
  FillChar(LBuffer, SizeOf(LBuffer), #0);

  LLen := GetDllText(LBuffer, Length(LBuffer));
  if LLen > 0 then begin
    //取得した文字列を使用する処理
    LText := LBuffer;
    MessageBox(Handle, PChar(LText), '結果', MB_ICONINFORMATION);
  end;
end;




05_文字列変数を DLL の入出力兼用の引数として使用する方法


Delphi 専用の DLL として作成するのであれば String 型の変数にすれば入出力兼用の引数として使用できます.本サンプルは,引数を PChar 型の文字列として扱う場合です.
一般的に DLL に文字列を渡す文字列変数と,文字列を取得する変数は別にします.これは他のアプリに対する操作の場合も同じです.Windows API の関数類でもそうなっています.
どうしても入出力兼用にしたい場合は,引数の文字列変数を Char 型の静的配列の変数にすると,その変数を入出力兼用の変数として使用可能にできます.そのテスト用のサンプルです.

StrAlloc 関数のように,メモリ領域を設定して,そこに文字列を格納する方法では入出力兼用の引数として使用できません.メモリ領域の確保は,プロシージャーあるいはそのアプリだけに対して有効です.DLL や他のアプリ (プロセス) にそのメモリ領域が確保されるわけではありません.DLL や他のアプリ (プロセス) とメモリ領域を共有するには共有メモリ,あるいはメモリマップドファイルという仕組みを使用する必要があります.
Char 型の静的配列は null 終端文字列用の領域を確保してくれます.



図6
実行結果
  • 「日本語」は DLL に渡した文字列
  • それ以降は DLL 内で追加した文字列

リスト3
文字列引数を持つ DLL の関数の例
引数の文字列を Char 型の静的配列専用とする場合
library DllTest_PChar;

uses
  Windows,
  SysUtils;

//-----------------------------------------------------------------------------
//  DLL内の文字列を外部に渡す関数
//  呼び出し側で文字列の領域をChar型の静的配列で設定した場合の専用コード
//-----------------------------------------------------------------------------
function GetDllText(Buff: PChar; Len: DWORD): DWORD; stdcall;
var
  LText : string;
  LLen  : DWORD;
begin
  LText := string(Buff) + '+DLL内で取得した文字';

  //+1はnull終端文字の分
  LLen   := Length(LText) + 1;
  Result := LLen;
  if LText <> '' then begin
    //呼出し側で確保した領域に文字列をコピーする
    SysUtils.StrPLCopy(Buff, LText, LLen);
    Result := Length(LText);
  end;
end;

//-----------------------------------------------------------------------------
exports
  GetDllText;

end.


上の DLL の関数を使用する例です.実行結果は上の図の通りです.



リスト4
上の DLL の関数を使用する例
implementation

{$R *.dfm}

function GetDllText(Buf: PChar; Len: DWORD): DWORD; stdcall; external 'DllTest_PChar.dll';

//=============================================================================
//  DLL内の関数から文字列を取得
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LBuffer : array [0..32000 - 1] of Char;
  LText   : string;
  LLen    : DWORD;
begin
  FillChar(LBuffer, SizeOf(LBuffer), #0);

  LBuffer := '日本語';
  LLen := GetDllText(LBuffer, Length(LBuffer));
  if LLen > 0 then begin
    //取得した文字列を使用する処理
    LText := string(LBuffer);

    //Charの要素数が大きすぎると以下の行は正常に処理できない
    //ShowMessage(LText)であれば動作する
    LText := LText + sLineBreak + '文字数 : ' + IntToStr(LLen);
    MessageBox(Handle, PChar(LText), '結果', MB_ICONINFORMATION);
  end;
end;


[備考 1]
このサンプルの方法は,引数の文字列の型が Char 型の静的配列の場合に限られます.決して汎用的とは言えません.入力用と出力用の引数は別々にする方が無難で確実と言えます.


[備考 2]
整数型等の変数を DLL の引数に使用した場合は,サイズが固定なので DLL に渡すのも,DLL 側から値を受け取る入出力兼用の変数として使用できます.


[備考 3]
DLL 内の変数の値は,その DLL を使用するアプリ固有の値となります.この仕組みによって,同じ DLL 内の関数や変数を複数のプログラムから使用できるようになっています.あるプログラムで DLL 内の変数に値を設定しても,その値を別のプログラムから取得することはできません.





06_DLL 内で DLL 自身のフルパスを取得


DLL 内で DLL 自身のフルパスを取得する例です.
GetModuleFileName 関数を使用して取得します.GetModuleName 関数の引数を 0 にすると,この DLL の関数を呼び出した EXE のフルパスの取得になります.ParamStr(0) の値もこの DLL 内の関数を呼び出した EXE のフルパスの取得となります.これらの値も取得するようにしています.



図7
実行結果
  • 最初の値が DLL 自身のフルパス
  • 後の 2 つは DLL を呼び出した EXE のフルパス
  • 全て DLL 内で取得した値

リスト5
DLL 内で DLL 自身のフルパスを取得する関数の例
library DllTest_PChar;

uses
  Windows,
  SysUtils;

//-----------------------------------------------------------------------------
//  DLL自身のフルパスを取得する関数
//  ついでにこのDLLを呼び出したEXEのフルパスも取得
//  
//  引数のLenには十分サイズの値を呼び出し側で設定する
//-----------------------------------------------------------------------------
function GetDllPath(Buff: PChar; Len: DWORD): DWORD; stdcall;
var
  LText : string;
begin
  Result := 0;
  //このDLL自身のフルパスを取得
  //このifの条件は実際には不要(Libraryであることは間違いないので)
  if IsLibrary then begin
    LText  := GetModuleName(HInstance);
    Result := Length(LText);
  end;

  //このDLL呼び出したEXEのフルパスを取得して追加
  LText := LText + sLineBreak + GetModuleName(0);

  //このDLL呼び出したEXEのフルパスをParamStr(0)で取得した値も追加
  LText := LText + sLineBreak + ParamStr(0);

  //呼出し側で確保した(メモリまたは文字列の)領域に文字列をコピーする
  SysUtils.StrPLCopy(Buff, LText, Len);
end;

//-----------------------------------------------------------------------------
exports
  GetDllPath;

end.


上の DLL の関数を使用する例です.実行結果は上の図の通りです.



リスト6
上の DLL の関数を使用する例
implementation

{$R *.dfm}

function GetDllPath(Buf: PChar; Len: DWORD): DWORD; stdcall; external 'DllTest_PChar.dll';

//=============================================================================
//  DLL自身のフルパスを取得
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LPChar : PChar;
  LLen   : DWORD;
begin
  LLen := 1025;
  //文字列の領域を確保
  LPChar := StrAlloc(LLen);
  try
    GetDllPath(LPChar, LLen);

    //取得した値を使用する処理
    MessageBox(Handle, LPChar, '結果', MB_ICONINFORMATION);
  finally
    StrDispose(LPChar);
  end;
end;




07_DLL をリソースとして EXE に埋め込む


DLL を使用するアプリは DLL も配布する必要があります.そこで, DLL を リソースとして EXE に埋め込んでしまう方法が考えられます.そうすれば,配布の際に DLL は必要ありません.

本項は,EXE に埋め込んだ DLL のテストです.テストに使用する DLL は以下のように,関数を 1 つだけ実装した簡単な内容としています.このプロジェクトをコンパイルして DLL を作成します.



リスト7
テストに使用した DLL のプロジェクト plTestDLL.dpr のコード
DLL 内の関数は 1 つだけ (ダイアログを表示する関数)
library plTestDLL;

uses
  Windows;

function DiaplayDialog(hHandle: HWND; Text: PChar): Boolean; stdcall;
begin
  Result := True;
  MessageBox(hHandle, Text, 'DLL のダイアログ', MB_ICONINFORMATION);
end;

exports
  DiaplayDialog;
end.


DLL を作成したら新規にプロジェクトを作成します.そして,[プロジェクト] [リソースと画像] から下図の [リソースマネージャ] を表示して,[追加] ボタンで対象の DLL を追加します.
プロジェクトをコンパイルすれば,リソースに DLL が格納されます.リソースを変更したら,プロジェクトをビルドまたは再構築します.


図8
[リソースと画像] のダイアログ
  • このダイアログは [リソースマネージャー] とも言う
  • リソース識別子の名前は任意.ここではデフォルトのままで使用

下に示すコードが EXE に埋め込んだ DLL を使用するサンプルです.
ビットマップ画像や JPEG 画像であれば,以下のような,LoadFromStream というメソッドで,メモリから画像を読み込むことができますが,DLL ではそのような操作はできません.そこで,リソースから読み込んだ DLL をディスクに保存して使用します.

後は,DLL を動的にロードして使用します.


  TMemo.Lines.LoadFromStream(TResourceStream);
  TBitmap.LoadFromStream(TResourceStream);
  TJpegImage.LoadFromStream(TResourceStream);
  sndPlaySound(TResourceStream.Memory, SND_MEMORY or SND_SYNC);



図9
実行結果
  • このダイアログは,DLL 内の関数が表示している

リスト8
DLL を使用するプログラムのコード
リソースから取り出した DLL をディスクに保存してから利用する
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    FDLLHandle   : THandle;
    FShowDlgFunc : function (hHandle: HWND; AText: PChar): BOOL; stdcall;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  LDllName    : string;
  LResStream  : TResourceStream;
  LFilePath   : string;
begin
  LDllName := 'plTestDLL.dll';
  //対象のリソースの存在を確認
  if FindResource(hInstance, 'Resource_1', RT_RCDATA) = 0 then exit;

  //リソースを読み出してディスクに保存
  LResStream := TResourceStream.Create(hInstance, 'Resource_1', RT_RCDATA);
  LFilePath  := ExtractFilePath(Application.ExeName) + LDllName;
  try
    LResStream.SaveToFile(LFilePath);
  finally
    FreeAndNil(LResStream);
  end;


  //DLLをロード
  FDLLHandle := LoadLibrary(PChar(LFilePath));
  if FDLLHandle <> 0 then begin
    //DLL内の関数のアドレスを取得
    //関数が複数ある場合は各々の関数のアドレスを取得
    //あるいはその関数を使用する時に取得
    @FShowDlgFunc := GetProcAddress(FDLLHandle, 'DiaplayDialog');
  end;
end;

//=============================================================================
//  フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  if FDLLHandle <> 0 then begin
    FreeLibrary(FDLLHandle);
  end;
end;

//=============================================================================
//  DLL内の関数の実行
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  if @FShowDlgFunc <> nil then begin
    FShowDlgFunc(Handle, 'リソース DLL のテスト');
  end;
end;

end.




08_リソースからロードした DLL を直接使用 - MemoryModule


リソースとして埋め込んだ DLL をディスクに保存しないで,メモリ上で操作して使用する方法があります.そのためには,EXE と DLL の構造を解析して操作する必要がありますが,そのような操作用のコードを提供しているサイトがあります.



上記の DelphGear.cn から mm_20130708.zip をダウンロードしてテスしてみました.ダウンロードして解凍したユニット MemoryModule を使用します.
前項と同じ DLL のプロジェクトを使用して,32 ビット と 64 ビットの DLL を作成しました.そして,32 ビットと 64 ビットの EXE を作成して動作を確認しました.EXE は Delphi XE(UP1) Pro と Delphi XE7 でコンパイルして作成しました.Windows 7 U64(SP1) 上で実行しています.
下に示すのがテストに使用したコードです.実行結果は前項と同じです.

警告が幾つか現れます.気になるのであれば修正するか,警告の表示を無効にします.



図10
実行結果
  • このダイアログは,DLL 内の関数が表示している

リスト9
リソースの DLL をディスクに保存しないで利用する方法
DelphiGear.cn からダウンロードした MemoryModule というユニットを使用する例
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    FMemModule : NativeUInt;
    FShowDlgFunc : function (hHandle: HWND; AText: PChar): BOOL; stdcall;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  MemoryModule;

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  LResStream : TResourceStream;
begin
  //対象のリソースの存在を確認
  if FindResource(hInstance, 'Resource_1', RT_RCDATA) = 0 then exit;

  //リソースを読み出す
  LResStream := TResourceStream.Create(hInstance, 'Resource_1', RT_RCDATA);
  try
    FMemModule := MemLoadLibrary(LResStream.Memory);

    if FMemModule <> 0 then begin
      //DLL内の関数のアドレスを取得
      //関数が複数ある場合は各々の関数のアドレスを取得
      //あるいはその関数を使用する時に取得
      @FShowDlgFunc := MemGetProcAddress(FMemModule, 'DiaplayDialog');
    end;
  finally
    FreeAndNil(LResStream);
  end;
end;

//=============================================================================
//  フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  if FMemModule <> 0 then begin
    MemFreeLibrary(FMemModule);
  end;
end;

//=============================================================================
//  DLL内の関数の実行
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  if @FShowDlgFunc <> nil then begin
    FShowDlgFunc(Handle, 'リソース DLL のテスト');
  end;
end;

end.




09_リソースからロードした DLL を直接使用 - BTMemoryModule


下記の DSPlayer のサイトに GitHub へのリンクがあります.そこから BTMemoryModule と言うユニットがダウンロードできます.この BTMemoryModule も,リソースから取り出した DLL を,メモリ上で操作できます.デモプログラムも同梱されています.

下のコードは,この BTMemoryModule を使用する例です.動作結果は前項のサンプルと同じです.
BTMemoryModule は 32 ビットの EXE, DLL 専用です.



[備考]
BTMemoryModule の最新版は,2016年05月20日の時点では,参考リンクの GitHub にある v0.0.42 です.これは 14.04.2015 の News の記事にリンクがあります.
下記の Google Code Archive には,0.0.41, 0.0.4 があります.これらは同サイトの Download のページにリンクがあります.これらのユニットを使用して,下のコードを実行すると,アプリの終了時に例外が発生します.OnCreate イベント内で実行しているコードを,OnShow イベント内で実行すれば例外は発生しません.


リスト10
リソースの DLL をディスクに保存しないで利用する方法
BTMemoryModule というユニットを使用する例
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, BTMemoryModule;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    FPBTMemModule : PBTMemoryModule;
    FShowDlgFunc : function (hHandle: HWND; AText: PChar): BOOL; stdcall;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  LResStream : TResourceStream;
begin
  //対象のリソースの存在を確認
  if FindResource(hInstance, 'Resource_1', RT_RCDATA) = 0 then exit;

  //リソースを読み出す
  LResStream := TResourceStream.Create(hInstance, 'Resource_1', RT_RCDATA);
  try
    FPBTMemModule := BTMemoryLoadLibary(LResStream.Memory, LResStream.Size);
    if FPBTMemModule <> nil then begin
      //DLL内の関数のアドレスを取得
      //関数が複数ある場合は各々の関数のアドレスを取得
      //あるいはその関数を使用する時に取得
      @FShowDlgFunc := BTMemoryGetProcAddress(FPBTMemModule, 'DiaplayDialog');
    end;
  finally
    FreeAndNil(LResStream);
  end;
end;

//=============================================================================
//  フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  if FPBTMemModule <> nil then begin
    BTMemoryFreeLibrary(FPBTMemModule);
  end;
end;

//=============================================================================
//  DLL内の関数の実行
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  if @FShowDlgFunc <> nil then begin
    FShowDlgFunc(Handle, 'リソース DLL のテスト');
  end;
end;

end.