概要

 MSHTMLを使用してaタグのリンク等を取得しエディットボックスに表示します。
 MSHTMLとは、インターネットエクスプローラーのhtmlファイルを解析するためのモジュールです。
 ここでは、http://yamatyuu.net/computer/program/vc2013/htmlget2/htmlget2.htmlを呼び出しています。
 ダイアログボックスはマウスによるサイズの変更に対応しています。
 マウスによるサイズ変更をする必要がない場合は、htmlget2.cppの#define SIZE_CHG 1をコメントアウトし、resource.rcの| WS_THICKFRAMEを削除します。
 使い方は、プログラムを起動し読み込みをクリックするとソースが表示されます。

テスト環境

コンパイラ

Visual C++ 2008/2013 Express 32/64bit UNICODE

実行環境

Windows XP Professional Service Pack 3 32bit(Virtual Box上の仮想マシーン)
Windows 7 Enterprise Service Pack 1 64bit

プログラムソースの概要

htmlget2.cpp

WinMain

 CoInitialize APIによりCOMを初期化後、ダイアログボックスを呼び出します。
 ダイアログボックスが終了したらCoUninitialize APIによりCOMを解放します。

DlgProc

WM_INITDIALOG
 ダイアログボックスの初期化時に呼び出されます。
 ダイアログボックスのサイズの変更に備えて各種コモンコントロールの位置や大きさを取得します。
WM_SIZE
 ダイアログボックスの大きさが変更された時に呼び出されます。
 ダイアログボックスの大きさに合わせて、ボタンはダイアログボックスの高さにあわせて移動させます。
 エディットボックスは、ダイアログボックスの大きさに合わせてサイズを変化させます。
WM_COMMAND
IDC_READ_BUTTON
 読み込みボタンがクリックされた時に呼び出されます。
 カーソルを処理中に変更し、htmlRead関数を呼び出しエディットボックスにソースを表示させ、関数から戻ったときにカーソルを元に戻します。
IDCANCEL
 キャンセルボタンがクリックされた時に呼び出されます。
 EndDialog APIによりダイアログボックスを終了させます。

htmlRead

URLを読み込む元になるオブジェクトの作成
 MSHTML::IHTMLDocument4Ptr doc4pをCreateInstanceメンバー関数により初期化します。
 指定したURLを呼び出すcreateDocumentFromUrlメンバー関数は、元になるオブジェクトが必要なため、<html></html>という内容を含んだSAFEARRAY型を作成し、writeメンバー関数によりIHTMLDocument2インターフェースに書き込みcloseメンバー関数により書き込みを終了させます。
URLの読み込み
 元になるオブジェクトを元にcreateDocumentFromUrlメンバー関数を読み出し、MSHTML::IHTMLDocument2Ptr doc2pに保存します。
 createDocumentFromUrlメンバー関数は読み込みが終了する前に呼び出し元に戻るので、MSHTML::IHTMLDocument2Ptr doc2p->readyState != cmpで読み込み終了をチェックしています。  COMをSTAで初期化したので、PeekMessage~DispatchMessageによりメッセージループを設けてあげないとCOMと本プログラム間でメッセージが届かないのでプログラムが正常に動作しません。
Aタグ全部を抽出
 doc2pからMSHTML::IHTMLDocument3Ptr doc3へキャストします。
 MSHTML::getElementsByTagNameによりdoc3からAタグを含むエレメントを全部抽出しMSHTML::IHTMLElementCollectionPtr elementに保存します。
任意のAタグを抽出
 elment->length()によりエレメント数を取得します。
 item(variant_t(i))によりelementからi個目のaタグを取得しMSHTML::IHTMLElementPtr eに保存します。
 ElementPut関数を呼び出しid,name,href等をbufに取得します。
 全部のaタグについてid,name,href等を取得し、エディットボックスに表示します。
 作成された、doc4p,doc2p,elementpを0を代入することにより解放されます。解放しないとエラーが発生して正常に動作しません。
 なお、MSHTMLとは、#importでmshtml.tlbを読み込むときに変更した名前であり、import.hの中で定義しています。ほかの人ではHTMLとかに定義している人もいます。気に入らなければ変更することができます。
 MSHTML::IHTMLDocument2Ptr、MSHTML::IHTMLDocument3Ptr、MSHTML::IHTMLDocument4Ptrはメンバーが異なるのみでキャストすれば自由に使い分けできます。たとえばwriteメンバー関数を呼び出すためにMSHTML::IHTMLDocument4PtrをMSHTML::IHTMLDocument2Ptrにキャストしています。
 ソースに出てくるBSTR型、VARIANT型、SAFEARRAY型はCOMでよく使われるものです。

