Webアプリケーション+αの使い勝手を実現するカスタムブラウザの作り方【別表】


アリエルネットワークス
中山淳
2006/12/7

【コード1】

  #define AllocStdCallThunk() HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sizeof(_stdcallthunk))
  #define FreeStdCallThunk(p) HeapFree(GetProcessHeap(), 0, p)

【コード2】
#include <atlbase.h>
extern CComModule _Module;  // ATLを使うためのお呪い
#include <atlcom.h>
#include <atlhost.h>
#include <exdispid.H>
#include <exdisp.h>
#include <mshtmcid.h>
#include <mshtml.h>

#include <windows.h>
#include <tchar.h>
#include <assert.h>

// ATLを使うためのお呪い
CComModule _Module;
BEGIN_OBJECT_MAP(ObjectMap)
END_OBJECT_MAP()

// カスタムブラウザアプリケーション用ウィンドウクラス
class AtBrowserClass {
    static const TCHAR WINDOW_CLASS_NAME[];

    AtBrowserClass(const AtBrowserClass&);
    AtBrowserClass& operator=(const AtBrowserClass&);
public:
    AtBrowserClass(LRESULT (CALLBACK* windowProcedure)(HWND, UINT, WPARAM, LPARAM));
    HWND createWindow(LPCTSTR windowName, DWORD style,
                    int x, int y, int width, int height,
                    HWND parentWinodw, HMENU menu, HINSTANCE instance, LPVOID param) const;
};

// ウィンドウクラスの名前
const TCHAR AtBrowserClass::WINDOW_CLASS_NAME[] = _T("AtBrowser");

// AtBrowserClassのコンストラクタ
AtBrowserClass::AtBrowserClass(LRESULT (CALLBACK* windowProcedure)(HWND, UINT, WPARAM, LPARAM))
{
    // ウィンドウクラスを登録する
    WNDCLASSEX windowClass = {
        sizeof(WNDCLASSEX),
        CS_HREDRAW | CS_VREDRAW,
        windowProcedure,
        0,
        0,
        GetModuleHandle(NULL),
        LoadIcon(NULL, IDI_APPLICATION),
        LoadCursor(NULL, IDC_ARROW),
        reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)),
        NULL,
        WINDOW_CLASS_NAME,
        LoadIcon(NULL, IDI_APPLICATION),
    };
    RegisterClassEx(&windowClass);
}

HWND AtBrowserClass::createWindow(LPCTSTR windowName, DWORD style,
                                int x, int y, int width, int height,
                                HWND parentWinodw, HMENU menu,
                                HINSTANCE instance, LPVOID param) const
{
    // ウィンドウクラスに属するウィンドウを作成する
    return CreateWindow(WINDOW_CLASS_NAME, windowName, style,
                        x, y, width, height,
                        parentWinodw, menu, instance, param);
}


