EternalWindows
MSHTML / イメージのダウンロード

今回は、ドキュメント内に含まれる一連のエレメントを走査する方法について説明します。 こうした方法について理解しておけば、idが設定されていないエレメントも取得することが可能になります。 例とて、<img>タグを列挙する方法について示します。

IHTMLElementCollection *pImgCollection;

bstrTag = SysAllocString(L"img");
pDocument3->getElementsByTagName(bstrTag, &pImgCollection);
SysFreeString(bstrTag);

IHTMLDocument3::getElementsByTagNameは、第1数のエレメントを列挙するためのIHTMLElementCollectionを第2引数に返します。 実際にエレメントを表すIHTMLElementを取得するには、IHTMLElementCollection::itemを繰り返して呼び出さなければならないため、 IHTMLElementを取得するまでの過程は少し長いといえます。 ちなみに、<img>タグの列挙に限定するならば、IHTMLDocument2::get_imagesを呼び出した方が効率的です。

pDocument2->get_images(&pImgCollection);

このようにすれば、タグ名を表した文字列を明示的に確保する必要はなくなります。 使用するインターフェースがIHTMLDocument3ではなくIHTMLDocument2であることに注意してください。 IHTMLDocument3はIHTMLDocument2を継承しているわけではないので、 ドキュメントを要求する時点でIID_IHTMLDocument2を指定しておくことになります。

今回のプログラムは、IEで現在表示されているページの全てのイメージをダウンロードします。 本ページにはイメージが表示されていないため、イメージが表示されているページへアクセスしておいてください。

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

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

BOOL GetImages(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;
	}
	
	GetImages(pDocument2);

	CoUninitialize();
	
	return 0;
}

BOOL GetImages(IHTMLDocument2 *pDocument2)
{
	BSTR                   bstrUrl;
	LONG                   i, lImgCount;
	VARIANT                varName, varIndex;
	IDispatch              *pDispatch;
	IHTMLImgElement        *pImgElement;
	IHTMLElementCollection *pImgCollection;
	WCHAR                  szSrcDirectory[] = L"C:\\sample";
	WCHAR                  szFilePath[256];
	LPWSTR                 lpszFileName;

	pDocument2->get_images(&pImgCollection);

	pImgCollection->get_length(&lImgCount);
	if (lImgCount == 0) {
		MessageBox(NULL, TEXT("イメージがありません。"), TEXT("OK"), MB_OK);
		return FALSE;
	}

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

		VariantInit(&varIndex);
		varIndex.vt = VT_I4;
		varIndex.lVal = 0;

		pImgCollection->item(varName, varIndex, &pDispatch);
		pDispatch->QueryInterface(IID_PPV_ARGS(&pImgElement));
		pDispatch->Release();

		pImgElement->get_href(&bstrUrl);
		
		lpszFileName = PathFindFileNameW(bstrUrl);
		wsprintfW(szFilePath, L"%s\\%s", szSrcDirectory, lpszFileName);
		URLDownloadToFileW(NULL, bstrUrl, szFilePath, 0, NULL);
		
		SysFreeString(bstrUrl);
		pImgElement->Release();
	}

	MessageBox(NULL, TEXT("全てのイメージをダウンロードしました。"), TEXT("OK"), MB_OK);
	
	pImgCollection->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;
}

IHTMLElementCollectionはエレメントを列挙することができるため、 まずは列挙数を取得するためにget_lengthを呼び出します。 そして、この数だけループしてitemを呼び出すことになります。

VariantInit(&varName);
varName.vt = VT_I4;
varName.lVal = i;

VariantInit(&varIndex);
varIndex.vt = VT_I4;
varIndex.lVal = 0;

pImgCollection->item(varName, varIndex, &pDispatch);
pDispatch->QueryInterface(IID_PPV_ARGS(&pImgElement));
pDispatch->Release();

itemの第1引数と第2引数は共にVARIANT構造体であり、 第1引数の方にアイテムのインデックスを指定します。 第2引数の方については0で構いません。 エレメントはIDispatchの形で第3引数に返りますが、 今回の場合は<img>タグを表しますから、 QueryInterfaceでIHTMLImgElementを取得できます。

IHTMLImgElementを取得したら、エレメントからURLを取得してイメージをダウンロードします。

pImgElement->get_href(&bstrUrl);
		
lpszFileName = PathFindFileNameW(bstrUrl);
wsprintfW(szFilePath, L"%s\\%s", szSrcDirectory, lpszFileName);
URLDownloadToFileW(NULL, bstrUrl, szFilePath, 0, NULL);

IHTMLImgElement::get_hrefを呼び出せば、イメージのURL(src属性)を取得できます。 後は、URLDownloadToFileを呼び出してこれをダウンロードすればよいのですが、 そのためにはダウンロード先となるファイルのパスを完成しておかなければなりません。 PathFindFileNameを呼び出せばURLからファイル名の部分のみを取得できるため、 これとダウンロード先フォルダのパスを連結すれば、ダウンロード先のファイルパスが完成します。


戻る