EternalWindows
オートメーション / Wordの操作

前節では、タイプライブラリを通じてオブジェクトが実装するメソッドやプロパティを確認しましたが、 これだけではまだ、それらの関数を呼び出せる段階になったとはいえません。 単純に、その関数を呼び出すことによって何が起きるかが分かりませんし、 個々の引数の意味も分かりません。 よってオブジェクトを操作するクライアントは、 メソッドやプロパティの名前だけでなく、その使い方についても理解しておく必要があります。

Officeアプリケーションが作成するオブジェクトは、MSDNに簡単なリファレンスが掲載されています。 たとえば、Wordが作成するオブジェクトのリストは、Office Development→2007 Microsoft Office System→Word 2007 →Word 2007 Developer Reference→Word Object Model Referenceから確認することができます。

たとえば、上記でApplication Objectを選択すると、Applicationオブジェクトのメソッドやプロパティを確認することができます。 例としてDocumentsプロパティの使い方を調べてみましょう。

リファレンスの書き方が通常のWindows APIと異なって分かりにくいところですが、 Returns a Documentsという一文に注目してください。 どうやら、Documentsプロパティを呼び出すと、Documentsオブジェクトが返るようです。 このように新しいオブジェクトが登場した場合は、今度はそのオブジェクトについて調べることになります。

DocumentsオブジェクトのOpenメソッドを呼び出した場合、 指定したドキュメントがオープンされ、そのドキュメントを表すDocumentオブジェクトが返るようです。 想像がつくように、このドキュメントというのはWordにおける1つの文書のことであり、 Openメソッドを使用することによって、Wordに1つのドキュメントを読み込ませることができます。

今回のプログラムは、Wordを起動して1つのドキュメントを開きます。 ドキュメントは、c:\\sample.docであると仮定しています。

#include <windows.h>

HRESULT Invoke(IDispatch *pDispatch, LPOLESTR lpszName, WORD wFlags, VARIANT *pVarArray, int nArgs, VARIANT *pVarResult);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	IDispatch *pApplication;
	IDispatch *pDocuments;
	IDispatch *pDocument;
	CLSID     clsid;
	HRESULT   hr;
	VARIANT   var;
	VARIANT   varResult;

	CoInitialize(NULL);
	
	hr = CLSIDFromProgID(L"Word.Application", &clsid);
	if (FAILED(hr)) {
		CoUninitialize();
		return 0;
	}
	
	hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&pApplication));
	if (FAILED(hr)) {
		CoUninitialize();
		return 0;
	}
	
	var.vt = VT_I4;
	var.lVal = 1;
	Invoke(pApplication, L"Visible", DISPATCH_PROPERTYPUT, &var, 1, NULL);

	VariantInit(&varResult);
	Invoke(pApplication, L"Documents", DISPATCH_PROPERTYGET, NULL, 0, &varResult);
	pDocuments = varResult.pdispVal;
 
	var.vt = VT_BSTR;
	var.bstrVal = SysAllocString(L"C:\\sample.doc");
	VariantInit(&varResult);
	hr = Invoke(pDocuments, L"Open", DISPATCH_METHOD, &var, 1, &varResult);
	pDocument = varResult.pdispVal;
	SysFreeString(var.bstrVal);
	if (FAILED(hr)) {
		MessageBox(NULL, TEXT("ドキュメントのオープンに失敗しました。"), NULL, MB_ICONWARNING);
		pDocuments->Release();
		pApplication->Release();
		CoUninitialize();
		return 0;
	}

	pDocument->Release();
	pDocuments->Release();
	pApplication->Release();
	CoUninitialize();
	
	return 0;
}

