MSHTMLを使用して特定のidのtabelを取得(32/64bit)

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

概要

 MSHTMLを使用してidがt1のtableを取得しセルの内容をエディットボックスに表示します。
 MSHTMLとは、インターネットエクスプローラーのhtmlファイルを解析するためのモジュールです。
 ここでは、http://yamatyuu.net/computer/program/vc2013/htmlget4/htmlget4.htmlを呼び出しています。
 ダイアログボックスはマウスによるサイズの変更に対応しています。
 マウスによるサイズ変更をする必要がない場合は、htmlget4.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

プログラムソースの概要

htmlget4.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と本プログラム間でメッセージが届かないのでプログラムが正常に動作しません。
IDからタグを取得
 doc2pからMSHTML::IHTMLDocument3Ptr doc3へキャストします。
MSHTML::IHTMLDocument3Ptr getElementByIdによりdoc3からid名がid5のエレメントを取得しHTML::IHTMLElementPtr elementに保存します。
tableの取得
 IHTMLDocument3Ptr elementp->QueryInterfaceにより MSHTML::IHTMLTablePtrインターフェースを取得しtablep保存します。  ElementPut関数を呼び出しname,href等をbufに取得します。
 bufをエディットボックスに表示します。
 作成された、doc4p,doc3p,doc2p,elementp,tablepを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::IHTMLTablePtrのget_rowsメンバーにより全部の行を取得しMSHTML::IHTMLElementCollectionPtrに保存します。
 MSHTML::IHTMLElementCollectionPtrのget_lengthメンバにより行数を取得します。
各行の取得
 IDispatchPtrのコンストラクタにより全部の行から任意の行を取得します。
 IDispatchPtrのメンバQueryInterfaceによりMSHTML::IHTMLTableRowPtrを取得します。
 MSHTML::IHTMLTableRowPtrのget_cellsにより行に含まれる全部のセルを取得しMSHTML::IHTMLElementCollectionPtrに保存します。
 MSHTML::IHTMLElementCollectionPtrのget_lengthにより列数を取得します。
セルの取得
 IDispatchPtrのコンストラクタにより全部の列から任意の列を取得します。
 IDispatchPtrのメンバQueryInterfaceによりMSHTML::IHTMLElementPtrを取得します。
 MSHTML::IHTMLElementPtrのget_innerTextによりセルの文字列を取得します。  取得した文字列はbufに書式化して保存します。
 各属性を保存するために動的に割り付けたメモリをfreeで開放します。

ソースコード

htmlget4.cpp

// htmlget4.htmlを読み込み特定のIDのtableタグを解析してエディットボックスに表示する
// getElementByIdで特定のIDのエレメントを抽出、QueryInterfaceを使用してMSHTML::IHTMLTablePtrに変換

#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/htmlget4/htmlget4.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::IHTMLTablePtr e,TCHAR* buf,int sz){
        TCHAR t[256];
        MSHTML::IHTMLElementCollectionPtr pRowCollection;
        LONG row_max,cram_max;
        e->get_rows(&pRowCollection);
        pRowCollection->get_length(&row_max);    //      行数
        buf[0]=_T('\0');
        for(int r=0;r<row_max;r++){

                MSHTML::IHTMLTableRowPtr        prows;
                MSHTML::IHTMLElementCollectionPtr       pcrams;
                IDispatchPtr    pDispatch(pRowCollection->item(_variant_t(r)));
                pDispatch->QueryInterface(IID_PPV_ARGS(&prows));
                pDispatch=0;

                prows->get_cells(&pcrams);
                pcrams->get_length(&cram_max);
                prows=0;

                for(int c=0;c<cram_max;c++){
                        BSTR bstrCell;
                        MSHTML::IHTMLElementPtr  elementp;
                        IDispatchPtr     pDispatch(pcrams->item(_variant_t(c) , _variant_t()) );
                        pDispatch->QueryInterface(IID_PPV_ARGS(&elementp));
                        pDispatch=0;
                        elementp->get_innerText(&bstrCell);
                        if(c==cram_max-1)
                                _stprintf_s(t,sizeof(t)/sizeof(TCHAR),_TEXT("%s\r\n"),bstrCell);
                        else
                                _stprintf_s(t,sizeof(t)/sizeof(TCHAR),_TEXT("%s,"),bstrCell);
                        _tcscat_s(buf,sz,t);
                        ::SysFreeString(bstrCell);
                        elementp=0;
                }

                pcrams=0;
        }


        pRowCollection=0;
//      MessageBox(0,_TEXT(""),_TEXT(""),MB_OK);
}

//      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);
        ::SysFreeString(cmp);

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

        BSTR bstrId = SysAllocString(_TEXT("t1"));
        elementp=doc3p->getElementById(bstrId );
        if(elementp == NULL){
                MessageBox(0,bstrId,_TEXT("下記のIDが見つかりませんでした。"),MB_OK);
                ::SysFreeString(bstrId);
                return;
        }
        MSHTML::IHTMLTablePtr tablep;
        elementp->QueryInterface(IID_PPV_ARGS(&tablep));

        TCHAR buf[2048];
        ::SysFreeString(bstrId);
        ElementPut(tablep,buf,sizeof(buf)/sizeof(TCHAR));

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

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

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