seraphyの日記

日記というよりは過去を振り返るときのための単なる備忘録

MSHTMLを使ってHTMLを解析してみる*3

URLを指定してHTMLを読み込みドキュメントツリーを見るテスト

#include "stdafx.h"
#include <mshtml.h>

class CMSHTMLParserTest
    : public CAtlExeModuleT<CMSHTMLParserTest>
{
public:
    HRESULT PreMessageLoop(int nShowCmd) throw()
    {
        HRESULT hr = CAtlExeModuleT<CMSHTMLParserTest>::PreMessageLoop(nShowCmd);
        if (FAILED(hr)) {
            return hr;
        }

        hr = pHTMLDocument_.CoCreateInstance(__uuidof(HTMLDocument));
        ATLASSERT(SUCCEEDED(hr));
        return hr;
    }

    void RunMessageLoop( ) throw()
    {
        HRESULT hr;

        CComPtr<IMoniker> pMoniker;
        hr = CreateURLMoniker(NULL,
            L"http://www.microsoft.com/japan/msdn/",
            &pMoniker);
        ATLASSERT(SUCCEEDED(hr));

        CComPtr<IBindCtx> pBindCtx;
        hr = CreateBindCtx(0, &pBindCtx);
        ATLASSERT(SUCCEEDED(hr));

        CComQIPtr<IPersistMoniker> pPersistMoniker(pHTMLDocument_);
        hr = pPersistMoniker->Load(FALSE, pMoniker, pBindCtx, STGM_READ);
        ATLASSERT(SUCCEEDED(hr));

        hr = WaitForComplite();
        ATLASSERT(SUCCEEDED(hr));

        CComQIPtr<IHTMLDocument3> pDoc(pHTMLDocument_);

        CComPtr<IHTMLElement> pDocElement;
        hr = pDoc->get_documentElement(&pDocElement);
        ATLASSERT(SUCCEEDED(hr));

        hr = WalkTree(pDocElement);
        ATLASSERT(SUCCEEDED(hr));
    }

private:
    HRESULT WalkTree(IHTMLElement* pElm) throw()
    {
        HRESULT hr;
        try {
            CComBSTR tagName;
            hr = pElm->get_tagName(&tagName);
            ATLASSERT(SUCCEEDED(hr));

            OutputDebugStringW(tagName);
            OutputDebugStringW(L"\n");

            CComPtr<IDispatch> pChildrenDisp;
            hr = pElm->get_children(&pChildrenDisp);
            ATLASSERT(SUCCEEDED(hr));
            CComQIPtr<IHTMLElementCollection> pChildren(pChildrenDisp);

            long cnt;
            hr = pChildren->get_length(&cnt);
            ATLASSERT(SUCCEEDED(hr));

            for (long idx = 0; idx < cnt; idx++) {
                CComVariant name(idx);
                CComVariant dmy;
                CComPtr<IDispatch> pChildDisp;
                hr = pChildren->item(name, dmy, &pChildDisp);
                ATLASSERT(SUCCEEDED(hr));
                CComQIPtr<IHTMLElement> pChild(pChildDisp);
                hr = WalkTree(pChild);
                if (FAILED(hr)) {
                    break;
                }
            }
        }
        catch (...) {
            return E_FAIL;
        }
        return hr;
    }

    HRESULT WaitForComplite() throw()
    {
        try {
            HRESULT hr = E_FAIL;
            int loop = 0;
            for (;;) {
                CComBSTR state;
                hr = pHTMLDocument_->get_readyState(&state);
                ATLASSERT(SUCCEEDED(hr));
                if (FAILED(hr)) {
                    break;
                }
                OutputDebugStringW(state);
                OutputDebugStringW(L"\n");

                if (state != L"uninitialized" && state != L"loading") {
                    hr = S_OK;
                }
                else {
                    if (loop < 1000) {
                        Sleep(50);
                        continue;
                    }
                }
                break;
            }
            return hr;
        }
        catch (...) {
            return E_FAIL;
        }
    }

private:
    CComPtr<IHTMLDocument2> pHTMLDocument_;
};

CMSHTMLParserTest _atlModule;

int _tmain(int argc, _TCHAR* argv[])
{
    return _atlModule.WinMain(SW_SHOWNORMAL);
}

所感

上記コードに自信はないが、とりあえず、動いた。その程度のレベル。

モニカの仕組みが分かっていないのだが、CreateURLMonikerでURLを指定することはできても、CreateFileMonikerでファイルを指定しても正しく読んでくれなかったのだ。
どうやら、IPersistMonikerではなく、IPersistFileを使ってロードしなければダメらしい。
なんで、そんなことになっているのかはわからない。

また非同期的にHTMLを読み込んでくれるので、完了したかどうかをチェックしなければならないのは、単純なシングルスレッドなバッチツールのような用途では、めんどくさいような気がする。(もちろん、コールバックを登録できるようであるが。)
それに、ステートを文字列で比較する以外に方法はないのだろうか?

しかし、この方法を使えばWebBrowserコントロールなどを使わずとも、GUIなしでHTMLを読ませて解析することができるので、覚えておくと重宝するのかもしれない。

使い方は奥が深いというか、調べないと分からないことは多すぎる気がする。