簡単にReg-Free COMを使うためのDLL
2015年4月22日
マニフェストを用いずにReg-Free COMを使うためのコードだけを持ったDLLを作成した。先日、LabVIEWでJavaScriptを使う為の記事を書いた。これはもともとLabVIEWで機器用のAPI(DLLに記述されている)を大量に呼び出すためにやろうとしていることなので、JavaScript中でDLLが呼び出す必要があり、拙作のSFC miniを用いようとしている(仕事でこれを使うのは、始めて)。
通常、SFC miniを使う際には、これをレジストリーに登録して用いなければならない(管理者権限が必要)。ところが、職場のコンピューター(顕微鏡に繋がっている)の管理者権限を持っていないので、何かの変更のたびに管理者を呼んで作業してもらう必要があり、これは面倒である。なので、Registration-Free-COMとして、SFC miniを用いたい。
この用途のために、Script Users' and Programmer's Private Object Navigation(Suppon)というツールを作成したのだが、残念ながらこれは、suppon.exeというこのツールのexeのスレッド内でReg-Free COMを使うための物であり、LabVIEWから呼び出せない。
そこで、Supponの該当部分のコードだけを含むDLLを作成することにした。
コードは、以下の通り(ライセンスは、LGPL 2.1)。
regfreecom.cpp:
createobject.cpp:
実装しているのは、CreateObjectAとCreateObjectWの2つの関数。それぞれ、ANSI文字列とUNICODE文字列での呼び出しに対応している。引数は、DLLファイルのファイル名とCLSIDで、CLSIDは"{1303599D-3028-4eac-97BB-D9370B88B2DA}"のような文字列で与える(この文字列が不明の場合は、COMをインストールした後にレジストリーを調べること)。戻り値は、オブジェクトのポインター。エラーの場合は、0が返る。
VS2008のファイル一式は、こちら(ライセンスは、LGPL v 2.1)。
通常、SFC miniを使う際には、これをレジストリーに登録して用いなければならない(管理者権限が必要)。ところが、職場のコンピューター(顕微鏡に繋がっている)の管理者権限を持っていないので、何かの変更のたびに管理者を呼んで作業してもらう必要があり、これは面倒である。なので、Registration-Free-COMとして、SFC miniを用いたい。
この用途のために、Script Users' and Programmer's Private Object Navigation(Suppon)というツールを作成したのだが、残念ながらこれは、suppon.exeというこのツールのexeのスレッド内でReg-Free COMを使うための物であり、LabVIEWから呼び出せない。
そこで、Supponの該当部分のコードだけを含むDLLを作成することにした。
コードは、以下の通り(ライセンスは、LGPL 2.1)。
regfreecom.cpp:
#include "./regfreecom.h" #pragma comment(linker, "/entry:\"DllMain\"") #pragma comment(linker, "/export:CreateObjectA=_CreateObjectA@8,PRIVATE") #pragma comment(linker, "/export:CreateObjectW=_CreateObjectW@8,PRIVATE") BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } STDAPI CreateObjectA(LPCSTR file, LPCSTR clsidstr){ return (HRESULT) CreateObject(file,clsidstr); } STDAPI CreateObjectW(LPWSTR file, LPWSTR clsidstr){ return (HRESULT) CreateObject(file,clsidstr); }
createobject.cpp:
#include "./regfreecom.h" IDispatch* CreateObject(LPCSTR file, LPCSTR clsidstr){ GUID clsid; int cchWideChar; LPWSTR lpWideCharStr; IDispatch* result; // clsidstr: Calculate UNICODE string length cchWideChar=MultiByteToWideChar(CP_ACP,MB_ERR_INVALID_CHARS,clsidstr,-1,0,0); if (!cchWideChar) return NULL; // clsidstr: Prepare memory for UNICODE lpWideCharStr=new wchar_t[cchWideChar+2]; // clsidstr: Receive UNICODE if (!MultiByteToWideChar(CP_ACP,MB_ERR_INVALID_CHARS,clsidstr,-1,lpWideCharStr,cchWideChar)) { // Failed to construct UNICODE. return NULL; } // clsidstr: Receive CLSID if (NOERROR!=CLSIDFromString(lpWideCharStr,&clsid)) return NULL; delete lpWideCharStr; // file: Calculate UNICODE string length cchWideChar=MultiByteToWideChar(CP_ACP,MB_ERR_INVALID_CHARS,file,-1,0,0); if (!cchWideChar) return NULL; // file: Prepare memory for UNICODE lpWideCharStr=new wchar_t[cchWideChar+2]; // file: Receive UNICODE if (!MultiByteToWideChar(CP_ACP,MB_ERR_INVALID_CHARS,file,-1,lpWideCharStr,cchWideChar)) { // Failed to construct UNICODE. return NULL; } // All done let's create the object result=CreateObject(lpWideCharStr,&clsid); delete lpWideCharStr; return result; } IDispatch* CreateObject(LPWSTR file, LPWSTR clsidstr){ GUID clsid; if (NOERROR!=CLSIDFromString(clsidstr,&clsid)) return NULL; return CreateObject(file,&clsid); } IDispatch* CreateObject(LPWSTR file, const GUID* clsid){ // General variables int i; // Load DLL & check DllGetClassObject HINSTANCE hDLL; void* Address; if (!(hDLL=LoadLibraryW(file))) return 0; if (!(Address=GetProcAddress(hDLL,"DllGetClassObject"))) return 0; // DllGetClassObject(&clsid,IID_IClassFactory,(LPVOID*)&ppv); IClassFactory* ppv=0; void *Stack1,*Stack2,*Stack3; Stack3=&ppv; Stack2=(void*)&IID_IClassFactory; Stack1=(void*)clsid; _asm push Stack3; _asm push Stack2; _asm push Stack1; _asm call Address; _asm mov i,eax; if (i!=S_OK || !ppv) return 0; // Class factory was succesfully obteined. // Lets get the object. IDispatch* ppvObject=0; if (S_OK!=ppv->QueryInterface(IID_IClassFactory, (void**) &ppv)) return 0; ppv->Release(); if (S_OK!=ppv->CreateInstance(NULL,IID_IUnknown,(void**)&ppvObject)) return 0; ppv->Release(); if (!ppvObject) return 0; if (S_OK!=ppvObject->QueryInterface(IID_IDispatch,(void**)&ppvObject)) return 0; ppvObject->Release(); return ppvObject; }
実装しているのは、CreateObjectAとCreateObjectWの2つの関数。それぞれ、ANSI文字列とUNICODE文字列での呼び出しに対応している。引数は、DLLファイルのファイル名とCLSIDで、CLSIDは"{1303599D-3028-4eac-97BB-D9370B88B2DA}"のような文字列で与える(この文字列が不明の場合は、COMをインストールした後にレジストリーを調べること)。戻り値は、オブジェクトのポインター。エラーの場合は、0が返る。
VS2008のファイル一式は、こちら(ライセンスは、LGPL v 2.1)。