EternalWindows
オートメーション / コレクションオブジェクト

Officeアプリケーションが作成するオブジェクトには、コレクションオブジェクトと呼ばれる種類があります。 コレクションオブジェクトは、複数のオブジェクトを管理するオブジェクトのことで、 たとえばWordのDocumentsオブジェクトはDocumentオブジェクトを、 ExcelのWorkbooksオブジェクトはWorkbookオブジェクトを管理しています。 また、コレクションオブジェクトには必須実装となっているプロパティがあり、 Countプロパティは管理しているオブジェクトの数を返し、 Itemプロパティは指定されたインデックスで識別されるオブジェクトを返します。

今回のプログラムは、Excelのワークブックに含まれるワークシートの名前を表示します。 ワークシートが3個存在するのであれば、3回名前が表示されることになります。 ワークブックは、c:\\sample.xlsであると仮定しています。

#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 *pWorkbooks;
	IDispatch *pWorkbook;
	IDispatch *pWorksheets;
	IDispatch *pWorksheet;
	LONG      i;
	LONG      lCount;
	CLSID     clsid;
	HRESULT   hr;
	VARIANT   var;
	VARIANT   varResult;

	CoInitialize(NULL);
	
	hr = CLSIDFromProgID(L"Excel.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"Workbooks", DISPATCH_PROPERTYGET, NULL, 0, &varResult);
	pWorkbooks = varResult.pdispVal;
	
	var.vt = VT_BSTR;
	var.bstrVal = SysAllocString(L"C:\\sample.xls");
	VariantInit(&varResult);
	hr = Invoke(pWorkbooks, L"Open", DISPATCH_METHOD, &var, 1, &varResult);
	pWorkbook = varResult.pdispVal;
	SysFreeString(var.bstrVal);
	if (FAILED(hr)) {
		MessageBox(NULL, TEXT("ワークブックのオープンに失敗しました。"), NULL, MB_ICONWARNING);
		pWorkbooks->Release();
		pApplication->Release();
		CoUninitialize();
		return 0;
	}
	
	VariantInit(&varResult);
	Invoke(pWorkbook, L"Worksheets", DISPATCH_PROPERTYGET, NULL, 0, &varResult);
	pWorksheets = varResult.pdispVal;

	VariantInit(&varResult);
	Invoke(pWorksheets, L"Count", DISPATCH_PROPERTYGET, NULL, 0, &varResult);
	lCount = varResult.lVal;

	for (i = 0; i < lCount; i++) {
		var.vt = VT_I4;
		var.lVal = i + 1;
		VariantInit(&varResult);
		Invoke(pWorksheets, L"Item", DISPATCH_PROPERTYGET, &var, 1, &varResult);
		pWorksheet = varResult.pdispVal;

		VariantInit(&varResult);
		Invoke(pWorksheet, L"Name", DISPATCH_PROPERTYGET, NULL, 0, &varResult);
		MessageBoxW(NULL, (LPWSTR)varResult.bstrVal, TEXT("OK"), MB_OK);
		VariantClear(&varResult);

		pWorksheet->Release();
	}

	pWorksheets->Release();
	pWorkbook->Release();
	pWorkbooks->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;
}

ワークシートを列挙するには、Worksheetsオブジェクトを取得しなければなりません。 このための処理は次のようになっています。

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

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

VariantInit(&varResult);
Invoke(pWorkbook, L"Worksheets", DISPATCH_PROPERTYGET, NULL, 0, &varResult);
pWorksheets = varResult.pdispVal;

Worksheetsオブジェクトを取得するには、まずWorkbooksオブジェクトを取得し、 さらにWorkbookオブジェクトを取得しなければなりません。 Workbooksオブジェクトは、ApplicationオブジェクトのWorkbooksメソッドを呼び出すことで取得できます。 Workbooksオブジェクトを取得したら、Openメソッドを呼び出して1つのワークブックを開くことができるため、 これでWorkbookオブジェクトを取得できたことになります。 後はこのオブジェクトのWorksheetsプロパティを呼び出せば、Worksheetsオブジェクトを取得できたことになります。

Worksheetsオブジェクトを取得したら、Countプロパティを呼び出してオブジェクトの数を取得します。 そして、この数だけItemプロパティを呼び出してWorksheetオブジェクトを取得します。

VariantInit(&varResult);
Invoke(pWorksheets, L"Count", DISPATCH_PROPERTYGET, NULL, 0, &varResult);
lCount = varResult.lVal;

