MSHTMLを使用してクラス名d2のdivタグの中身を取得(32/64bit)

icon 項目のみ表示/展開表示の切り替え

概要

 MSHTMLを使用してクラス名がd2の<div>タグに含まれるタグを取得しエディットボックスに表示します。
 MSHTMLとは、インターネットエクスプローラーのhtmlファイルを解析するためのモジュールです。
 ここでは、http://yamatyuu.net/computer/program/vc2013/htmlget5/htmlget5.htmlを呼び出しています。
上記には2つのdivタグがありますが、本プログラムではクラス名d2の方のみ抽出してます。
 ダイアログボックスはマウスによるサイズの変更に対応しています。
 マウスによるサイズ変更をする必要がない場合は、htmlget5.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

プログラムソースの概要

htmlget5.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::IHTMLDocument4Pt 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と本プログラム間でメッセージが届かないのでプログラムが正常に動作しません。
タグを取得
 doc2pからMSHTML::IHTMLDocument3Ptr doc3へキャストします。
MSHTML::IHTMLDocument3Ptr getElementByTagNameによりdivタグを取得します。 doc3からタグ名がd2のエレメントを取得しMSHTML::IHTMLElementCollectionPtrに保存します。
divのうちクラス名d2のdivを抽出
divタグの個数をMSHTML::IHTMLElementCollectionPtr->lengthにより取得します。
MSHTML::IHTMLElementCollectionPtr->itemにより複数のdivタグの中から指定されたdivタグを取得しMSHTML::IHTMLElementPtrに保存します。
MSHTML::IHTMLElementPtr->getAttributeによりdivの属性のうちクラス名を取得しVARIANT型に保存します。クラス名を取得するときは、属性名がClassではなくClassNameと指定しないと取得できませんので注意が必要です。
 VARIANT型から文字列を抽出し文字列d2と比較し一致すればdivタグのうちクラス名がd2が取得できたことになります。
divに含まれるタグの取得
 MSHTML::IHTMLElementPtr->QueryInterfaceにより MSHTML::IHTMLDOMNodePtrインターフェースを取得しnodepに保存します。
MSHTML::IHTMLDOMNodePtrのメンバー変数childNodesに子エレメントが含まれているのでMSHTML::IHTMLDOMChildrenCollectionPtr collpに保存します。
MSHTML::IHTMLDOMChildrenCollectionPtr->lengthにより子エレメントの個数を取得します。
MSHTML::IHTMLDOMNodePtr->itemにより任意の子エレメントを取得しMSHTML::IHTMLDOMNodePtrに保存します。
MSHTML::IHTMLDOMNodePtrをMSHTML::IHTMLElementPtrへキャストします。
 ElementPut関数を呼び出しタグをbufに取得します。
 bufをエディットボックスに表示します。
 作成された、doc4p,doc3p,doc2p,elementp,e,nodepに0を代入することにより解放されます。解放しないとエラーが発生して正常に動作しません。
 なお、MSHTMLとは、#importでmshtml.tlbを読み込むときに変更した名前であり、import.hの中で定義しています。ほかの人ではHTMLとかに定義している人もいます。気に入らなければ変更することができます。
MSHTML::IHTMLDocument2Ptr、MSHTML::IHTMLDocument3Ptr、MSHTML::IHTMLDocument4Ptrはメンバーが異なるのみでキャストすれば自由に使い分けできます。たとえばwriteメンバー関数を呼び出すためにMSHTML::IHTMLDocument4PtrをMSHTML::IHTMLDocument2Ptrにキャストしています。
 ソースに出てくるBSTR型、VARIANT型、SAFEARRAY型はCOMでよく使われるものです。

ElementPut

1タグごとに呼び出されます。
MSHTML::IHTMLElementPtr->outerHTMLによりタグを取得しbufにコピーをとります。
bufに復帰と改行を追加して戻ります。

ソースコード

htmlget5.cpp


// htmlget5.htmlを読み込み<div>に含まれるタグを解析してエディットボックスに表示する
// getElementByTagNameでdivタグを検出しクラス名がd2の場合、QueryInterfaceを使用して子ノードのコレクションを抽出しその中から順番に1個ずつ取り出し表示する

#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ファイルを読み込み<div class=""d2>タグの中に含まれるタグを表示

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/htmlget5/htmlget5.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){
        TCHAR* out=e->outerHTML;
        _tcscpy_s(buf,sz,out);
        _tcscat_s(buf,sz,_TEXT("\r\n"));
}

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

void htmlRead(TCHAR* urls,HWND hEdit){
        MSHTML::IHTMLDocument2Ptr doc2p;
        MSHTML::IHTMLDocument4Ptr doc4p;
        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);
        ::SysFreeString(cmp);

        MSHTML::IHTMLDocument3Ptr doc3p=(MSHTML::IHTMLDocument3Ptr)doc2p;

        BSTR bstrId = SysAllocString(_TEXT("div"));
        MSHTML::IHTMLElementCollectionPtr       elementp;
        elementp=doc3p->getElementsByTagName(bstrId);
        if(elementp == NULL){
                MessageBox(0,bstrId,_TEXT("下記のTagが見つかりませんでした。"),MB_OK);
                ::SysFreeString(bstrId);
                return;
        }
        
        int max=elementp->length;
        TCHAR buf[256];
        TCHAR edit[4096];
        edit[0]=_T('\0');
        for(int i=0;i<max;++i){
                MSHTML::IHTMLElementPtr e=elementp->item( variant_t(i) );
                VARIANT var=e->getAttribute(_bstr_t(_TEXT("className")),FALSE);
                TCHAR* c=_TEXT("");
                if(var.vt != VT_NULL && var.bstrVal!=(void*)NULL){
                        c=var.bstrVal;
                }
                MSHTML::IHTMLDOMNodePtr nodep;
                MSHTML::IHTMLDOMChildrenCollectionPtr collp;
                int max2;
                if(_tcscmp(c,_TEXT("d2"))==0){
                        e->QueryInterface(IID_PPV_ARGS(&nodep));
                        collp=(MSHTML::IHTMLDOMChildrenCollectionPtr)nodep->childNodes;
                        max2=collp->length;
                        MSHTML::IHTMLDOMNodePtr cnodep;
                        for(int n=0;n<max2;n++){
                                cnodep=(MSHTML::IHTMLDOMNodePtr)collp->item(n);
                                MSHTML::IHTMLElementPtr celementp = (MSHTML::IHTMLElementPtr)cnodep;
                                ElementPut(celementp,buf,sizeof(buf)/sizeof(TCHAR));
                                _tcscat_s(edit,sizeof(edit)/sizeof(TCHAR),buf);
                        }
                        nodep=0;
                }
                e = 0;
        }

        SetWindowText(hEdit, edit);     //      取得したオブジェクトのソースをエディットボックスに張り付ける
        
        ::SysFreeString(bstr);  //      bstrの解放
        ::SafeArrayDestroy(sfArray);    //      SAFEARRAYの解放

//      作成されたオブジェクトの解放
        elementp=0;
        doc3p=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 "htmlget5"
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
}

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