Delphi Programming / Object Pascal
[掲載 2005年04月02日] [更新 2019年11月22日] Delphi サンプルプログラム集
DLL 使用サンプルの利用方法
動作確認等
Windows 7 U64(SP1) + Delphi XE(UP1) Pro
001_UseDLLProject.zip [4,378 KB] 2019年11月22日版 (EXE 同梱)
このサンプルプログラム集には、既成の DLL を使用するのではなく、サンプルとして DLL を作成して、その DLL を使用するサンプルがあります。
DLL を使用するプログラムではまず、以下の手順で DLL を作成した後で、本体のプロジェクトをコンパイル。実行して下さい。DLL を同梱しているサンプルもありますが、その DLL のコードを修正変更した場合も同様に修正変更した DLL をコンパイルし直す必要があります。
(1) DLL のプロジェクトを IDE で開く
(2) [Ctrl] + [F9] または [Shift] + [F9] でコンパイルする
(3) サンプルプログラムの本体プロジェクトを開いて [F9] で実行する
図1
plHookCBT.dpr を開く
図2
[Ctrl] + [F9]
DLL を使用したサンプルの様に、複数のプロジェクトを使用するアプリケーションでは、プロジェクトグループを作成すると便利な場合があります。既存のプロジェクトに、[プロジェクト] [既存のプロジェクト追加] で追加できます。もちろん、新規のプロジェクトも追加可能です。
次回からは、このプロジェクトグループを IDE で開けば、同時の複数のプロジェクトの編集ができることになります。
[備考]
DLL のプロジェクトもプロジェクトグループに追加して IDE 上でデバックできますが、1 つのプロジェクトは 1 つのプログラムです。つまり、DLL は、アプリケーションで言えば、全く別のアプリケーションです。このことに注意する必要があります。
図3
プロジェクトグループの例
編集対象となるのは太字のプロジェクト
プロジェクトグループでの保存もできる
03_ サンプルで使用している DLL 内の処理コードについて
当サイトのサンプルプログラムでは、DLL にしなければ動作しないサンプル以外は基本的に DLL を使用していません。これは、Delphi の特徴である、ランタイム等を必要としないアプリケーションが作成できる、ということにもつながります。
各サンプルの DLL 内のコードは、汎用性があるわけではありません。DLL を使用しているサンプルは、ほとんどフック関数の関係ですが、フックの処理内容によっては、他のプロセスにメッセージを送らない方がいい場合もあります。また一方では他のプロセスにメッセージを送らないと、システム全体に影響を与えることもあります。
したがって、汎用性のある DLL のコードを作成するには、それなりの工夫が必要となります。
04_ DLL 内の関数や手続き内の処理結果の文字列を取得
関数や手続きで文字列の引数扱う場合、同じプロジェクト内の関数や手続きであれば、String 型の変数を使用できます。Strring 型の変数の前に予約語 var を付ければ入出力兼用の引数にすることができます。
DLL 内の関数や手続きの場合、DLL の汎用性ということを考慮すると、PChar 型の変数とした方が確実で間違いがありません。
以下のサンプルは、引数に PChar 型の変数を使用する例です。
関数や手続きの PChar
型の引数から文字列を取得するには、PChar 型の文字列を格納するバッファとしてのメモリ領域の確保が必要です。確保したメモリは解放処理が必要です。
このサンプルの関数では、引数の PChar 型変数の値を nil にすると必要なメモリのサイズをバイト数で取得するようにしています。この方法は、Windows API で文字列を扱う際によく使用されている方法です。
関数や手続き内部と呼び出し側では、メモリを確保した変数には文字列を直接代入しないようにします。文字列をセットする時は、StrPCopy や StrPLCopy
関数あるいは MoveChars 関数等を使用して、確保したメモリ領域のメモリに文字列をコピーします。
メモリを確保した変数に文字列を直接代入すると、確保したメモリ以外の場所に文字列が格納されます。その結果、期待する処理結果が取得できないことになります。インデックス (添字) を指定して文字単位での代入であれば問題ありません。また、メモリを開放した後であれば、その変数に文字列を直接代入しても構いません。
図4
DLL 内の関数に文字列を渡す
図5
DLL から文字列を取得
リスト1
DLL のコード
外部から文字列を受け取る関数と文字列を外部に渡す関数だけを実装
copy code
library DllTest_PChar;
uses
Windows,
SysUtils;
// -----------------------------------------------------------------------------
// DLL内に文字列を渡す
// -----------------------------------------------------------------------------
procedure SetText2Dll(Buff: PChar); stdcall ;
var
LText : string ;
begin
LText := 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
// 呼出し側で確保した(メモリまたは文字列の)領域に文字列をコピーする
// Buff := PChar(LText); のような直接代入はしないこと
SysUtils.StrPLCopy(Buff, LText, Result);
Result := Length(LText);
end ;
end ;
// -----------------------------------------------------------------------------
exports
SetText2Dll,
GetDllText;
end .
DLL を使用する外部のプログラムからは、例えば以下のようにして文字列を取得します。
copy code
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);
// 取得した文字列を使用する処理
// LPChar に文字列を直接代入しないこと
LText := LPChar;
MessageBox(Handle, PChar(LText), '結果' , MB_ICONINFORMATION);
finally
StrDispose(LPChar);
end ;
// LPChar のメモリを解放したので, LPChar に文字列を代入してもよい
end ;
end ;
当然ですが、DLL 内の文字列を取得する際、十分大きな領域を確保すれば、以下のように、必要なバッファサイズを取得する必要はありません。ファイルのフルパス名の取得のように、取得すべき長さが限定されている場合も同じように考えることができます。
copy code
// =============================================================================
// DLL内の関数から文字列を取得
// 十分なメモリを確保していれば必要な文字数取得処理は不要
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LLen : DWORD;
LPChar : PChar;
LText : string ;
begin
LLen := 100 ;
LPChar := StrAlloc(LLen);
try
GetDllText(LPChar, LLen);
LText := LPChar;
MessageBox(Handle, PChar(LText), '結果' , MB_ICONINFORMATION);
finally
StrDispose(LPChar);
end ;
end ;
文字列を Char 型の静的な文字配列で受け取る場合の例です。
Char 型の静的配列の変数は PChar 型の引数にそのまま渡せます。静的な配列の場合、FillChar 手続きで初期化しておかないと結果の文字列にゴミが入ることがあります。
copy code
// =============================================================================
// DLL内の関数から文字列を取得
// Char 型の静的配列を渡して DLL から文字列を取得する場合
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LLen : DWORD;
LBuffer : array [0 ..100 ] of Char;
LText : string ;
begin
// 配列要素の初期化
FillChar(LBuffer, SizeOf(LBuffer), #0 );
LLen := Length(LBuffer);
GetDllText(LBuffer, LLen);
LText := LBuffer;
MessageBox(Handle, PChar(LText), '結果' , MB_ICONINFORMATION);
end ;
文字列を Char 型の動的な文字配列で受け取る場合の例です。
Char 型の動的配列の変数は PChar でキャストすれば PChar 型の引数に渡せます。SetLength 手続きを使用して文字配列の文字数を設定すればメモリの確保となります。動的な配列の場合は、SetLength 手続きでメモリを確保すると、確保したメモリ領域が初期化されます。
copy code
// =============================================================================
// DLL内の関数から文字列を取得
// Char 型の動的配列を渡して DLL から文字列を取得する場合
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LLen : DWORD;
LBuffer : array of Char;
LText : string ;
begin
LLen := 100 ;
SetLength(LBuffer, LLen);
GetDllText(PChar(LBuffer), LLen);
LText := string (LBuffer);
MessageBox(Handle, PChar(LText), '結果' , MB_ICONINFORMATION);
end ;
文字列を String 型の変数で受け取る場合の例です。
String 型の変数は PChar でキャストすれば PChar 型の引数に渡せます。バッファとしてのメモリの確保には SetLength
関数を使用します。
copy code
// =============================================================================
// DLL内の関数から文字列を取得
// String 型の変数を渡して DLL から文字列を取得する場合
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LLen : DWORD;
LBuffer : string ;
LText : string ;
begin
LLen := 100 ;
SetLength(LBuffer, LLen);
GetDllText(PChar(LBuffer), LLen);
LText := LBuffer;
MessageBox(Handle, PChar(LText), '結果' , MB_ICONINFORMATION);
end ;
[備考]
一般的に、DLL 内の関数の処理結果は引数で取得するようにします。
DLL 内の関数で発生したエラーは、通常は、例外を含めて DLL 内の関数を使用する側からは捕捉できません。そこで、DLL 内の関数の戻り値でエラー等を検出するようにします。Windows が提供している Windows API の関数類の多くはそういう仕様になっています。
05_ 文字列変数を関数や手続きの入出力兼用の引数として使用
関数や手続きの PChar 型の引数を入出力兼用として使用する例です。
PChar 型の引数を入出力兼用しして使用する場合も、前項と同様、その PChar 型の変数用のメモリ領域を確保します。確保したメモリは解放処理が必要です。関数や手続き内部での処理、呼び出し側における変数の扱いについても前項と同じです。
引数を入出力兼用にする場合は、処理内容によっては、呼出し側から受け取った文字列を、関数や手続き内部で退避する等の処理が必要になることがあります。
図6
実行結果
「日本語」は DLL に渡した文字列
それ以降は DLL 内で追加した文字列
リスト3
PChar 型の文字列引数を持つ DLL の関数の例
copy code
library DllTest_PChar;
uses
Windows,
SysUtils;
// -----------------------------------------------------------------------------
// DLL内の文字列を外部に渡す関数
// 第 2 引数を負の値にすると呼出し側で確保に必要な文字数だけをを返す
// サイズが不足している場合は呼出し側で ReallocMemory 関数で再割り当てが必要
// この文字数には Null 終端文字の分も含まれる
// -----------------------------------------------------------------------------
function GetDllText(ABuff: PChar; ALen: Integer): DWORD; stdcall ;
var
LText : string ;
LLen : DWORD;
begin
// 引数から受け取った文字列を退避
LText := ABuff;
// DLL 内での文字列の処理例
// ここでは単に引数から受け取った文字列と結合
LText := LText + 'DLL内で取得した文字' ;
// 処理の結果の文字列が引数の文字列よりも短くなった場合
// ゴミが入らないようにする
if Length(LText) < StrLen(ABuff) then begin
ZeroMemory(ABuff, StrLen(ABuff) * SizeOf(Char));
end ;
// +1はnull終端文字の分
LLen := Length(LText) + 1 ;
if ALen < 0 then begin
Result := LLen;
end else begin
// 呼出し側で確保した領域に文字列をコピーする
// ABuff := PChar(LText); のような直接代入はしないこと
SysUtils.StrPLCopy(ABuff, LText, LLen);
Result := Length(LText);
end ;
end ;
// -----------------------------------------------------------------------------
exports
GetDllText;
end .
上の DLL の関数を使用する例です。実行結果は上の図の通りです。
copy code
implementation
{$R *.dfm}
function GetDllText(ABuff: PChar; ALen: Integer): DWORD;
stdcall ; external 'DllTest_PChar.dll' ;
// =============================================================================
// DLL内の関数から文字列を取得
// PChar 型の変数で取得する例
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LBuffer : PChar;
LTextLen : Integer;
LText : string ;
LLen : Integer;
begin
// Null 終端文字も含めて 200 文字分のメモリを確保
LTextLen := 200 ;
LBuffer := AllocMem((LTextLen + 1 ) * SizeOf(Char));
try
// LBuffer (PChar 型) に直接文字列を代入しないこと
StrPCopy(LBuffer, '日本語' );
// 必要な文字数を取得
LLen := GetDllText(LBuffer, -1 );
if LLen > (LTextLen + 1 ) then begin
// バッファが不足している場合は再確保
ReallocMem(LBuffer, LLen * SizeOf(Char));
end ;
LLen := GetDllText(LBuffer, LLen);
// 取得結果の LBuffer (PChar 型) に直接文字列を代入しないこと
LText := string (LBuffer);
LText := LText + sLineBreak + '文字数 : ' + IntToStr(LLen);
MessageBox(Handle, PChar(LText), '結果' , MB_ICONINFORMATION);
finally
// 確保したメモリを解放
FreeMem(LBuffer);
end ;
// LBuffer (PChar 型) のメモリを解放したので LBuffer に直接文字列を代入してもよい
end ;
// =============================================================================
// DLL内の関数から文字列を取得
// PChar 型の変数で取得する例
// 十分なメモリを確保している場合は必要な文字数のチェックは不要
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LBuffer : PChar;
LTextLen : Integer;
LText : string ;
LLen : Integer;
begin
// Null 終端文字も含めて 200 文字分のメモリを確保
LTextLen := 200 ;
LBuffer := AllocMem((LTextLen + 1 ) * SizeOf(Char));
try
// LBuffer (PChar 型) に直接文字列を代入しないこと
StrPCopy(LBuffer, '日本語' );
LLen := GetDllText(LBuffer, LTextLen);
// 取得結果の LBuffer (PChar 型) に直接文字列を代入しないこと
LText := string (LBuffer);
LText := LText + sLineBreak + '文字数 : ' + IntToStr(LLen);
MessageBox(Handle, PChar(LText), '結果' , MB_ICONINFORMATION);
finally
// 確保したメモリを解放
FreeMem(LBuffer);
end ;
// LBuffer (PChar 型) のメモリを解放したので LBuffer に直接文字列を代入してもよい
end ;
[備考]
DLL 内の変数の値は、その DLL を使用するアプリ固有の値となります。この仕組みによって、同じ DLL 内の関数や変数を複数のプログラムから使用できるようになっています。あるプログラムで DLL 内の変数に値を設定しても、その値を別のプログラムから取得することはできません。
DLL 内で DLL 自身のフルパスを取得する例です。
GetModuleFileName 関数を使用して取得します。GetModuleName 関数の引数を 0 にすると、この DLL の関数を呼び出した EXE のフルパスの取得になります。ParamStr(0) の値もこの DLL 内の関数を呼び出した EXE のフルパスの取得となります。これらの値も取得するようにしています。
図7
実行結果
最初の値が DLL 自身のフルパス
後の 2 つは DLL を呼び出した EXE のフルパス
全て DLL 内で取得した値
リスト5
DLL 内で DLL 自身のフルパスを取得する関数の例
copy code
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 の関数を使用する例です。実行結果は上の図の通りです。
copy code
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 つだけ (ダイアログを表示する関数)
copy code
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
実行結果
リスト8
DLL を使用するプログラムのコード
リソースから取り出した DLL をディスクに保存してから利用する
copy code
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
実行結果
リスト9
リソースの DLL をディスクに保存しないで利用する方法
DelphiGear.cn からダウンロードした MemoryModule というユニットを使用する例
copy code
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 というユニットを使用する例
copy code
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 .