プログラミング

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

コメント

コメントはありません

コメント送信