#pragma twice

KAB-studio > プログラミング > #pragma twice > 313 Version 15.13 DLL の実行中リンク

#pragma twice 313 Version 15.13 DLL の実行中リンク

前のページへ 表紙・目次へ 次のページへ

 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 を実行中リンクしてみる >
につづく!
ここからまた一筋縄じゃいかないよー
また怖がらせてー、殴るよ?
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。