32bit DLLを64bitアプリからcallすることはできません。今、何かの32bit DLLを持っているとして、これの64bit版が必ず入手できるとは限らない。そして、世の中は64bit化していって64bit DLLしか存在しないって状況もあり得る。ということは、64bitアプリから64bitDLLだけでなく32bitDLLを読み出さないといけないっていう需要は必ずある。
ちなみにMicrosoftはこう言っている。「アウトプロセス COM サーバーでラップ」しろ、、、と。なんとも難しいことを言う。
で、目指すところは、こうでした。
64bit dllは32bit appをCreateProcessせんといかんのだけど、それをいつするのか。64bit dllにDllMainエントリポイントを作って、Load時に自動で32bit appを起動するようにしたい。で、そのためにDllMainでどうやるのかを事前検討します。
ちなみに、DllMainはdllには必ずあるらしいのですが、コードとして省略すると勝手に空っぽで追加されてコンパイルされるらしく、今まで省略していたんです。
で、DllMainが実際どう動くのかを32bit DLLを64bitアプリから使う(1)で作ったdllとテストアプリをベースにしたもので確認します。
まず、dllです。基本的には、ここに書いてあるDllMainを貼り付けて、printfで通ったかどうかをコンソールに示すことにします。
では、
MyDLL32.cpp
#include <windows.h> #include <stdio.h> extern "C" int __stdcall tasu(int arg1,int arg2); extern "C" int __stdcall hiku(int arg1,int arg2); //extern "C" float __stdcall pi(void); BOOL WINAPI DllMain( HINSTANCE hinstDLL, // handle to DLL module DWORD fdwReason, // reason for calling function LPVOID lpvReserved ) // reserved { // Perform actions based on the reason for calling. switch( fdwReason ) { case DLL_PROCESS_ATTACH: // Initialize once for each new process. // Return FALSE to fail DLL load. printf("DLL_PROCESS_ATTACH\n"); break; case DLL_THREAD_ATTACH: // Do thread-specific initialization. printf("DLL_THREAD_ATTACH\n"); break; case DLL_THREAD_DETACH: // Do thread-specific cleanup. printf("DLL_THREAD_DETACH\n"); break; case DLL_PROCESS_DETACH: printf("DLL_PROCESS_DETACH_1\n"); if (lpvReserved != nullptr) { break; // do not do cleanup if process termination scenario } // Perform any necessary cleanup. printf("DLL_PROCESS_DETACH_2\n"); break; } return TRUE; // Successful DLL_PROCESS_ATTACH. } extern "C" int __stdcall tasu(int arg1,int arg2){ return arg1+arg2; } extern "C" int __stdcall hiku(int arg1,int arg2){ return arg1-arg2; } //extern "C" float __stdcall pi(void){ // return 3.14159265358979; //}
__stdcallとWINAPIを混在させるんじゃないって怒られそう、、、
で、テストアプリ側、LoadLibraryとFreeLibraryの前にprintfを追加しただけ。
testMyDLL32.c
#include <windows.h> #include <stdio.h> #define MyDLL32 "MyDLL32.dll" typedef int (__stdcall *tasu_type)(int arg1,int arg2); typedef int (__stdcall *hiku_type)(int arg1,int arg2); //typedef int (__stdcall *pi_type)(void); HMODULE dll; tasu_type ptasu; hiku_type phiku; //pi_type ppi; int main(int argc,char** argv){ int arg1=10; int arg2=3; int wa,sa; printf("call LoadLibrary\n"); dll=LoadLibrary(MyDLL32); if(dll==NULL){ printf("Failed to load dll.\n"); return -1; } ptasu=(tasu_type)GetProcAddress(dll, "tasu"); phiku=(hiku_type)GetProcAddress(dll, "hiku"); //ppi=(pi_type)GetProcAddress(dll, "pi"); wa=ptasu(arg1,arg2); printf("%d + %d = %d\n",arg1,arg2,wa); sa=phiku(arg1,arg2); printf("%d - %d = %d\n",arg1,arg2,sa); //printf("pi = %f\n",ppi()); printf("call FreeLibrary\n"); FreeLibrary(dll); return 0; }
で、別に個別にコンパイルしてもいいんだけど、dll作るのって記述が多いので、Makefileも作ります。Makefileって単純なビルドシーケンスでも趣向を凝らしたものを公開している人が多いけど、わかりやすさ重視でちょーださい書き方をしています。まぁしかし、前回と全く同じなんだけど。
Makefile
CC="C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x86\cl.exe" LINK="C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x86\link.exe" test32 : MyDLL32.dll testMyDLL32.exe .\testMyDLL32.exe MyDLL32.dll : MyDLL32.obj $(LINK) /DLL /out:MyDLL32.dll /DEF:MyDLL32.def MyDLL32.obj MyDLL32.obj : MyDLL32.cpp $(CC) /c MyDLL32.cpp testMyDLL32.exe : testMyDLL32.C $(CC) testMyDLL32.c clean: del MyDLL32.dll testMyDLL32.exe MyDLL32.obj testMyDLL32.obj
で、
nmake test32
ってすると、
.\testMyDLL32.exe
call LoadLibrary
DLL_PROCESS_ATTACH
10 + 3 = 13
10 - 3 = 7
call FreeLibrary
DLL_PROCESS_DETACH_1
DLL_PROCESS_DETACH_2
ってなる。
LoadLibraryでLoadされたときにfdwReasonにDLL_PROCESS_ATTACHがセットされて、FreeLibraryで解放されるときにDLL_PROCESS_DETACHがセットされて、switch文でそれぞれの状況に応じた処理を入れることができるってことがわかったぜい。
コメントをお書きください