HRESULT Invoke(IDispatch *pDispatch, LPOLESTR lpszName, WORD wFlags, VARIANT *pVarArray, int nArgs, VARIANT *pVarResult)
{
	DISPPARAMS dispParams;
	DISPID     dispid;
	DISPID     dispidName = DISPID_PROPERTYPUT;
	HRESULT    hr;
	
	hr = pDispatch->GetIDsOfNames(IID_NULL, &lpszName, 1, LOCALE_USER_DEFAULT, &dispid);
	if (FAILED(hr))
		return hr;
	
	dispParams.cArgs = nArgs;
	dispParams.rgvarg = pVarArray;
	if (wFlags & DISPATCH_PROPERTYPUT) {
		dispParams.cNamedArgs = 1;
		dispParams.rgdispidNamedArgs = &dispidName;
	}
	else {
		dispParams.cNamedArgs = 0;
		dispParams.rgdispidNamedArgs = NULL;
	}

	hr = pDispatch->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, wFlags, &dispParams, pVarResult, NULL, NULL);

	return hr;
}

ドキュメントを開くには、DocumentsオブジェクトのOpenメソッドを呼び出す必要があります。 このためはまず、Documentsオブジェクトを取得しなければなりませんから、 ApplicationオブジェクトのDocumentsプロパティを呼び出します。

VariantInit(&varResult);
Invoke(pApplication, L"Documents", DISPATCH_PROPERTYGET, NULL, 0, &varResult);
pDocuments = varResult.pdispVal;

Invokeの第1引数は、Documentsプロパティを実装するApplicationオブジェクトのポインタを指定します。 第2引数はDocumentsという名前を指定し、これは取得に関するプロパティですから第3引数はDISPATCH_PROPERTYGETを指定します。 取得に関するプロパティに引数を指定する必要はないため、第4引数と第5引数はNULLと0で構いません。 第6引数は、プロパティの戻り値を受け取る変数のアドレスを指定します。 この場合は、事前にVariantInitを呼び出しておく必要があります。 関数が成功した場合はpdispValに戻り値が格納され、 Documentsプロパティの場合はDocumentsオブジェクトへのポインタですから、 IDispatch型のpDocumentsという変数で受け取っています。

Documentsオブジェクトへのポインタを取得したら、これを使用してOpenメソッドを呼び出すことができます。

var.vt = VT_BSTR;
var.bstrVal = SysAllocString(L"C:\\sample.doc");
VariantInit(&varResult);
hr = Invoke(pDocuments, L"Open", DISPATCH_METHOD, &var, 1, &varResult);
pDocument = varResult.pdispVal;
SysFreeString(var.bstrVal);

Openメソッドは引数としてファイル名を要求するため、 これをVARIANT構造体に格納できるように、vtメンバにVT_BSTRを指定します。 これにより、bstrValメンバにBSTR型の文字列を指定することができます。 Invokeの第1引数はpDocumentsを指定していますが、ここで間違ってもpApplicationを指定してはいけません。 Openメソッドを実装するのはDocumentsオブジェクトですから、 これを識別しているpDocumentsを指定することになります。 第2引数はOpenという文字列を指定し、これはメソッドですから第3引数はDISPATCH_METHODになります。 第5引数はメソッドの引数とする数であり、ここでは1としています。 実際には、Openメソッドにはまだ他に引数があるのですが、それらはオブションとなっているため、 ファイル名を格納した1つのVARIANT構造体のみを指定しています。 Openメソッドは戻り値としてDocumentオブジェクトを返すため、第6引数を通じてこれを受け取り、 pdispValからDocumentオブジェクトのポインタを取得します。 SysFreeStringはBSTR型の文字列を開放する関数ですが、 今回のように文字列がVARIANT構造体に格納されている場合はVariantClearで文字列を開放しても構いません。

ドキュメントの保存

文字列をドキュメントに保存する例を次に示します。

#include <windows.h>