ElementPut

MSHTML::IHTMLElementPtrよりGetidによりidを取得しBSTR bstrに保存します。
MSHTML::IHTMLElementPtrよりgetAttributeでname,href等を取得し、VARIANT型に保存します。
MSHTML::IHTMLElementPtrよりouterHTMLにより<a href ~ </a>全部を取得します。
MSHTML::IHTMLElementPtrよりinnerHTMLによりaタグに囲まれた中身を取得します。
取得されたid,name,href等をbufに保存します。 使用したBSTR bstr,VARIANT var_name,VARIANT varを開放します。

ソースコード

htmlget2.cpp

// htmlget2.htmlを読み込みAタグを解析してエディットボックスに表示する
// getElementsByTagNameでAタグを抽出

#include <windows.h>
#include <tchar.h>
#include <docobj.h>
#include <commctrl.h>

#define SIZE_CHG 1 //	ダイアログボックスの大きさを変更した場合に各コントロールの大きさ位置を変更させる場合に定義してください

#include "resource.h"
#include "import.h"

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

using namespace std;


//	ダイアログボックスプロシージャー
LRESULT CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);


int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,LPSTR lpsCmdLine, int nCmdShow){
	InitCommonControls();

	if( FAILED( CoInitialize(NULL) )){	//	COMの初期化 STA(シングルスレッドアパートメント)
		MessageBox(0,_TEXT("COMの初期化に失敗しました"),_TEXT("エラー"),MB_OK);
		return -1;
	}
//	ダイアログボックスの表示
	DialogBox(hCurInst, TEXT("DLG1"), 0, (DLGPROC)DlgProc);

	CoUninitialize();	//	COMの解放
	return (int)0;
}

//	Htmlファイルを読み込みエディットコントロールにbodyタグに含まれるソースを張り付ける

void htmlRead(TCHAR* url,HWND hEdit);


//	ダイアログボックスプロシージャー

