EternalWindows
モニカ / ROT

モニカを使用するのは、基本的にオブジェクトを操作するクライアント側ですが、 オブジェクトを管理するサーバー側が使用することもあります。 たとえば、実行中のオブジェクトをクライアントが参照できるようにするには、 そのオブジェクトと共にモニカをROTに格納することになります。 ROT(Running Object Table)とは、ワークステーションに1つ存在するグローバルなテーブルであり、 様々なサーバーによって作成された現在実行中のオブジェクトが格納されています。 ROTを操作するインターフェースはIRunningObjectTableであり、 GetRunningObjectTableから取得することができます。

HRESULT GetRunningObjectTable(
  DWORD reserved,
  LPRUNNINGOBJECTTABLE *pprot
);

reservedは、予約されているため0を指定します。 pprotは、IRunningObjectTable型変数のアドレスを指定します。

今回は実際にサーバーを作成するわけではありませんが、 サーバーがどのようにしてオブジェクトを登録するかは、理解しておいたほうがよいといえます。 オブジェクトを登録することで生じる利点は様々ですが、 クライアントがオブジェクトを参照できるようになることや、 オブジェクトへのバインドが高速になることが挙げられます。 サーバーはオブジェクトをROTに登録したい段階になると、IRunningObjectTable::Registerを呼び出します。

CreateItemMoniker(L"!", szClsid, &pMoniker);
pRunningObjectTable->Register(0, pObject, pMoniker, &dwRegister);

Registerの呼び出しには登録したいオブジェクトとモニカが必要になります。 オブジェクトはサーバー内で定義されているため特に準備は不要ですが、 モニカついてはサーバーが明示的に作成することになります。 一般的には、このモニカはオブジェクトのCLSIDを名前としてアイテムモニカになりますが、 WordやExcelなどでは現在開かれているファイルを表すファイルモニカも作成しています。 Registerの第1引数は0で問題ありませんが、この場合は登録したオブジェクトにアクセスできるクライアントが、 サーバーと同じウインドウステーション上で実行されているクライアントに制限されることになります。 ROTFLAGS_ALLOWANYCLIENTを指定すれば、サービスのような異なるウインドウステーション上で実行されているクライアントからも、 オブジェクトを取得できることになります。 第4引数に返された値は、IRunningObjectTable::Revokeでオブジェクトの登録を解除する際に使用します。

ROTに登録されたモニカは、EnumRunningで列挙することができます。

HRESULT IRunningObjectTable::EnumRunning(
  IEnumMoniker **ppenumMoniker
);

ppenumMonikerは、IEnumMoniker型の変数のアドレスを指定します。

今回のプログラムは、ROTに登録されているオブジェクトの名前を列挙します。

#include <windows.h>

