Version 15.13
DLL の実行中リンク
「今回は、前回触れた」
・ DLL 内の関数を動的に見つけて呼び出す。
「について説明します」
『この〈動的〉ってのがよく分からないんだけど』
「そうだね、はっきり言ってこれまでの関数の呼び出し方と考え方が違うか
ら、そこをまず押さえておこうか」
『考え方が違う!?』
「 Version 15.11 ( No.311 ) で説明した、 DLL の中の関数を使う方法」
<プロジェクトFrom>
[From.cpp]
呼び出す側
↓
↓コンパイル
↓
[From.obj]
↓
↓ リンク (A)
→→→→→→→→→→→→→→←←←←←←←←←←←←[DLL.lib]
↓
[From.exe]
<実行するとき>
[From.exe]
↓
↓ 実行時リンク (B)
→→→→→→→→→→→→→→←←←←←←←←←←←←[DLL.dll]
↓
実行
「これとは全く違うんです。図で示すとこうなります」
<プロジェクトFrom>
[From.cpp]
呼び出す側
↓
↓コンパイル
↓
[From.obj]
↓
↓リンク
↓
[From.exe]
<実行するとき>
[From.exe]
↓
↓実行中
↓
↓ DLL を直接ファイル名で指定してリンク←←←←←←←←[DLL.dll]
↓
↓ DLL 内の関数を名前で指定して呼び出し。
↓
終了
『え…… DLL を使うのに、ライブラリファイルのリンクとか、実行すると
きのリンクが必要ないの!?』
「そういうこと。これまで紹介してきた方法は、 DLL を〈実行開始時〉に
リンクしていました。でも今回紹介する方法は、好きな DLL を〈実行中〉
にリンクして、好きな関数を呼び出すことができるんです」
『ええーっ!? だって、だって関数を呼び出したら、その関数が入ってる
ライブラリファイルを指定しなきゃ、リンクエラーになるじゃない!』
「そう、だから、関数を普通に呼び出すんじゃないんです」
『普通に呼び出さない……?』
「ま、実際に見てもらった方が早いでしょ。今回は、前回説明した」
// Main.cpp
#include <Windows.h>
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
MessageBox( NULL, "テスト", "テストダイアログ", MB_OK );
return 0;
}
「を方法を変えて実現します」
『 MessageBox() を呼び出すってこと?』
「そういうこと。実際にプログラムを見てもらおうか」
// Main.cpp
#include <Windows.h>
// MessageBox()関数ポインタの typedef 。
typedef int (WINAPI *type_pfnMessageBox)
( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType );
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
// user32.dll に実行中リンクします。
HINSTANCE hInstanceDll = LoadLibrary( "user32.dll" );
if( hInstanceDll == NULL )
{
OutputDebugString( "DLLをロードできませんでした。" );
return 0;
}
// user32.dll の MessageBoxA() 関数の関数ポインタを取得します。
type_pfnMessageBox pfnMessageBox
= (type_pfnMessageBox)GetProcAddress
( hInstanceDll, "MessageBoxA" );
if( pfnMessageBox == NULL )
{
OutputDebugString( "関数を取得できませんでした。" );
}
else
{
// ここで、 MessageBox() 関数を呼び出します。
pfnMessageBox( NULL, "テスト", "テストダイアログ", MB_OK );
}
FreeLibrary( hInstanceDll );
return 0;
}
『ビルドして実行……! 本当にダイアログ出た! なんで!?
MessageBox() 呼んでないのに!』
「それを説明していきます。まずは DLL の実行中リンク。 MessageBox()
関数は、 user32.dll に入ってます」
『 MSDN に user32.lib がライブラリファイル、って書かれてたから?』
「そう。 MSDN を見てどの API がどの DLL に入っているか調べます。
DLL が分かったら、その DLL を実行中にリンクします」
// user32.dll に実行中リンクします。
HINSTANCE hInstanceDll = LoadLibrary( "user32.dll" );
『 LoadLibrary() って API ?』
「そう、この API は DLL を実行中にリンクします。普通は実行開始時に自
動的にリンクされる DLL を、ここでファイル名を直接指定して、好きな
DLL をリンクすることができるんです」
『ファイル名って、"user32.dll" のこと?』
「そう。ここではファイル名だけだから、 System32 フォルダ内の DLL が
探されるけど、フルパスで指定することもできるから」
『そっか、 Windows の DLL じゃなくてもいいってことね』
「 DLL が見つかると、その DLL のインスタンスハンドルが返されます」
『インスタンスハンドルって Version 8.02 ( No.144 ) でやったね。
あれ? でも DLL でもインスタンスなの?』
「その回を読み返してみて。インスタンスハンドルって、アドレスのこと
だったでしょ。実行中リンクした DLL も、メモリ上に置かれるから」
『その置かれたアドレス!』
「そういうこと。この DLL のインスタンスハンドルを使って、 DLL 内の
関数を探します。それがこれ」
// user32.dll の MessageBoxA() 関数の関数ポインタを取得します。
type_pfnMessageBox pfnMessageBox
= (type_pfnMessageBox)GetProcAddress
( hInstanceDll, "MessageBoxA" );
『な、なんか難しいです!』
「ひとつひとつ見ていこうか。まず GetProcAddress() 」
『 API ?』
「そう。この API で、第1引数で指定した DLL の中から、第2引数で指定
した関数を探します」
『第1引数はさっきのインスタンスハンドルね。第2引数は MessageBoxA
って、なんかちょっと違うような……』
「 A については Version 11.06 ( No.206 ) を読み返して」
『あ! そっか、 API って A がついてるのと W がついてるのがあって、
マクロで切り替わってたんだ』
「普段は MessageBox() を使ってるけど、これはマクロで実際には
MessageBoxA() か MessageBoxW() に切り替わってるわけ。 DLL に入ってい
る実際の関数は」
『 MessageBoxA() と MessageBoxW() だから、この名前で指定しなきゃいけ
ないんだ……って、それってどうやって調べればいいの!?』
「ヘッダーファイル見るしかないね。ま、基本的に API はこうやって呼ぶ
ものじゃないから……」
『確かに、動的に呼び出すってなんか特殊な感じ……と、変数に戻り値入れ
てるとこ説明して』
「そうそう。 GetProcAddress() は、関数の関数ポインタを返します。関数
ポインタについては Version 13.17 ( No.253 ) とかを読み返してみて」
『関数のアドレスを入れるポインタなんだよね、この比較関数とか、あと
ウィンドウプロシージャに使うの。そか、その関数ポインタを受け取れば、
関数が呼び出せる!』
「そういうこと。関数ポインタは、まずこのように typedef で型として
定義しておきます」
// MessageBox()関数ポインタの typedef 。
typedef int (WINAPI *type_pfnMessageBox)
( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType );
「これはこうなっています」
typedef 戻り値 (呼出方 *型名)(引数);
『変数の時とほとんど同じね』
「この変数として pfnMessageBox っていう変数を作って、
GetProcAddress() の戻り値をキャストしたものを格納します」
『これで MessageBoxA() の関数ポインタが入ったの?』
「入ってなければ NULL が格納されるから。ちゃんと入ってれば」
// ここで、 MessageBox() 関数を呼び出します。
pfnMessageBox( NULL, "テスト", "テストダイアログ", MB_OK );
『で、 MessageBox() と同じように呼び出せる!!』
「呼び出したら、終了処理をします」
FreeLibrary( hInstanceDll );
『リンクした DLL を解放するんだ』
「こうすることで、好きな DLL の好きな関数を呼び出せるわけです」
/*
Preview Next Story!
*/
『なんだかこれまでの概念が……』
「でもこういうのができるようになるととたんに面白くなるよ」
『そなの?』
「好きなように機能が切り替えられるようになるでしょ」
『あー、確かにパーツ切り替えみたいな?』
「ま、その切り替えがうまくできないこともあるんだけど」
『?』
「というわけで次回」
< Version 15.14 自作 DLL を実行中リンクしてみる >
『につづく!』
「ここからまた一筋縄じゃいかないよー」
『また怖がらせてー、殴るよ?』