MSHTMLを使用してbodyタグ内のソースを得る(32/64bit)

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

概要

 MSHTMLを使用してbodyタグ内のソースを取得しエディットボックスに表示します。
 MSHTMLとは、インターネットエクスプローラーのhtmlファイルを解析するためのモジュールです。
 ここでは、googleのトップページ(http://www.google.co.jp/)を呼び出しています。
 ダイアログボックスはマウスによるサイズの変更に対応しています。
 マウスによるサイズ変更をする必要がない場合は、htmlget1.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

プログラムソースの概要

htmlget1.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からGetbodyメンバー関数によりbodyタグ内を取得し、MSHTML::IHTMLElementPtr elementpに保存します。
 GetouterHTMLメンバー関数を実行するとbodyタグ内のソースの文字列が取得できるのでエディットボックスに書き込みします。
 作成された、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でよく使われるものです。

ソースコード

htmlget1.cpp

//      googleのホープページを読み込みエディットボックスにbodyタグ内のソースを表示する。

#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);
//      Htmlファイルを読み込みエディットコントロールにbodyタグに含まれるソースを張り付ける
void htmlRead(TCHAR* url,HWND hEdit);


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;
}


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

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://www.google.co.jp/"),hEdit);
                                        SetCursor(org_icon);
                                        return FALSE;
                                }
                                case IDCANCEL:
                                        EndDialog(hDlg,TRUE);
                                        return TRUE;
                                default:
                                        return FALSE;

                        }
                        default:
                                return FALSE;
        }
        return TRUE;
}

//      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("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);

        elementp=doc2p->Getbody();   //      bodyタグに含まれるオブジェクトを全部取得

        SetWindowText(hEdit, elementp->GetouterHTML());      //      取得したオブジェクトのソースをエディットボックスに張り付ける
        ::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 "HTMLGet1"
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
}

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