// カスタムブラウザ クラス
#define SINKID_ATBROWSER 0
class ATL_NO_VTABLE AtBrowser : public CComObjectRootEx<CComSingleThreadModel>
                              , public IDispEventImpl<SINKID_ATBROWSER, AtBrowser, &DIID_DWebBrowserEvents2> {
    BEGIN_COM_MAP(AtBrowser)
        COM_INTERFACE_ENTRY_IID(DIID_DWebBrowserEvents2, AtBrowser)
    END_COM_MAP()

    BEGIN_SINK_MAP(AtBrowser)
    END_SINK_MAP()

    HWND appWindow_;                    // アプリケーションウィンドウ
    HWND ieWindow_;                     // ブラウザコンポーネントのコンテナウィンドウ
    CComQIPtr<IWebBrowser2> browser_;   // IEコンポーネント

    AtBrowser(const AtBrowser&);
    AtBrowser& operator=(const AtBrowser&);
public:
    AtBrowser();
    bool show(int commandShow);
    int messageLoop();
private:
    LRESULT onKeyDown(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
    LRESULT onDestroy(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

    static LRESULT CALLBACK windowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
    static const AtBrowserClass AT_BROWSER_CLASS;
};

// カスタムブラウザアプリケーション用ウィンドウクラスのインスタンスを生成する
const AtBrowserClass AtBrowser::AT_BROWSER_CLASS(&AtBrowser::windowProcedure);

// AtBrowserのコンストラクタ
AtBrowser::AtBrowser()
: appWindow_(NULL)
, ieWindow_(NULL)
, browser_()
{
    // AtBrowserClassのインスタンスとして、アプリケーションウィンドウを作成する
    // ウィンドウプロシージャはAtBrowser::windowProcedureが使われる
    appWindow_ = AT_BROWSER_CLASS.createWindow(_T("AtBrowser"), WS_OVERLAPPEDWINDOW,
                                            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
                                            NULL, NULL, GetModuleHandle(NULL), NULL);

    // AtWindowオブジェクトへの参照をウィンドウの属性として設定する (see AtBrowser::windowProcedure)
    SetWindowLong(appWindow_, GWL_USERDATA, reinterpret_cast<LONG>(this));

    // IEコンポーネントのコンテナとしてウィンドウを作成する
    ieWindow_ = CreateWindow(_T("AtlAxWin"), _T("Shell.Explorer.2"), WS_CHILD | WS_VISIBLE | WS_MAXIMIZE,
                            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
                            appWindow_, NULL, GetModuleHandle(NULL), NULL);

    // IEコンポーネントのインスタンスを取得して、コンテナに関連づける
    CComPtr<IUnknown> unkown;
    if (AtlAxGetControl(ieWindow_, &unkown) == S_OK) {
        browser_ = unkown;
    }
    if (browser_ == NULL) {
        // 失敗したらメッセージループを抜ける (アプリケーションを終了する)
        PostQuitMessage(0);
    }
    UpdateWindow(appWindow_);
}

// アプリケーションウィンドウを表示する
bool AtBrowser::show(int commandShow)
{
    AtlGetObjectSourceInterface(browser_, &m_libid, &m_iid, &m_wMajorVerNum, &m_wMinorVerNum);
    DispEventAdvise(browser_);

    browser_->GoHome();
    return ShowWindow(appWindow_, commandShow) != FALSE;
}

int AtBrowser::messageLoop()
{
    // Win32アプリケーションのお約束として、メッセージループを回す
    MSG msg;
    BOOL isValidMessage;
    while ((isValidMessage = GetMessage(&msg, NULL, 0, 0)) != 0 && isValidMessage != -1) {
        if (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST) {
            TCHAR className[256];
            GetClassName(msg.hwnd, className, (sizeof className) - 1);
            if (_tcscmp(className, _T("Internet Explorer_Server")) == 0) {
                SendMessage(appWindow_, msg.message, msg.wParam, msg.lParam);
            }
        }
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

// AtWindowアプリケーションのウィンドウプロシージャ
LRESULT CALLBACK AtBrowser::windowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    // AtWindowオブジェクトへの参照をウィンドウの属性として取得する (see AtBrowser constructor)
    AtBrowser* atBrowser = reinterpret_cast<AtBrowser*>(GetWindowLong(hwnd, GWL_USERDATA));

    // ウィンドウメッセージに応じてディスパッチする
    switch (message) {
    case WM_KEYDOWN:
        assert(atBrowser != NULL);
        return atBrowser->onKeyDown(hwnd, message, wParam, lParam);
    case WM_DESTROY :
        assert(atBrowser != NULL);
        return atBrowser->onDestroy(hwnd, message, wParam, lParam);
    }

    // ほとんどのメッセージはデフォルト処理に任せる
    return DefWindowProc(hwnd, message, wParam, lParam);
}

// WM_KEYDOWNメッセージのハンドラ
LRESULT AtBrowser::onKeyDown(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    CComQIPtr<IOleInPlaceActiveObject, &IID_IOleInPlaceActiveObject> inPlace(browser_);
    if (inPlace != NULL) {
        MSG msg = {hwnd, message, wParam, lParam};
        inPlace->TranslateAccelerator(&msg);
    }
    return 0;
}

// WM_DESTROYメッセージのハンドラ
LRESULT AtBrowser::onDestroy(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    // アプリケーションウィンドウが閉じられたら、ブラウザオブジェクトを破棄してメッセージループを抜ける
    browser_.Release();
    PostQuitMessage(0);
    return 0 ;
}

// エントリポイント / ここから実行が開始される
int APIENTRY _tWinMain(HINSTANCE instance, HINSTANCE previousInstance,
                       LPTSTR commandLine, int commandShow)
{
    // ATLを使うためのお呪い
    _Module.Init(ObjectMap, instance);
    AtlAxWinInit();

    // カスタムブラウザを実体化する
    CComObject<AtBrowser>* atBrowser;
    CComObject<AtBrowser>::CreateInstance(&atBrowser);
    atBrowser->show(commandShow);
    return atBrowser->messageLoop();
}

【コード3】
    BEGIN_SINK_MAP(AtBrowser)
        SINK_ENTRY_EX(SINKID_ATBROWSER, DIID_DWebBrowserEvents2, DISPID_DOWNLOADCOMPLETE, &AtBrowser::onDownloadComplete)
    END_SINK_MAP()

    void _stdcall onDownloadComplete();

 

【コード4】
void _stdcall AtBrowser::onDownloadComplete()
{
    BSTR pbstrLocationName;
    if (browser_ != NULL && browser_->get_LocationName(&pbstrLocationName) == S_OK) {
        ::SetWindowText(appWindow_, pbstrLocationName);
        ::SysFreeString(pbstrLocationName);
    }
}

【コード5】
    BEGIN_SINK_MAP(AtBrowser)
        SINK_ENTRY_EX(SINKID_ATBROWSER, DIID_DWebBrowserEvents2, DISPID_DOWNLOADCOMPLETE, &AtBrowser::onDownloadComplete)
    END_SINK_MAP()

    void _stdcall onDownloadComplete();

【コード6】
    BEGIN_SINK_MAP(AtBrowser)
        SINK_ENTRY_EX(SINKID_ATBROWSER, DIID_DWebBrowserEvents2, DISPID_DOWNLOADCOMPLETE, &AtBrowser::onDownloadComplete)
        SINK_ENTRY_EX(SINKID_ATBROWSER, DIID_DWebBrowserEvents2, DISPID_BEFORENAVIGATE2, &AtBrowser::onBeforeNavigate2)
    END_SINK_MAP()

    void _stdcall onDownloadComplete();
    void _stdcall onBeforeNavigate2(IDispatch** dispatch, VARIANT* url, VARIANT* flags,
            VARIANT* frameName, VARIANT* postData, VARIANT* headers, VARIANT_BOOL* cancel);

【コード7】
void _stdcall AtBrowser::onBeforeNavigate2(IDispatch** dispatch, VARIANT* url, VARIANT* flags, VARIANT* frameName, VARIANT* postData, VARIANT* headers, VARIANT_BOOL* cancel)
{
    static const TCHAR SERACH_WORD[] = _T("/search?");
    const TCHAR* p;
    if (_tcsstr(url->bstrVal, _T(".google.")) != NULL && (p = _tcsstr(url->bstrVal, SERACH_WORD)) != NULL) {
        p += _tcslen(SERACH_WORD);
        while (p != NULL && *p != _T('\0')) {
            static const TCHAR SPLIT_WORD[] = _T("q=");
            if (_tcsstr(p, SPLIT_WORD) == p) {
                p += _tcslen(SPLIT_WORD);
                const TCHAR* end = _tcschr(p, _T('&'));
                end = end == NULL ? p + _tcslen(p) : end;
                TCHAR* words = static_cast<TCHAR*>(calloc(end - p + 1, sizeof(TCHAR)));
                memcpy(words, p, sizeof(TCHAR) * (end - p));
                FILE* file = _tfopen(_T("keywords-history.txt"), _T("a"));
                    SYSTEMTIME st;
                GetLocalTime(&st);
                _ftprintf(file, _T("%04d/%02d/%02d %02d:%02d:%02d\t"),
                    st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
                for (const TCHAR* pc = words; *pc != _T('\0'); pc++) {
                    if (*pc == _T('%') && pc + 2 < words + (end - p)) {
                        TCHAR ch[2 + 1] = {
                            pc[1], pc[2], _T('\0')
                        };
                        pc += 2;
                        TCHAR* endp;
                        _ftprintf(file, _T("%c"), _tcstoul(ch, &endp, 16));
                    } else if (*pc == _T('+')) {
                        _fputtc(_T(' '), file);
                    } else {
                        _fputtc(*pc, file);
                    }
                }
                _fputtc(_T('\t'), file);
                _fputts(url->bstrVal, file);
                _fputtc(_T('\n'), file);
                fclose(file);
                free(words);
            } else {
                p = _tcschr(p, _T('&'));
                p =  p != NULL ? p + 1 : NULL;
            }
        }
    }
}

 



HTML5 + UX フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

HTML5+UX 記事ランキング

本日 月間