· 

32bit DLLを64bitアプリから使う(5)

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文でそれぞれの状況に応じた処理を入れることができるってことがわかったぜい。