簡単に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)。