プラグイン・プログラミング(6)
2007年1月20日………あけおめことよろヽ(・∀・)ノ●ウンコー
さて、前回でプラグイン・プログラミングは終わりと書いていたのだが………実はもうちょっとだけ続くんじゃ、である(´Д`)
今回は、前回までの記述に対するフォローと異種コンパイラ間で通用するプラグイン作成においての注意事項を記そうと思う。
★舐めてんのかMicrosoft(#゜Д゜)
というワケで、第一の注意事項である。
以前に、__declspec(dllexport)とcdeclで修飾すると装飾名=関数名となると書いたが、アレは間違いである。
実は、Microsoft製のコンパイラにて__declspec(dllexport)で修飾すると、先頭の「_」(アンダースコア)が削除されてしまうらしい(ソース)
つまり、以前のように表にすると以下のような感じとなるワケだ↓
関数名 | void h(int) | void h(int, char) | void h(void) |
装飾名(cdecl) | h | h | h |
装飾名(stdcall) | h@4 | h@5 | h@0 |
装飾名(fastcall) | h@4 | @h@5 | @h@0 |
つまり__declspec(dllexport)で修飾を行った場合、BCCとVCでは装飾名が異なる。
より具体的な例を挙げると、extern "C" __declspec(dllexport) void Hoge();という関数(cdecl前提ね)をDLLからインポートしたい場合、
・BCCで作成されたDLLなら、GetProcAddress(HMODULE, "_Hoge");
・VCで作成されたDLLなら、GetProcAddress(HMODULE, "Hoge");
としなければならないという事である。うわ〜、なんてやらしいんだ………つ〜か、Microsoftは逝ってよし(#゜Д゜)
対策はひとつ、即ち__declspec(dllexport)ではなくモジュール定義ファイルでエクスポートテーブルを作成するしかない。
しかし、実行ファイルでは何故かモジュール定義ファイルが作成出来ない(エラーが発生する)………と思いきや、それも間違いだったようである(;´Д`)
★実行ファイルでもモジュール定義ファイルは作成出来る(でもVCのみ)
そう、出来るのだ(;´Д`) 何をミスったかは知らないが、出来るっぽい………騙された奴等にゃ、嘘吐いてめんご(;´Д`)人
………つ〜か、何故かBCCでは実行ファイルでモジュール定義ファイルが作成出来ませんがっ!? いい加減にしないと、おいちゃん怒りますわよっ!?ヽ(#゜Д゜)ノ
………つ〜わけで、異種コンパイラ間で通用するプラグイン作成は実質無理(;´Д`)
勿論、無理を通せば道理は引っ込むがそこまで無理をする必要も無いワケで………VisualStudioは、2005のExpressEditionが無償で提供されてるしなぁ('A`)
………と言いつつも一応は補完、俺って優しいねヽ(´―`)ノ
上記の通り、Microsoft製のコンパイラでは__declspec(dllexport)で修飾した場合に先頭の「_」(アンダースコア)が削除されてしまう。
また、BCCでは実行ファイルでモジュール定義ファイルが作成出来ない。よって、BCCは先頭に「_」が存在する事となる。つまりは、
VC専用に作成したモジュール定義ファイルで、「_」無しの装飾名を「_」有りに置換してしまえばいいワケだ。VCが勝手に先頭の「_」
を削除しない、あるいはBCCが実行ファイルでのモジュール定義ファイルの作成を許可してくれるのが一番なのだが………。
つまりVC側では、
DLL側(定義ファイル) |
EXPORTS _SetTest = SetTest |
DLL側(ソースコード)
#include <string.h> #include <windows.h> struct Test{ int nMem; char strMem[20]; }; typedef void (*PFunc)(Test*); extern "C" __declspec(dllexport) void SetTest(Test* Obj){ Obj->nMem = 20; strcpy(Obj->strMem, "Dll"); PFunc fpPrintTest = (PFunc)GetProcAddress(GetModuleHandle(NULL), "_PrintTest"); fpPrintTest(Obj); }
EXE側(定義ファイル) |
EXPORTS _PrintTest = PrintTest |
EXE側(ソースコード)
#include <stdio.h> #include <string.h> #include <windows.h> struct Test{ int nMem; char strMem[20]; }; typedef void (*PFunc)(Test*); extern "C" __declspec(dllexport) void PrintTest(Test* Obj){ printf("Test::nMem = %d\n", Obj->nMem); printf("Test::strMem = %s\n", Obj->strMem); } int main(){ Test Obj; HMODULE hLib; PFunc fpSetTest = NULL; Obj.nMem = 10; strcpy(Obj.strMem, "Main"); printf("Test::nMem = %d\n", Obj.nMem); printf("Test::strMem = %s\n", Obj.strMem); hLib = LoadLibrary("Hoge.dll"); if(hLib){ fpSetTest = (PFunc)GetProcAddress(hLib, "_SetTest"); } else{ printf("Hoge.dllの取得に失敗!\n"); } if(fpSetTest){ fpSetTest(&Obj); } else{ printf("_SetTestの取得に失敗!\n"); } FreeLibrary(hLib); return(getchar()); }
とすることで、SetTest(関数名)→_SetTest(装飾名)→SetTest(__declspec(dllexport)による「_」の削除)→_SetTest(モジュール定義ファイルによる装飾名の変更)となるワケだ。
同様に、EXE側のPrintTestも変化する。二度手間といえば二度手間なのだが、独自拡張のせいなのだから仕方が無い(;´Д`)
一方、BCCでは上記のソースコードをモジュール定義ファイル無しで記述する。そうすれば、SetTest(関数名)→_SetTest(装飾名)となりVCの場合と装飾名が同一になるワケだ。
こうすることで、DLL側のSetTestもEXE側のPrintTestも一律で「_」を先頭に付加すればGetProcAddress可能となる。
VC使いに負担を掛ける工夫だが、まあBCCとVCで両立するソースコードを記述する場合なら仕方が無いだろう(;´Д`)
ちなみに、warning
LNK4197という警告が表示されるが気にする必要はない。多重定義っつ〜ても、勝手に先頭の「_」を削除したのはお前だろとツッコミ返しとく(#゜Д゜)
………以上、これで(6)は終了である。実はまだまだ続くのである、次回はクラスと構造体に関してのより詳しいことを書こうと思う。
しかし、勉強していると過去の誤りが色々と明らかになるものだ………今回こっそりと誤りの修正も行ったので、今まで読み進めてきた者は是非とも読み返して頂きたい。