EternalWindows
MSHTML / エレメントの追加

今回は、DOMのノードという概念を使用してエレメントのデータを書き換えます。 DOMは、エレメントをオブジェクトとして扱うための方法を提供し、 これまで使用してきたIHTMLElementなどはDOMの機能によるものです。 しかし、DOMはエレメントを階層上に存在するオブジェクトとして扱うこともあり、 そのようなときはエレメントのことをノードと呼びます。 ノードについて理解するために、次に例を示します。

<body>
<div>
eternal
</div>
</body>

上記では、bodyノードの中にdivノードが存在していますが、 このようなときdivノードはbodyノードの子であると表現されます。 今回行おうとするのは、このようなノードの中へ動的に新しいノードを追加することであり、 イメージとしては上記のdivを次のように変更します。

<body>
<div>
eternal
<font color = "#ff00000">windows</font>
</div>
</body>

上記では、divノードの中にfontノードが存在していますから、 divノードのデータは変更されたことになります。 ただし、本当にhtmlファイルのdivノードが書き換えられたわけではないことに注意してください。 あくまでmshtml内に維持されているデータを書き換えただけなので、 ページをリロードすると元のhtmlファイルの中身が表示されるようになります。

今回のプログラムは、下記のdivノードのデータを書き換えます。 実行すると、eternalという文字列の後ろにwindowsという赤の文字列が表示されます。

eternal
#include <windows.h>
#include <exdisp.h>
#include <mshtml.h>
#include <oleacc.h>

#pragma comment (lib, "oleacc.lib")

BOOL AddElement(IHTMLDocument3 *pDocument3);
BOOL GetDocumentFromIE(IHTMLDocument3 **pp);
BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	IHTMLDocument3 *pDocument3;

	CoInitialize(NULL);

	if (!GetDocumentFromIE(&pDocument3)) {
		CoUninitialize();
		return 0;
	}
	
	AddElement(pDocument3);

	CoUninitialize();
	
	return 0;
}

BOOL AddElement(IHTMLDocument3 *pDocument3)
{
	BSTR           bstrId, bstrColor, bstrElement, bstrAttribute, bstrText;
	VARIANT        var;
	IHTMLDOMNode   *pDomNode, *pChildNode, *pChildRef;
	IHTMLDocument2 *pDocument2;
	IHTMLElement   *pElement, *pNewElement;

	bstrId = SysAllocString(L"sample");
	pDocument3->getElementById(bstrId, &pElement);
	if (pElement == NULL) {
		SysFreeString(bstrId);
		return FALSE;
	}
	
	pElement->QueryInterface(IID_PPV_ARGS(&pDomNode));
	pDocument3->QueryInterface(IID_PPV_ARGS(&pDocument2));

	bstrElement = SysAllocString(L"font");
	bstrColor = SysAllocString(L"red");
	bstrAttribute = SysAllocString(L"color");
	bstrText = SysAllocString(L"windows");
	
	pDocument2->createElement(bstrElement, &pNewElement);
	VariantInit(&var);
	var.vt = VT_BSTR;
	var.bstrVal = bstrColor;
	pNewElement->setAttribute(bstrAttribute, var);
	pNewElement->put_innerText(bstrText);

	pNewElement->QueryInterface(IID_PPV_ARGS(&pChildNode));
	pDomNode->appendChild(pChildNode, &pChildRef);
	
	MessageBox(NULL, TEXT("エレメントを追加しました。"), TEXT("OK"), MB_OK);
	
	SysFreeString(bstrText);
	SysFreeString(bstrAttribute);
	SysFreeString(bstrColor);
	SysFreeString(bstrElement);
	SysFreeString(bstrId);
	pChildRef->Release();
	pChildNode->Release();
	pNewElement->Release();
	pDocument2->Release();
	pDomNode->Release();
	pElement->Release();

	return TRUE;
}

BOOL GetDocumentFromIE(IHTMLDocument3 **pp)
{
	HWND    hwnd;
	UINT    uMsg;
	LRESULT lResult;
	HRESULT hr;
	
	EnumChildWindows(FindWindow(TEXT("IEFrame"), NULL), EnumChildProc, (LPARAM)&hwnd);
	if (hwnd == NULL)
		return FALSE;

	uMsg = RegisterWindowMessage(TEXT("WM_HTML_GETOBJECT"));
	if (!SendMessageTimeout(hwnd, uMsg, 0, 0, SMTO_ABORTIFHUNG, 1000, (LPDWORD)&lResult))
		return FALSE;

	hr = ObjectFromLresult(lResult, IID_IHTMLDocument3, 0, (void **)pp);
	if (FAILED(hr))
		return FALSE;

	return TRUE;
}

BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam)
{
	TCHAR szClassName[256];

	GetClassName(hwnd, szClassName, sizeof(szClassName) / sizeof(TCHAR));
	if (lstrcmp(szClassName, TEXT("Internet Explorer_Server")) == 0) {
		*((HWND *)lParam) = hwnd;
		return FALSE;
	}
	else
		return TRUE;
}

エレメントをノードとして扱うにはIHTMLDOMNodeが必要になるため、 pElement->QueryInterfaceでこれを取得します。 ここで取得したpDomNodeはdivノードを表しています。 IHTMLDocument3には新規エレメントを作成するメソッドがないため、 pDocument3->QueryInterfaceでIHTMLDocument2を取得します。 IHTMLDOMNodeとIHTMLDocument2を取得したら、ノードを追加する作業に入ります。

pDocument2->createElement(bstrElement, &pNewElement);
VariantInit(&var);
var.vt = VT_BSTR;
var.bstrVal = bstrColor;
pNewElement->setAttribute(bstrAttribute, var);
pNewElement->put_innerText(bstrText);

pNewElement->QueryInterface(IID_PPV_ARGS(&pChildNode));
pDomNode->appendChild(pChildNode, &pChildRef);

pDocument2->createElementによって、新しいエレメントを表すIHTMLElementを取得できます。 エレメントのタグ名は第1引数に指定し、今回の場合はfontになります。 setAttributeを呼び出せばエレメントに属性を指定でき、 さらにput_innerTextを呼び出せばエレメントに文字列を指定できます。 この時点でpNewElementは、<font color = "#ff00000">windows</font>を表していると考えてよいでしょう。 後はこれをdivノードに追加すればよいため、pDomNode->appendChildを呼び出します。 これにより、第1引数のfontノードがdivノードに追加されたことになります。


戻る