void EnumObjectFromROT(HWND hwndListBox);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndListBox = NULL;
	
	switch (uMsg) {

	case WM_CREATE: {
		CoInitialize(NULL);
		hwndListBox = CreateWindowEx(0, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		EnumObjectFromROT(hwndListBox);
		return 0;
	}

	case WM_SIZE:
		MoveWindow(hwndListBox, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
		return 0;

	case WM_DESTROY:
		CoUninitialize();
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

void EnumObjectFromROT(HWND hwndListBox)
{
	IMoniker            *pMoniker;
	IEnumMoniker        *pEnumMoniker;
	IBindCtx            *pBindCtx;
	IRunningObjectTable *pRunningObjectTable;
	LPOLESTR            lpszDisplayName;
	
	GetRunningObjectTable(0, &pRunningObjectTable);
	pRunningObjectTable->EnumRunning(&pEnumMoniker);
	
	CreateBindCtx(NULL, &pBindCtx);

	while (pEnumMoniker->Next(1, &pMoniker, NULL) == S_OK) {
		pMoniker->GetDisplayName(pBindCtx, NULL, &lpszDisplayName);
		SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)lpszDisplayName);
		pMoniker->Release();
	}
	
	pBindCtx->Release();
	pEnumMoniker->Release();
	pRunningObjectTable->Release();
}

実行結果を確認してみると、CLSIDを含んだアイテムモニカが多く列挙されているのではないかと思われます。 表示されているCLSIDを基にレジストリを確認すれば、実際にどのようなオブジェクトが登録されているかを把握することができます。 特に何も列挙されない場合は、Visual Studioを実行した状態で今回のプログラムを起動すればよいでしょう。 Visual StudioはCLSIDを基にしたアイテムモニカとVisualStudio.DTEというアイテムモニカ、 さらに現在開いているソリューションファイルのファイルモニカを作成します。 これが意味することは、それら3つのモニカのいずれかを使用してVisual Studioのオブジェクトを取得できるということですが、 実際に取得したオブジェクトが全て同一であるとは限りません。

自作関数のEnumObjectFromROTでは、IRunningObjectTable::EnumRunningを呼び出すことでIEnumMonikerを取得しています。 このインターフェースのNextを呼び出せば、ROTに登録されているモニカを取得することができ、 GetDisplayNameを呼び出せばモニカの表示名を取得することができます。

登録されたオブジェクトの取得

CreateFileMonikerやMkParseDisplayNameで作成したモニカにおけるバインドでは、 目的のオブジェクトがROTに存在しているかを確認することになっています。 つまり、オブジェクトが既に実行状態である場合はそれを返し、 実行されていない場合は新しくオブジェクトを作成するようになっています。 このようなオブジェクトの作成を望まず、 実行されているオブジェクトのみを取得したい場合は、 ROTへ明示的にアクセスすることになります。

#include <windows.h>

BOOL GetObjectFromROT(LPOLESTR lpszName, IUnknown **ppUnknown);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	IUnknown *pUnknown;

	CoInitialize(NULL);
	
	if (!GetObjectFromROT(L"C:\\sample.xlsx", &pUnknown)) {
		MessageBox(NULL, TEXT("オブジェクトの取得に失敗しました。"), NULL, MB_ICONWARNING);
		CoUninitialize();
		return 0;
	}

	MessageBox(NULL, TEXT("オブジェクトを取得しました。"), TEXT("OK"), MB_OK);

	pUnknown->Release();
	CoUninitialize();

	return 0;
}

BOOL GetObjectFromROT(LPOLESTR lpszName, IUnknown **ppUnknown)
{
	HRESULT             hr = E_FAIL;
	IMoniker            *pMoniker;
	IEnumMoniker        *pEnumMoniker;
	IBindCtx            *pBindCtx;
	IRunningObjectTable *pRunningObjectTable;
	LPOLESTR            lpszDisplayName;

	*ppUnknown = NULL;
	
	GetRunningObjectTable(0, &pRunningObjectTable);
	pRunningObjectTable->EnumRunning(&pEnumMoniker);
	
	CreateBindCtx(NULL, &pBindCtx);

	while(pEnumMoniker->Next(1, &pMoniker, NULL) == S_OK) {
		pMoniker->GetDisplayName(pBindCtx, NULL, &lpszDisplayName);
		if (lstrcmpW(lpszName, lpszDisplayName) == 0) {
			hr = pRunningObjectTable->GetObject(pMoniker, ppUnknown);
			pMoniker->Release();
			break;
		}
		pMoniker->Release();
	}
	
	pBindCtx->Release();
	pEnumMoniker->Release();
	pRunningObjectTable->Release();

	return hr == S_OK;
}

自作関数であるGetObjectFromROTは、第1引数に関連するオブジェクトが実行されているかを確認し、 実行されている場合はそのオブジェクトを返します。 列挙されたモニカのGetDisplayNameの結果がlpszNameと一致した場合は、 目的のファイルが見つかったことになりますから、 IRunningObjectTable::GetObjectを通じてモニカに関連するオブジェクトを取得します。

CLSIDを基に実行中のオブジェクトを取得したい場合は、オートメーションAPIのGetActiveObjectを呼び出すのがよいと思われます。 この関数は内部で、目的のCLISDを持ったオブジェクトをIRunningObjectTable::GetObjectで取得するようになっています。 IDispatchを実装するオートメーションオブジェクトをROTに登録する場合は、 IRunningObjectTable::Registerを呼び出すよりも、RegisterActiveObjectを呼び出すのが適切であるかもしれません。



戻る