EternalWindows
MSHTML / スタイルシートの作成

今回は、アプリケーション内で作成したスタイルシートをブラウザに適応させる方法について説明します。 スタイルシートはエレメントのスタイルを定義しているため、 これを使用することでエレメントの外観を変更できるようになります。 現在使用されているスタイルシートは、次の方法で確認できます。

BOOL EnumStyleSheet(IHTMLDocument2 *pDocument2)
{
	LONG                       i, lCount;
	BSTR                       bstrHref;
	VARIANT                    varIndex, varResult;
	IHTMLStyleSheet            *pStyleSheet;
	IHTMLStyleSheetsCollection *pStyleSheetsCollection;
	
	pDocument2->get_styleSheets(&pStyleSheetsCollection);
	
	pStyleSheetsCollection->get_length(&lCount);

	for (i = 0; i < lCount; i++) {
		VariantInit(&varIndex);
		varIndex.vt = VT_I4;
		varIndex.lVal = i;

		pStyleSheetsCollection->item(&varIndex, &varResult);
		varResult.pdispVal->QueryInterface(IID_PPV_ARGS(&pStyleSheet));
		varResult.pdispVal->Release();

		pStyleSheet->get_href(&bstrHref);
		MessageBoxW(NULL, bstrHref, L"OK", MB_OK);
		
		SysFreeString(bstrHref);
		pStyleSheet->Release();
	}

	pStyleSheetsCollection->Release();

	return TRUE;
}

IHTMLDocument2::get_styleSheetsを呼び出せば、一連のスタイルシートを管理するIHTMLStyleSheetsCollectionを取得できます。 get_lengthでスタイルシートの数を取得したら、itemを呼び出すことでスタイルシートを表すIHTMLStyleSheetを取得でき、 get_hrefを呼び出せばスタイルシートのリンク先を取得できます。 また、get_cssTextを呼び出せばスタイルシートに記述されているテキストを取得できます。

今回のプログラムは、新しいスタイルシートを作成する例を示しています。

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

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

BOOL CreateStyleSheet(IHTMLDocument2 *pDocument2);
BOOL GetDocumentFromIE(IHTMLDocument2 **pp);
BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	IHTMLDocument2 *pDocument2;

	CoInitialize(NULL);

	if (!GetDocumentFromIE(&pDocument2)) {
		CoUninitialize();
		return 0;
	}
	
	CreateStyleSheet(pDocument2);

	CoUninitialize();
	
	return 0;
}

BOOL CreateStyleSheet(IHTMLDocument2 *pDocument2)
{
	LONG                       lCount;
	BSTR                       bstrHref, bstrText;
	IHTMLStyleSheet            *pStyleSheet;
	IHTMLStyleSheetsCollection *pStyleSheetsCollection;
	
	pDocument2->get_styleSheets(&pStyleSheetsCollection);
	
	pStyleSheetsCollection->get_length(&lCount);

	bstrHref = SysAllocString(L"sample_css");
	pDocument2->createStyleSheet(bstrHref, lCount, &pStyleSheet);
	
	bstrText = SysAllocString(L"body{background-color : #0000ff;}");
	pStyleSheet->put_cssText(bstrText);

	MessageBox(NULL, TEXT("スタイルシートを変更しました。"), TEXT("OK"), MB_OK);

	SysFreeString(bstrText);
	SysFreeString(bstrHref);
	pStyleSheet->Release();
	pStyleSheetsCollection->Release();

	return TRUE;
}

BOOL GetDocumentFromIE(IHTMLDocument2 **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_IHTMLDocument2, 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;
}

スタイルシートは、IHTMLDocument2::createStyleSheetを呼び出すことで作成できます。 第1引数はスタイルシートのリンク先とする文字列ですが、これは自由に決定して問題ないと思われます。 第2引数はスタイルシートをコレクションに追加する際のインデックスですが、 今回は最後のインデックスを指定するようにしています。 理由は、0を指定してコレクションの先頭に追加すると、 既存のスタイルシートのスタイルが先行されてしまうからです。 put_cssTextを呼び出せばスタイルシートにテキストが設定され、 エレメントの外観もそれを反映するはずです。

エレメントのスタイル

今回はスタイルシートを使用してエレメントのスタイルを変更しましたが、 実際にテキストに記述したエレメントは<body>タグだけでした。 このように1つのエレメントのスタイルのみを変更する場合は、 エレメントのスタイルを直接変更したほうが効率的でしょう。 スタイルを表すインターフェースにはIHTMLStyleとIHTMLCurrentStyleがあるため、 まずはその違いを確認します。

BOOL GetBackgroundColor(IHTMLDocument2 *pDocument2)
{
	VARIANT           var;
	IHTMLElement      *pElement;
	IHTMLElement2     *pElement2;
	IHTMLStyle        *pStyle;
	IHTMLCurrentStyle *pCurrentStyle;

	pDocument2->get_body(&pElement);
	
	pElement->get_style(&pStyle);
	pStyle->get_backgroundColor(&var);
	MessageBoxW(NULL, var.bstrVal, L"OK", MB_OK);
	VariantClear(&var);
	pStyle->Release();
	
	pElement->QueryInterface(&pElement2);
	pElement2->get_currentStyle(&pCurrentStyle);
	pCurrentStyle->get_backgroundColor(&var);
	MessageBoxW(NULL, var.bstrVal, L"OK", MB_OK);
	VariantClear(&var);
	pCurrentStyle->Release();
	pElement2->Release();

	pElement->Release();

	return TRUE;
}

IHTMLStyleはIHTMLElement::get_styleで取得でき、IHTMLCurrentStyleはIHTMLElement2::get_currentStyleで取得できます。 どちらにおいても、get_backgroundColorを呼び出して背景色を取得しているわけですが、 おそらくIHTMLStyleの方は正しい結果が得られていないはずです。 理由は、IHTMLStyleが<body style = "background-color : #0000ff">のような インラインスタイルシートしか考慮しないからです。 よって、スタイルを取得する場合は、IHTMLCurrentStyleを使用するのがよいと思われます。

IHTMLCurrentStyleには設定に関するメソッドがないため、 スタイルを変更する場合はIHTMLStyleを使用することになります。

BOOL PutBackgroundColor(IHTMLDocument2 *pDocument2)
{
	VARIANT      var;
	IHTMLElement *pElement;
	IHTMLStyle   *pStyle;

	pDocument2->get_body(&pElement);
	pElement->get_style(&pStyle);

	var.vt = VT_I4;
	var.lVal = 0x0000ff;
	pStyle->put_backgroundColor(var);

	VariantClear(&var);
	pStyle->Release();
	pElement->Release();

	return TRUE;
}

これにより、<body>の背景色が変更されたことになります。 IHTMLStyleで明示的に設定したスタイルは、同じくIHTMLStyleでも取得できるようになります。



戻る