for (i = 0; i < lCount; i++) {
	var.vt = VT_I4;
	var.lVal = i + 1;
	VariantInit(&varResult);
	Invoke(pWorksheets, L"Item", DISPATCH_PROPERTYGET, &var, 1, &varResult);
	pWorksheet = varResult.pdispVal;

	VariantInit(&varResult);
	Invoke(pWorksheet, L"Name", DISPATCH_PROPERTYGET, NULL, 0, &varResult);
	MessageBoxW(NULL, (LPWSTR)varResult.bstrVal, TEXT("OK"), MB_OK);
	VariantClear(&varResult);

	pWorksheet->Release();
}

Itemプロパティはオブジェクトのインデックスを1から数えるため、 +1を指定した状態でインデックスを指定することになります。 pdispValから取得したWorksheetオブジェクトへのポインタはInvokeに指定され、 Nameプロパティの呼び出しに使用されています。 このプロパティは、ワークシートの名前をBSTR型で返すことができます。 VariantClearを呼び出しているのは、このBSTR型を開放するためです。

_NewEnumプロパティとIEnumVARIANT

コレクションオブジェクトが管理するオブジェクトは、_NewEnumプロパティを呼び出すことによっても列挙することができます。 このプロパティが返すオブジェクトはIEnumVARIANTというインターフェースを実装しているため、 これを取得してオブジェクトを列挙します。

#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    *pWorkbooks;
	IDispatch    *pWorkbook;
	IDispatch    *pWorksheets;
	IDispatch    *pWorksheet;
	IDispatch    *pNewEnum;
	IEnumVARIANT *pEnumVariant;
	CLSID        clsid;
	HRESULT      hr;
	VARIANT      var;
	VARIANT      varResult;

	CoInitialize(NULL);
	
	hr = CLSIDFromProgID(L"Excel.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"Workbooks", DISPATCH_PROPERTYGET, NULL, 0, &varResult);
	pWorkbooks = varResult.pdispVal;
	
	var.vt = VT_BSTR;
	var.bstrVal = SysAllocString(L"C:\\sample.xls");
	VariantInit(&varResult);
	hr = Invoke(pWorkbooks, L"Open", DISPATCH_METHOD, &var, 1, &varResult);
	pWorkbook = varResult.pdispVal;
	SysFreeString(var.bstrVal);
	if (FAILED(hr)) {
		MessageBox(NULL, TEXT("ワークブックのオープンに失敗しました。"), NULL, MB_ICONWARNING);
		pWorkbooks->Release();
		pApplication->Release();
		CoUninitialize();
		return 0;
	}
	
	VariantInit(&varResult);
	Invoke(pWorkbook, L"Worksheets", DISPATCH_PROPERTYGET, NULL, 0, &varResult);
	pWorksheets = varResult.pdispVal;
	
	VariantInit(&varResult);
	Invoke(pWorksheets, NULL, DISPATCH_PROPERTYGET, NULL, 0, &varResult);
	pNewEnum = varResult.pdispVal;
	pNewEnum->QueryInterface(IID_IEnumVARIANT, (void **)&pEnumVariant);
	
	VariantInit(&varResult);
	while (pEnumVariant->Next(1, &varResult, NULL) == S_OK) {
		if (varResult.vt != VT_DISPATCH)
			continue;
		pWorksheet = varResult.pdispVal;
		
		VariantInit(&varResult);
		Invoke(pWorksheet, L"Name", DISPATCH_PROPERTYGET, NULL, 0, &varResult);
		MessageBoxW(NULL, (LPWSTR)varResult.bstrVal, TEXT("OK"), MB_OK);
		VariantClear(&varResult);

		pWorksheet->Release();
	}

	pEnumVariant->Release();
	pNewEnum->Release();
	pWorksheets->Release();
	pWorkbook->Release();
	pWorkbooks->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;

	if (lpszName != NULL) {
		hr = pDispatch->GetIDsOfNames(IID_NULL, &lpszName, 1, LOCALE_USER_DEFAULT, &dispid);
		if (FAILED(hr))
			return hr;
	}
	else
		dispid = DISPID_NEWENUM;
	
	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;
}

Worksheetsオブジェクトを取得したら_NewEnumプロパティを呼び出します。 これは少し特殊なプロパティであり、タイプライブラリやMSDNからはこの名前を確認することができません。 しかし、DISPID_NEWENUMというDISPIDをIDispatch::Invokeに指定すれば呼び出すことができるので、 Invokeの第2引数にNULLを指定した場合はそのような処理が実行されることになっています。 _NewEnumプロパティが返したオブジェクトは列挙を行うために必要にIEnumVARIANTを実装しているので、 QueryInterfaceにIEnumVARIANTのIIDを指定して取得します。 IEnumVARIANTへのポインタを取得したら、Nextメソッドを呼び出すことでオブジェクトを列挙することができます。



戻る