LRESULT CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){
	static HWND hEdit;
#ifdef SIZE_CHG	//	ダイアログボックスの大きさを変更した場合に各コントロールの大きさ位置を変更させる場合に必要
	static HWND hCANCLE,hBUTTON102;
	static int hEditx,hEdity,hEditWidth,hEditHeight;
	static int hCANCLEx,hCANCLEy,hCANCLEWidth,hCANCLEHeight;
	static int hBUTTON102x,hBUTTON102y,hBUTTON102Width,hBUTTON102Height;
	RECT hDlgrect;
	RECT rect;
	POINT point;
#endif
	static HCURSOR wait_icon=0;
	static HCURSOR org_icon=0;

	switch (msg) {
		case WM_INITDIALOG:{
			wait_icon=LoadCursor(NULL,IDC_WAIT);	//	処理中アイコンの取得

			hEdit=GetDlgItem(hDlg,IDC_EDIT);
#ifdef SIZE_CHG	//	ダイアログボックスの大きさを変更した場合に各コントロールの大きさ位置を変更させる場合に必要
			hCANCLE=GetDlgItem(hDlg,IDCANCEL);
			hBUTTON102=GetDlgItem(hDlg,IDC_READ_BUTTON);
//	ダイアログボックスクライアント領域の大きさを取得
			GetClientRect(hDlg,&hDlgrect);
			int width=hDlgrect.right;
			int height=hDlgrect.bottom;
			GetWindowRect(hDlg,&hDlgrect);

//	エディットボックスのダイアログボックスのクライアント座標・大きさとダイアログボックスの大きさの差を取得
			GetWindowRect(hEdit,&rect);
			point.x=rect.left;
			point.y=rect.top;
			ScreenToClient(hDlg,&point);
			hEditx=point.x;
			hEdity=point.y;
			hEditWidth=(rect.right-rect.left)-width;
			hEditHeight=(rect.bottom-rect.top)-height;
//	ボタンコントロールのイアログボックスのクライアント座標(ダイアログbottomを基準)・大きさを取得
			GetWindowRect(hBUTTON102,&rect);
			point.x=rect.left;
			point.y=rect.top;
			ScreenToClient(hDlg,&point);
			hBUTTON102x=point.x - (width)/2;
			hBUTTON102y=point.y-height;
			hBUTTON102Width=(rect.right-rect.left);
			hBUTTON102Height=(rect.bottom-rect.top);

			GetWindowRect(hCANCLE,&rect);
			point.x=rect.left;
			point.y=rect.top;
			ScreenToClient(hDlg,&point);
			hCANCLEx= point.x -(width)/2;
			hCANCLEy=point.y-height;
			hCANCLEWidth=(rect.right-rect.left);
			hCANCLEHeight=(rect.bottom-rect.top);
#endif
			return TRUE;
		}
#ifdef SIZE_CHG
		case WM_SIZE:{	//	ウィンドウサイズが変更された

			RECT hDlgrect;
			GetWindowRect(hDlg,&hDlgrect);

			int width=LOWORD(lParam);
			int height=HIWORD(lParam);
			MoveWindow(hEdit , hEditx,hEdity, width + hEditWidth, height+hEditHeight,TRUE);
			MoveWindow(hBUTTON102 , width/2+hBUTTON102x,height+hBUTTON102y, hBUTTON102Width, hBUTTON102Height,TRUE);
			MoveWindow(hCANCLE , width/2+hCANCLEx,height+hCANCLEy,hCANCLEWidth, hCANCLEHeight,TRUE);
			return FALSE;
		}
#endif
		case WM_COMMAND:
			switch (LOWORD(wParam)) {
				case IDC_READ_BUTTON:{
					org_icon=GetCursor();
					SetCursor(wait_icon);
					htmlRead(_TEXT("http://yamatyuu.net/computer/program/vc2013/htmlget2//htmlget2.html"),hEdit);
					SetCursor(org_icon);
					return FALSE;
				}
				case IDCANCEL:
					EndDialog(hDlg,TRUE);
					return TRUE;
				default:
					return FALSE;

			}
			default:
				return FALSE;
	}
	return TRUE;
}

//	エレメントを表示

void ElementPut(MSHTML::IHTMLElementPtr e,TCHAR* buf,int sz){
	BSTR bstr;
	e->get_tagName(&bstr);
	TCHAR* tag=bstr;

	bstr=e->Getid();	//	idを取得
	TCHAR* id=_tcsdup(bstr);

	VARIANT var_name=e->getAttribute(_bstr_t(_TEXT("name")),FALSE);	//	nameを取得
	TCHAR* name=_TEXT("");
	if(var_name.vt != VT_NULL && var_name.bstrVal!=(void*)NULL){
		name=var_name.bstrVal;
	}

	VARIANT var=e->getAttribute(_bstr_t(_TEXT("href")),FALSE);	//	href(リンク先)を取得
	TCHAR* href=_TEXT("");
	if(var.vt != VT_NULL && var.bstrVal!=(void*)NULL){
		href=var.bstrVal;
	}
	TCHAR* out=e->outerHTML;	//	<a href ~ </a>全部を取得
	TCHAR* in=e->innerHTML;	//	aタグに囲まれた中身を取得
	_stprintf_s(buf,sz,_TEXT("tag:%s\r\nid:%s\r\nname:%s\r\nhref:%s\r\nouter:%s\r\ninner:%s\r\n"),tag,id,name,href,out,in);
	free(id);
	::SysFreeString(bstr);
	VariantClear(&var_name);
	VariantClear(&var);
}

//	Htmlファイルを読み込みエディットコントロールにbodyタグに含まれるソースを張り付ける

