プラグイン・プログラミング(9)
2007年2月1日………プラグイン・プログラミング(8)の執筆後、引き続きこれを書いている。
これにてプラグイン・プログラミングは今度こそ終了である、予め断っておくと今回は今までのまとめに過ぎない。
つまり、今まで読み進めてきた者には不要である。冗長な文章を読むのが面倒な者や、注意点のまとめを見たい者の為である。
★プラグイン作成の基本
・関数のエクスポートは、「extern "C" __declspec(dllexport)」かモジュール定義ファイルで
・呼び出し規約は「cdecl」あるいは「stdcall」で、メンバ関数も同じだがBCCとVCなら「thiscall」もOK
・該当のDLLを「LoadLibrary」し、「GetProcAddress」で関数ポインタを取得し、「FreeLibrary」で解放する
・関数ポインタはtypedefで定義しておく、その方がソースコードの可読性も上がるしGetProcAddressで使いやすい
・DLL側からEXE側のエクスポートされた関数を取得するには、「GetProcAddress(GetModuleHandle(NULL), "「関数名」")」
★クラス&構造体(共通)
・仮想デストラクタを持たせてはならない
・パディングを統一しておく、具体的にはソースコードの最初に読み込まれる部分で「#pragma pack(1)」あるいは「#pragma pack(4)」
★クラス&構造体(継承して使う場合)
・BCCとVC以外でのプラグイン作成を許可する場合、全てのメンバ関数の呼び出し規約をcdeclに変えておく
・エクスポートするクラス(あるいは構造体)は単一継承で全てのメンバ関数を仮想にし、クラスに対応したFactory関数を用意する
・クラスを解放する為にDeleteInstance関数を用意する、あるいはSuicide関数のような自殺する為のメンバ関数をクラスに持たせておく
★クラス&構造体(そのまま使う場合)
・DLLで確保しEXEで解放する場合やその逆、メンバ関数内でnew/deleteを行う場合はnew/deleteをオーバーロードしなければならない
・方法は二つ存在する:EXE側のnew/deleteをDLL側のnew/deleteが呼び出すか、あるいはnew/deleteでのメモリの確保/解放をWINAPIに委譲する
★BCCとVCの相違点
・VCは「__declspec(dllexport)」で関数を修飾すると装飾名の先頭の「_」を勝手に削除してしまう、解決策は三つ
1.「__declspec(dllexport)」で関数を修飾せず、モジュール定義ファイルを使用する
2.BCCでモジュール定義ファイルを使用する、内容は装飾名の先頭の「_」を削除するもの
3.VCでモジュール定義ファイルを使用する、内容は削除された先頭の「_」を再び追加するもの
・BCCで実行ファイルを作成する場合はモジュール定義ファイルが使用できない、よって3の方法しか選択肢がない
★その他
・プラグインによる動的な機能追加を実装する場合、switch構文ではなく「map<ユニークID、関数ポインタ>」で管理する
・EXE側は「map<ユニークID、関数ポインタ>に登録する関数」を用意しておき、DLL側はDllMainでmapに登録して機能追加を行う
・DLLがLoadLibrary時に行う処理は、DllMainの引数である「fdwReason」が「DLL_PROCESS_ATTACH」であるかをチェックしてから行う
・同じくDLLがFreeLibrary時に行う処理は、DllMainの引数である「fdwReason」が「DLL_PROCESS_DETACH」であるかをチェックしてから行う
・DLLのファイル名が分からないのでEXE側が検索しLoadLibraryする必要がある、DLLを検索しLoadLibraryするソースコードは以下に記す↓
#include <windows.h> #include <vector> using namespace std; vector<HINSTANCE> hLibs; void LoadPlugin(){ HANDLE hFind; WIN32_FIND_DATA fd; hFind = FindFirstFile("*.dll", &fd); if(hFind!=INVALID_HANDLE_VALUE){ do{ hLibs.push_back(LoadLibrary(fd.cFileName)); if(hLibs.back()==NULL){ hLibs.pop_back(); } }while(FindNextFile(hFind, &fd)); FindClose(hFind); } }
・上記のソースコードでLoadLibraryした全てのDLLにFreeLibraryを行うには、「hLibs」をイテレーティングすればよい(書く必要はないとも思うが一応)
以上、これで(9)は終了である。