プラグイン・プログラミング(3)
2006年9月16日………今迄に無いハイペースである、つ〜か今迄が(略
まあ良い、とりあえず今回は逆にDLL側からソフトウェア側の関数を呼び出す方法を紹介する。
★おさらい
そもそも、DLLはソフトウェアに追加機能を提供する為のものである。よって、ソフトウェア側から呼ばれてもソフトウェア側を呼ぶ事には対応していない。
しかしソフトウェア側にサポートルーチンを用意しておいてDLL側から呼ぶというのは、ディスク容量などの観点から鑑みても合理的な手法だと思われる。
要するに、可能かどうかという事なのだが………実は可能なのである、人類の英知ってスンバラシィ〜(゜Д゜) 以下、その方法を順を追って紹介してみる。
★EXEにエクスポートテーブルを
まあ、ぶっちゃけてしまうと上の通りなのだが(´Д`) 要はDLLから呼ばれる様にする為に、EXEにもエクスポートテーブルを持たせる必要がある。
とはいえ、モジュール定義ファイルを使ったやり方だと何故かエラー(原因不明)になってしまうので、此処では__declspec(dllexport)を用いたやり方を説明する。
(※どうやらモジュール定義ファイルを使ったやり方も可能っぽい、というか何故にエラーが起きたのだろう?(;´Д`) 詳細は、プラグイン・プログラミング(6)で触れる予定である)
★では、実際にやってみようっ☆
………すんません、調子こきました(;´Д`) まあ、実際のソースは以下に↓
#include <windows.h> #include <stdio.h> extern "C" __declspec(dllexport) int adder(int x, int y){ return(x + y); } int main(){ int (*adder_func)(int, int) = NULL; adder_func = (int (*)(int, int))GetProcAddress(GetModuleHandle(NULL), "adder"); if(adder_func){ printf("%i + %i = %i\n", 10, 5, adder_func(10, 5)); } return(getchar()); }
今回は、エクスポートテーブルの存在を確認するだけのプログラムである。よって、DLLとEXEを分割しては作らない。
そのおかげ(せい)で、LoadLibraryが見当たらない。注目すべきところは、GetModuleHandle(NULL)だけである。
・GetModuleHandle(NULL)
モジュール名からモジュールのハンドルを取得する、引数がNULLの場合は「呼び出し側プロセスの作成に使われたファイルのハンドル」を取得する。
………若干理解しにくいが、要は「呼び出したソースコードが含まれているファイルのハンドル」を取得してくれると思ってほしい(それでもややこしいな)
今回の場合、アプリケーション本体のハンドルを取得する。つまり、アプリケーション本体のハンドルを取得→エクスポートテーブルから関数のメモリアドレスを取得としているワケだ。
・"adder"
前回も書いたが、Microsoft製のコンパイラで__declspec(dllexport)を用いた場合は装飾名=関数名で極めて容易に呼び出しが行える。
他言語から呼び出す事を前提に作るならcdeclは使えないが、CやC++から呼び出す事のみを前提に作るならstdcallは使わない方が良いだろう。
★では、実際にやってみようっ☆(2)
さて、上で準備は万全である。では、DLLとEXEを分割した実際のソースを以下に↓
DLL側(定義ファイル) |
LIBRARY HOGE EXPORTS PDll @1 CallMain @2 |
DLL側(ソースコード)
#include <stdio.h> #include <windows.h> typedef void (*PFunc)(); extern "C" void PDll(){ printf("PDll;\n"); } extern "C" void CallMain(){ PFunc PPMain = NULL; PPMain = (PFunc)GetProcAddress(GetModuleHandle(NULL), "PMain"); if(PPMain){ PPMain(); } else{ printf("PMainの取得失敗;\n"); } }
EXE側(ソースコード)
#include <stdio.h> #include <windows.h> typedef void (*PFunc)(); extern "C" __declspec(dllexport) void PMain(){ printf("PMain;\n"); } int main(){ HINSTANCE hLib; PFunc PPDll = NULL; PFunc PCallMain = NULL; hLib = LoadLibrary("HOGE.dll"); if(hLib){ PPDll = (PFunc)GetProcAddress(hLib, "PDll"); PCallMain = (PFunc)GetProcAddress(hLib, "CallMain"); } else{ printf("HOGE.dllの取得失敗;\n"); } if(PPDll){ PPDll(); } else{ printf("PPDllの取得失敗;\n"); } if(PCallMain){ PCallMain(); } else{ printf("PCallMainの取得失敗;\n"); } FreeLibrary(hLib); return(getchar()); }
今迄に書いていた事をしっかりと理解出来てさえいれば、今回のソースコードは何の説明も無く読める筈だ。
一応少しだけ補足しておくと、DLLは実行時にEXEとリンクする。正確には、EXEのプロセス内に自己をマッピングする。
よって、GetModuleHandle(NULL)を行っても取得するのは「マッピングしたプロセス」………即ち、EXE側のハンドルとなる。
(※"Hoge.dll"は適切なファイル名に修正すること、ここを修正しないと絶対にLoadLibraryが失敗する(;´Д`))
………以上、これで(3)は終了である。次回、クラスをDLLから呼び出すやり方を説明する(本来、DLLはクラスに対応していない)
とはいえ、俺の勉強自体がその辺りはまだ完了していない………よって、次回更新するのは何時になるのやら(;´Д`) でもまあ更新はする、多分………。