void htmlRead(TCHAR* urls,HWND hEdit){
	MSHTML::IHTMLDocument2Ptr doc2p;
	MSHTML::IHTMLDocument4Ptr doc4p;
	MSHTML::IHTMLElementPtr elementp;
	HRESULT hr;

//	IHTMLDocument4Ptrの初期化
	hr=doc4p.CreateInstance( MSHTML::CLSID_HTMLDocument );
	if(	FAILED(hr) ){
		MessageBox(0,_TEXT("CreateInstance 失敗"),_TEXT("エラー"),MB_OK);	
		return;
	}

//	doc4に最低限のオブジェクトを作成
	VARIANT* param;
	SAFEARRAY* sfArray=NULL;
	BSTR bstr=NULL;
	bstr= ::SysAllocString(OLESTR("<html></html>"));
	if(bstr==NULL){
		return;
	}
	sfArray=::SafeArrayCreateVector(VT_VARIANT,0,1);
	if(sfArray==NULL){
		::SysFreeString(bstr);
		return;
	}
	hr=::SafeArrayAccessData(sfArray,(LPVOID*)&param);
	param->vt=VT_BSTR;
	param->bstrVal=bstr;
	hr=::SafeArrayUnaccessData(sfArray);
	hr=((MSHTML::IHTMLDocument2Ptr)doc4p)->write(sfArray);
	((MSHTML::IHTMLDocument2Ptr)doc4p)->close();

//	doc4を親オブジェクトとしてurlを読み込む。
	BSTR url=::SysAllocString(urls);
	BSTR obj=0;	//	表示対象をプリンターが以外に設定

	_bstr_t cmp=::SysAllocString(_TEXT("complete"));
	doc2p=doc4p->createDocumentFromUrl(url,obj);
	MSG msg;
	unsigned n=0;
	do{	//	COMをSTAで初期化したため、メッセージループを設けないと読み込み完了が検出できない
		if (PeekMessage(&msg, 0, 0 ,0, PM_REMOVE)){
			TranslateMessage (&msg) ;
			DispatchMessage (&msg) ;
		}
		++n;
		Sleep(100);
	}while( doc2p->readyState != cmp );	//	読み込み完了をチェック
	::SysFreeString(url);

	MSHTML::IHTMLElementCollectionPtr	element;
	MSHTML::IHTMLDocument3Ptr doc3p=(MSHTML::IHTMLDocument3Ptr)doc2p;
	element=doc3p->getElementsByTagName(_bstr_t("a"));
	int max=element->length;
	TCHAR buf[256];
	TCHAR edit[4096];
	edit[0]=_T('\0');
	for(int i=0;i<max;++i){
		MSHTML::IHTMLElementPtr e=element->item( variant_t(i) );
		ElementPut(e,buf,sizeof(buf)/sizeof(TCHAR));
		_tcscat_s(edit,sizeof(edit)/sizeof(TCHAR),buf);
	}
	
//	elementp=doc2p->Getbody();	//	bodyタグに含まれるオブジェクトを全部取得
	
	SetWindowText(hEdit,	edit);	//	取得したオブジェクトのソースをエディットボックスに張り付ける
	::SysFreeString(bstr);	//	bstrの解放
	::SafeArrayDestroy(sfArray);	//	SAFEARRAYの解放

//	作成されたオブジェクトの解放
	elementp=0;
	doc2p=0;
	doc4p=0;
}

import.h

#pragma warning(disable:4192)
#pragma warning(disable:4278)

#import <shdocvw.dll> rename_namespace("SHDocVw") named_guids
#import <mshtml.tlb> rename_namespace("MSHTML") named_guids

#pragma warning(default:4192)
#pragma warning(default:4278)

resource.h

#define	IDC_EDIT		100
#define	IDC_READ_BUTTON		102

resource.rc

#include <windows.h>
#include "resource.h"


DLG1 DIALOG DISCARDABLE 0, 0, 414, 366
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP| WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_SETFONT | WS_THICKFRAME

CAPTION "getElementsByTagName使用例(Aタグ抽出)"
FONT 9, "MS 明朝"
{
 CONTROL "ソース", -11, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 7, 7, 54, 12
 CONTROL "読み込まれていません。", IDC_EDIT, "EDIT", WS_CHILD | WS_DLGFRAME | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL
                                  | ES_AUTOHSCROLL  | ES_AUTOVSCROLL | ES_MULTILINE | ES_READONLY, 7, 20, 400, 300
 CONTROL "読み込み(&R)", IDC_READ_BUTTON, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 146, 334, 54, 18
 CONTROL "キャンセル(&C)", IDCANCEL, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 214, 334, 54, 18
}

実行ファイルとソースファイルのダウンロード