Browser Helper Objects を作ってみる

Browser Helper Objects (BHO) ってどうやって作るの?っていう相談を受けたのでネタにします。
今更 COM かよ!っていう感じもしますが、COM は今でも重要なテクノロジなんですね。
まず、MSDN だと、ここに情報があります。

この内容に沿って作成すると簡単なサンプルができます。
一緒に作成してみましょう!(ほとんど同じですが、VS 2008 を使ってみます。)

1) プロジェクトのセットアップ

ここで、作成するのは DLL になります。追加のオプションは必要ありません。

BHO作成1

BHO作成2BHO作成3

BHO は COM クラスになります。IE がホストになって、BHO の COM クラスをホスティングするわけです。
ATL シンプル オブジェクトを選択して、COM クラスを作成します。
ここでのポイントは、IObjectWithSite を選択しておくことです。

BHO作成4

BHO作成5BHO作成6

すると、VS はいろいろなファイルをジェネレートしてくれます。
なお、IObjectWithSite インターフェースについては、この辺をどうぞ。

2) 基本の実装

ここでは、記載されている通りに進めてください。

HelloWorldBHO.h (記事の通りに)
HelloWorldBHO.cpp (記事の通りに)

ATL で作っているので、普通に CComPtr クラスを使っていますね。スマートポインタは偉大です。

なお、HelloWorld.cpp で DllMain にコードを追加していますが、
VS 2008 では、DllMain は dllmain.cpp という別のファイルに生成されますので注意してください。

3) BHO の登録

BHO は COM であるため、レジストリ登録が必要になります。
ここでのポイントは、Windows Explorer から BHO がロードされないようにしていることです。
BHO はそもそも、Internet Explorer と Windows Explorer に対する機能拡張になりますので
IE だけで使おうとなると、少し細工が必要になるわけです。
(*) Windows 2000 で Internet Explorer と Windows Explorer が統合された結果です

ForceRemove で記載している CLSID を間違えないようにしてください。
サイトに記載されている uuid をコピペして、自分で作成している uuid と置き換えないように。

一応、これでビルドができるようになったのですが、IE 動かしてみても何も起こりません。
先に進めてください。

4) イベントへの応答

イベントを処理するには、Invoke メソッドを使います。Invoke メソッドを実際に書いてもいいのですが
今どきの ATL では、BEGIN_SINK_MAP マクロという便利なものがあるんです。
BEGIN_SINK_MAP マクロで DISPID_DOCUMENTCOMPLETE を登録して、OnDocumentComplete メソッドで
実装しています。

ちなみに、BEGIN_SINK_MAP マクロを使わないと

STDMETHODIMP CHelloWorldBHO::Invoke(DISPID dispidMember,
                                    REFIID riid, LCID lcid, WORD wFlags,
                                    DISPPARAMS* pDispParams, VARIANT* pvarResult,
                                    EXCEPINFO*  pExcepInfo,  UINT* puArgErr)
{
    if (!pDispParams)
    {
        return E_INVALIDARG;
    }
    switch (dispidMember)
    {
    case DISPID_DOCUMENTCOMPLETE:
        // ここに処理を書く
        break;
    case DISPID_BEFORENAVIGATE2:
        // ここに処理を書く
        break;

・・・・
}

という巨大な switch 文を作成しなければなりません。使えるマクロは、どんどん使った方がよさそうです。

さあ、これで目で見えるような動きになった BHO ができました。
ビルドして IE を起動してみましょう。(テストの再実行 というところです)

BHO作成7

こんな感じで記事と同じようにできました。
時間があったら、その後に続く記事の内容もやってみるといいと思います。

IE が発生させるイベントは、ここに記載されています。

記事で使っている DISPID_DOCUMENTCOMPLETE というイベントは

というもので、HTMLドキュメントのロードが終了したときに発生するイベントです。
Event DISPID に「DISPID_DOCUMENTCOMPLETE」という記述があります。
この DISPID に対して Invoke メソッドを使い、処理するイベントを実装するというわけです。

なお、余談ですが、C# で書くと、こんな感じになります。

using SHDocVw;
using mshtml;
using System.Runtime.InteropServices;

namespace BHO_HelloWorld
{
    public class WindowWrapper : IWin32Window
    {
        private IntPtr hWnd;
        public WindowWrapper(IntPtr handle)
        {
            hWnd = handle;
        }
        public IntPtr Handle
        {
            get { return hWnd; }
        }
    }

class BHO : IObjectWithSite
    {
        WebBrowser webBrowser;

public void OnDocumentComplete(object pDisp, ref object URL)
        {
            WindowWrapper owner = new WindowWrapper(webBrowser.Handle);
            MessageBox.Show(owner, "Hello World!", "BHO", MessageBoxButtons.OK);
        }

public void SetSite(object site)
        {
            if (site != null)
            {
                webBrowser = (WebBrowser)site;
                webBrowser.DocumentComplete += new DWebBrowserEvents2_DocumentCompleteEventHandler(this.OnDocumentComplete);
            }
            else
            {
                webBrowser.DocumentComplete -= new DWebBrowserEvents2_DocumentCompleteEventHandler(this.OnDocumentComplete);
                webBrowser = null;
            }
        }
    }
}

今回はこれでおしまいです。

コメントを残す