HRESULT Invoke(IDispatch *pDispatch, LPOLESTR lpszName, WORD wFlags, VARIANT *pVarArray, int nArgs, VARIANT *pVarResult);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	IDispatch *pApplication;
	IDispatch *pDocuments;
	IDispatch *pDocument;
	IDispatch *pRange;
	CLSID     clsid;
	HRESULT   hr;
	VARIANT   var;
	VARIANT   varResult;

	CoInitialize(NULL);
	
	hr = CLSIDFromProgID(L"Word.Application", &clsid);
	if (FAILED(hr)) {
		CoUninitialize();
		return 0;
	}
	
	hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&pApplication));
	if (FAILED(hr)) {
		CoUninitialize();
		return 0;
	}

	VariantInit(&varResult);
	Invoke(pApplication, L"Documents", DISPATCH_PROPERTYGET, NULL, 0, &varResult);
	pDocuments = varResult.pdispVal;
	
	VariantInit(&varResult);
	Invoke(pDocuments, L"Add", DISPATCH_METHOD, NULL, 0, &varResult);
	pDocument = varResult.pdispVal;
	
	VariantInit(&varResult);
	Invoke(pDocument, L"Content", DISPATCH_PROPERTYGET, NULL, 0, &varResult);
	pRange = varResult.pdispVal;
	
	var.vt = VT_BSTR;
	var.bstrVal = SysAllocString(L"sample\ntext");
	Invoke(pRange, L"Text", DISPATCH_PROPERTYPUT, &var, 1, NULL);
	SysFreeString(var.bstrVal);
	
	var.vt = VT_BSTR;
	var.bstrVal = SysAllocString(L"C:\\sample.doc");
	hr = Invoke(pDocument, L"SaveAs", DISPATCH_METHOD, &var, 1, NULL);
	SysFreeString(var.bstrVal);
	if (SUCCEEDED(hr))
		MessageBox(NULL, TEXT("ドキュメントを保存しました。"), TEXT("OK"), MB_OK);
	else
		MessageBox(NULL, TEXT("ドキュメントの保存に失敗しました。"), NULL, MB_ICONWARNING);

	var.vt = VT_BOOL;
	var.boolVal = FALSE;
	Invoke(pDocument, L"Close", DISPATCH_METHOD, &var, 1, NULL);
	Invoke(pApplication, L"Quit", DISPATCH_METHOD, NULL, 0, NULL);

	pRange->Release();
	pDocument->Release();
	pDocuments->Release();
	pApplication->Release();
	CoUninitialize();
	
	return 0;
}

HRESULT Invoke(IDispatch *pDispatch, LPOLESTR lpszName, WORD wFlags, VARIANT *pVarArray, int nArgs, VARIANT *pVarResult)
{
	DISPPARAMS dispParams;
	DISPID     dispid;
	DISPID     dispidName = DISPID_PROPERTYPUT;
	HRESULT    hr;
	
	hr = pDispatch->GetIDsOfNames(IID_NULL, &lpszName, 1, LOCALE_USER_DEFAULT, &dispid);
	if (FAILED(hr))
		return hr;
	
	dispParams.cArgs = nArgs;
	dispParams.rgvarg = pVarArray;
	if (wFlags & DISPATCH_PROPERTYPUT) {
		dispParams.cNamedArgs = 1;
		dispParams.rgdispidNamedArgs = &dispidName;
	}
	else {
		dispParams.cNamedArgs = 0;
		dispParams.rgdispidNamedArgs = NULL;
	}

	hr = pDispatch->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, wFlags, &dispParams, pVarResult, NULL, NULL);

	return hr;
}

まず、Visibleプロパティを呼び出していない点に注目してください。 今回は、ドキュメントを保存する目的でWordを起動しますから、Wordのウインドウが表示される必要はありません。 Documentsプロパティを呼び出してDocumentsオブジェクトを取得したら、 Addメソッドを呼び出してDocumentオブジェクトを取得します。 さらに今度は、DocumentオブジェクトのContentプロパティを呼び出し、Rangeオブジェクトを取得します。 このオブジェクトのTextプロパティを呼び出すと文字列を書き込むことができるため、 vtメンバにVT_BSTRを指定し、bstrValに書き込みたい文字列を指定します。

文字列の書き込みが終了したら、ドキュメントオブジェクトのSaveAsメソッドを呼び出してドキュメントを保存します。 指定する引数は、保存先のファイル名でなければなりません。 保存が終了したらDocumentオブジェクトのCloseメソッドでドキュメントを閉じ、 ApplicationオブジェクトのQuitメソッドでWordを終了させます。 今回の場合、Closeメソッドの引数としてFALSEを指定するのは非常に重要であり、 FALSE以外を指定するとWordによって保存確認が行われるようになります。 しかし、今回はVisibleプロパティを呼び出していない関係上、この確認に応答することができませんから、 確認を行わないようにFALSEを指定します。



戻る