MarineProjects/MarineToolbar/Developer

プラグインの開発 bookmark

ここでは実際にプラグインのサンプルを作成しながら、プラグインの作成方法について説明していきます。
最初は簡単なサンプルを作成して、徐々に機能を追加していくことにします。

前提知識 bookmark

ここでは、VC++の操作、C++言語やCOMに関して理解しているものとして説明を行います。
これらの前提知識に関しては書籍などを参考にしてください。

プラグインの開発に必要なもの bookmark

  • 開発ツール
    • Visual Studio.NET(Visual C++)を使用します。
  • MarineToolBarPlugin関連のファイル
    • ここからプラグイン開発に必要なファイルを入手しておいてください。

作成するプラグイン bookmark

作成するプラグイン

準備 bookmark

今回のサンプルは、D:\Projectsフォルダを用意して、そのフォルダにVC++のプロジェクトを作成することにします。以下の手順でフォルダを作成して、必要なファイルを展開しておきます。

  1. D:\Projectsフォルダを作成する
  2. MarineToolBarPlugin.zipをダウンロードして、D:\Projectsフォルダに解凍する。

これで準備はできましたので、プロジェクトを作成していきましょう。

サンプルプラグインの開発 bookmark

プロジェクトの作成 bookmark

  • Visual Studio.NETを起動します。
  • メニューから[ファイル]→[新規作成]→[プロジェクト]を選択します。
  • プロジェクト名にSamplePlugin、場所にD:\Projects、テンプレートからATLプロジェクトを選択して、[OK]ボタンをクリックします。
Marineツールバー
  • ATLプロジェクトウィザードが起動されます。ここでは、[アプリケーションの設定]タブで以下の項目を設定して[完了]ボタンをクリックします。
    • 属性:チェックを外す
    • サーバーの種類:ダイナミックリンクライブラリ(DLL)
Marineツールバー
  • これで雛形となるプロジェクトが作成されました。
  • 次にプラグインとなるCOMオブジェクトを作成します。
    [プロジェクト]→[クラスの追加]を選択して、[クラスの追加]ダイアログを表示します。
Marineツールバー
  • カテゴリからATL、テンプレートからATLシンプルオブジェクトを選択して、[開く]ボタンをクリックします。
  • から[ATLシンプルオブジェクト]ウィザードが実行されるので、下図のように入力します。今回はプラグインを実装するクラス名をSampleButtonとしました。
Marineツールバー
  • ここまでの作業で必要なファイルが一通り作成されました。ソリューションエクスプローラーでは下図のようになっています。
Marineツールバー

フォルダ構成は、以下のようになっていることを確認してください。

  • D:\Projects\
    • MarineToolBarPlugin
    • SamplePlugin

ファイルの修正 bookmark

IDLファイルにプラグイン用インターフェースの追加 bookmark

idlファイルは、これから実装するCOMオブジェクトのI/Fを規定しているファイルです。今回はCOMオブジェクトの実装にATLを利用していますが、ウィザードで生成されたISampleButtonは使用しません。その変わりにプラグインで必要なIToolBarPluginと_DToolBarPluginEventsを実装します。
SamplePlugin.idl

// SamplePlugin.idl : SamplePlugin の IDL ソース
//
//
// このファイルは、タイプ ライブラリ (SamplePlugin.tlb) 
// およびマーシャリング コードを
// 作成するために MIDL ツールによって処理されます。

import "oaidl.idl";
import "ocidl.idl";
/*
[
	object,
	uuid(A53EF29B-519F-4E20-B6D5-33752552DDC1),
	dual,
	nonextensible,
	helpstring("ISampleButton インターフェイス"),
	pointer_default(unique)
]
interface ISampleButton : IDispatch{
};
*/
[
	uuid(44D26739-09D5-4034-A461-AF00D65D0395),
	version(1.0),
	helpstring("SamplePlugin 1.0 タイプ ライブラリ")
]
library SamplePluginLib
{
	import "..\MarineToolBarPlugin\MarineToolBarPlugin.idl";
	importlib("stdole2.tlb");
	[
		uuid(A96ED58C-255D-430F-B04C-728ED727A752),
		helpstring("SampleButton Class")
	]
	coclass SampleButton
	{
		[default] interface IToolBarPlugin;
		[default, source] dispinterface _DToolBarPluginEvents;
	};
};

ここのでの変更内容は以下の通りです。

  • ISampleButton定義は不要なためコメントアウト
  • プラグインで定義されたI/Fを使用するためにimport文を追加
    • import "..\MarineToolBarPlugin\MarineToolBarPlugin.idl";
  • coclass SampleButtonが実装するinterfaceを変更
    • [default] interface IToolBarPlugin;
    • [default, source] dispinterface _DToolBarPluginEvents;

IToolBarPluginと_DToolBarPluginEventsの追加 bookmark

SampleButton.h

SampleButton.hは、CSampleButtonクラスを実装するためのヘッダファイルです。
CSampleButtonクラスでは、ISampleButtonが実装されているので、IToolBarPluginに変更していきます。

ここのでの変更内容は以下の通りです。詳しくはソースファイルを参照してください。

  • 定義ファイルの読み込み
    • #include "..\MarineToolBarPlugin\MarineToolBarPlugin.h"
    • #include "..\MarineToolBarPlugin\_DToolBarPluginEvents_CP.H"
  • ISampleButtonからIToolBarPluginへ変更
  • IConnectionPointContainerImplの追加
    public IConnectionPointContainerImpl<CSampleButton>,
  • CProxy_DToolBarPluginEventsの追加
    CProxy_DToolBarPluginEventsは、VC++でプラグインを開発する際に利用できる_DToolBarPluginEventsを実装したクラスです。CSampleButtonは、_DToolBarPluginEventsクラスを多重継承します。
    public CProxy_DToolBarPluginEvents<CSampleButton>
  • BEGIN_COM_MAPの変更
    BEGIN_COM_MAP(CSampleButton)
    	COM_INTERFACE_ENTRY(IToolBarPlugin)
    	COM_INTERFACE_ENTRY(IDispatch)
    	COM_INTERFACE_ENTRY(IConnectionPointContainer)
    END_COM_MAP()
  • BEGIN_CONNECTION_POINT_MAPの追加
    BEGIN_CONNECTION_POINT_MAP(CSampleButton)
     	CONNECTION_POINT_ENTRY(__uuidof(_DToolBarPluginEvents))
    END_CONNECTION_POINT_MAP()
  • IToolBarPluginのメソッド定義追加
    STDMETHOD(Configuration)(LONG hwnd);
    STDMETHOD(SetPluginInfo)(LONG ID);
    STDMETHOD(SetWebBrowser)(IDispatch* Dispatch);
    STDMETHOD(SetParentHwnd)(LONG hwnd);
    STDMETHOD(GetButtonStyle)(long* Style);
    STDMETHOD(GetButtonText)(BSTR* Text);
    STDMETHOD(GetButtonImage)(long* bitmap);
    STDMETHOD(GetButtonImageIndex)(int* Index);
    STDMETHOD(GetToolTipText)(BSTR* Text);
    STDMETHOD(GetMenu)(long* Menu);
    STDMETHOD(QueryCommandUI)(BSTR Text, long* MenuStatus);
    STDMETHOD(InvokeCommand)(IDispatch* Dispatch, BSTR Text,
      long Command);
    STDMETHOD(InvokeMenuCommand)(IDispatch* Dispatch, BSTR Text,
      long Command);
    STDMETHOD(InvokeDropdown)(IDispatch* Dispatch, BSTR text, 
      LONG x, LONG y, VARIANT_BOOL* result);
    STDMETHOD(Terminate)(void);

SampleButton.cpp

  • IToolBarPluginのメソッドを実装
    SampleButton.hで定義したConfiguration~Terminateの実装を追加します。
    あとで1つ1つのメソッドについてみていきますので、S_OKを返すだけの実装をしておきます。
    STDMETHODIMP CSampleButton::Configuration(LONG hwnd)
    {
    	return S_OK;
    }
    ※同様にすべてのメソッドを追加していきます。

GetPluginInfo関数の追加 bookmark

SamplePlugin.cpp

  • 必要なファイルの読み込み
    MarineToolBarPlugin.hとMarineToolBarPlugin_i.cは、エキスポート関数の定義には関係なくプラグインで定義されたCOMインターフェースに関する定義です。
    #include "..\MarineToolBarPlugin\MarineToolBarPlugin.h"
    #include "..\MarineToolBarPlugin\MarineToolBarPlugin_i.c"
    #include "..\MarineToolBarPlugin\ToolBarPluginIF.h"
  • 関数の実装
    C++プロジェクト作成しているため、外部にエキスポートするためにextern "C" を宣言しています。
    extern "C" BOOL WINAPI GetPluginInfo(
       LPPLUGINSTRUCT lpPluginStruct, LPDWORD  lpdwCount)
    {
                ・
                ・
                ・
        return TRUE;
    }

SamplePlugin.def

  • エキスポートする関数を追加
    すでに記述されているDllUnregisterServerの後に追加した例
    最終行のGetPluginInfoが追加されていることを確認してください。
    EXPORTS
    	DllCanUnloadNow		PRIVATE
    	DllGetClassObject		PRIVATE
    	DllRegisterServer		PRIVATE
    	DllUnregisterServer	PRIVATE
    	GetPluginInfo

追加した内容の確認 bookmark

ここまで追加した内容に問題がないかを確認するために、一度コンパイルしてみましょう。

勿論、完全な実装ではないので、このままでは動作しませんが、この時点で正常にコンパイルできるかを確認しておいた方がいいと思います。

機能の追加 bookmark

これまでの作業でプラグインを作成するために必要な修正は完了しています。
これを雛型として実際のプラグインを追加していきましょう。
今回作成するプラグインは以下の仕様とします。

  • ボタンのスタイル:ノーマルなプッシュボタン
  • ボタンをクリック:メッセージの表示
    仕様的には単純なものですが、これが基本となっていくので、実際に実装していくことが理解を深めていくと思います。

リソースの用意 bookmark

プラグインでは実際のボタンに表示するビットマップと[プラグインの追加と削除]ダイアログに表示するビットマップの2種類が必要です。

ビットマップの仕様は1つのボタンのサイズが16×16、透過色はRGB(255, 0, 255)固定です。
[プラグインの追加と削除]ダイアログに表示するビットマップは16×16が1つだけ、実際のボタンに表示されるビットマップは、作成するアプリケーションに依存します。
例えば、信号のように状態が3種類ある場合は青、黄、赤のイメージ3種類を用意して、状態によって切り替えていくことになります。

とりあえずは状態を管理する仕様ではありませんが、後々状態によってアイコンを変更する予定なので、以下のようにビットマップを作成します。

  • [プラグインの追加と削除]ダイアログに表示するビットマップ
    • サイズ:16×16(1つのボタン)
    • ファイル名:button.bmp
      Marineツールバー
  • ボタン用のビットマップ
    • サイズ:32×16(2つのボタン)
    • ファイル名:image.bmp
      Marineツールバー

リソースのインポート bookmark

用意したビットマップをD:\Projects\SamplePlugin\resにコピーして、VC++にインポートします。インポートする時のID値は任意の値でもかまいませんが、ここでは以下のように指定します。

  • IDB_BITMAP_BUTTON : D:\Projects\SamplePlugin\res\button.bmp
  • IDB_BITMAP_IMAGE : D:\Projects\SamplePlugin\res\image.bmp

GetPluginInfo関数の実装 bookmark

次にプラグインの情報を取得する時に呼び出されるGetPluginInfo関数を実装します。

ここで重要なのは、PLUGINSTRUCT構造体のszCLSIDです。プラグインはCOMオブジェクトとして実装します。COMオブジェクトには、そのオブジェクトを識別するためのCLSIDという他のオブジェクトと一致しないユニークな値を割り当てる必要があります。

このサンプルでは、[ATLシンプルオブジェクト]ウィザードを使って、ATLシンプルオブジェクトを作成したので、自動的にユニークなCLSIDが割り当てられています。
その値は、SampleButton.rgsに記述されています。

このサンプルでは、{A96ED58C-255D-430F-B04C-728ED727A752}となっています。

[プラグインの追加と削除]ダイアログに表示されるボタンは、PLUGINSTRUCT構造体のhBitmapにビットマップハンドルとして設定します。
ビットマップをリソースから読み込むには、HINSTANCEが必要になります。その値は、DLLが読み込まれて一番初めに実行されるDllMain関数に引数として渡されます。その値を変数m_hInstanceに格納しておき、ビットマップを読み込む時にはその値を指定します。

その他のPLUGINSTRUCT構造体のメンバーはリファレンスを参照いただければ理解できると思います。

extern "C" BOOL WINAPI GetPluginInfo(LPPLUGINSTRUCT lpPluginStruct,
                                     LPDWORD lpdwCount)
{
    if(!lpPluginStruct){
        *lpdwCount = 1;
        return TRUE;
    }

    ::lstrcpy(lpPluginStruct->szCLSID,
          _T("{A96ED58C-255D-430F-B04C-728ED727A752}"));
    ::lstrcpy(lpPluginStruct->szPluginName,
          _T("サンプル"));
    ::lstrcpy(lpPluginStruct->szPluginDescription,
          _T("サンプルプラグインです"));
    ::lstrcpy(lpPluginStruct->szAuthor,
          _T("Darksky"));
    lpPluginStruct->hBitmap = ::LoadBitmap(m_hInstance,
          MAKEINTRESOURCE(IDB_BITMAP_BUTTON));

    //
    // 1.00 Release0 Build1
    //
    lpPluginStruct->byVersion[0] = 1;
    lpPluginStruct->byVersion[1] = 0;
    lpPluginStruct->byVersion[2] = 0;
    lpPluginStruct->byVersion[3] = 1;

    *lpdwCount = 1;
    return TRUE;
}

IToolBarPluginの実装 bookmark

前回、空のメソッドのみ追加しましたので、実際の処理を追加していきます。

  • STDMETHODIMP Configuration(LONG hwnd)
    Configurationメソッドは、プラグイン独自の設定やバージョン情報を表示する時に呼び出されます。今回は特に設定を行いませんので、MessageBox APIを使ってバージョン情報を表示するコードと追加します。
    #define VERSION_TEXT	_T("・・・・")
    ::MessageBox((HWND)hwnd, VERSION_TEXT, _T("バージョン情報"),
                                        MB_OK | MB_ICONINFORMATION);
  • STDMETHODIMP SetPluginInfo(LONG ID)
    SetPluginInfoは、プラグインに割り当てられたユニークなIDを通知する時に呼び出されます。
    このID値は、プラグイン側から本体へのイベントを通知する時に必要となる値なので、保存しておく必要があります。
    クラス内のメンバー変数LONG m_IDを定義して、その変数に値を格納します。
    m_ID = ID;
  • STDMETHODIMP SetWebBrowser(IDispatch* Dispatch)
    SetWebBrowserは、Marineツールバーを実行しているInternet ExplorerのIWebBrowser2を通知するために呼び出されます。
    ボタンがクリックされた場合に任意のサイトに移動したり、Internet Explorerを制御する場合に必要になります。
    クラス内のメンバー変数CComQIPtr<IWebBrowser2> m_spWebBrowser2を定義して、その変数に値を格納します。
    ここではATLがサポートするスマートポインタを使用しています。
    m_spWebBrowser2 = Dispatch;
  • STDMETHODIMP SetParentHwnd(LONG hwnd)
    SetParentHwndは、プラグインの親となるウィンドウのハンドルを通知するために呼び出されます。ウィンドウを作成したり、メッセージボックスを表示する場合の親ウィンドウとして使用します。
    今回のサンプルでは特に必要ではありませんが、クラス内のメンバー変数HWND m_hwndを定義して、その変数に値を格納しておきます。
    m_hwnd = (HWND)hwnd;
  • STDMETHODIMP GetButtonStyle(long* Style)
    GetButtonStyleは、プラグインがサポートするボタンのスタイルを取得するために呼び出されます。今回のサンプルでは、ノーマルなプッシュボタンを作成するので、BUTTON_STYLE_NORMALを設定します。
    *Style = BUTTON_STYLE_NORMAL;
  • STDMETHODIMP GetButtonText(BSTR* Text)
    GetButtonTextは、ボタンに表示するテキストを取得するために呼び出されます。今回は”サンプル”文字列を設定しておきます。また、空文字を指定するとボタンテキストは表示されません。
    *Text = ::SysAllocString(L"サンプル");
  • STDMETHODIMP GetButtonImage(long* bitmap)
    GetButtonImageは、ボタンに表示するイメージを取得するために呼び出されます。先ほど作成したボタンイメージ用ビットマップのハンドルを設定します。
    *bitmap = (LONG)::LoadBitmap(m_hInstance,
                       MAKEINTRESOURCE(IDB_BITMAP_IMAGE));
  • STDMETHODIMP GetButtonImageIndex(int* Index)
    GetButtonImageIndexは、ボタンに表示するイメージのインデックスを取得するために呼び出されます。
    複数のボタンイメージの中からどのイメージを表示するかを指定します。指定可能なインデックス値は、GetButtonImageで設定したビットマップのボタン数に依存します。
    インデックス値は、0~ボタン数-1の値を指定することに注意してください。
    今回は2つのボタンイメージを作成したので、0,1のいづかかを指定しることになりますが、特に理由はありませんが、0を設定することにします。
    *Index = 0;
  • STDMETHODIMP GetToolTipText(BSTR* Text)
    GetToolTipTextは、ボタンのツールチップに表示する文字列を取得するために呼び出されます。今回は単純に”サンプルボタンです”という文字列を設定します。
    *Text = ::SysAllocString(L"サンプルボタンです");
  • STDMETHODIMP GetMenu(long* Menu)
    GetMenuは、ポップアップメニューの表示に使用するポップアップメニューハンドルを取得するために呼び出されます。今回のサンプルでは、ポップアップメニューを表示するボタンスタイルを指定していないので、このメソッドが呼び出されることはありませんが、一応以下のようにNULLを設定しておきます。
    *Menu = NULL;
  • STDMETHODIMP QueryCommandUI(BSTR Text, long* MenuStatus)
    QueryCommandUIは、ボタンの状態(有効・無効)を取得するために呼び出されます。
    検索ボックスに文字の入力があった場合などに呼び出されます。
    例えば、検索ボックスに入力された文字を利用して何かを処理を行うプラグインの場合、空文字のときは無効表示、文字が入力されているときは有効表示というように状態を変更することができます。
    今回は常に有効状態にするため、BUTTON_STATUS_ENABLEを設定します。
    *MenuStatus = BUTTON_STATUS_ENABLE;
  • STDMETHODIMP InvokeCommand(IDispatch* Dispatch, BSTR Text, long Command)
    InvokeCommandは、ボタンがクリックされた時に呼び出されます。
    今回は単純にMessageBoxを利用して、メッセージを表示してみることにします。
    ::MessageBox(m_hwnd, _T("ボタンクリック!!"), _T("コマンド実行"),
                                            MB_OK | MB_ICONINFORMATION);
  • STDMETHODIMP InvokeMenuCommand(IDispatch* Dispatch, BSTR Text, long Command)
    InvokeMenuCommandは、ポップアップメニューが選択された時に呼び出されます。
    今回のサンプルではポップアップメニューを表示しないので、呼び出されることはありません。
    S_OKを返すだけで十分です。
  • STDMETHODIMP InvokeDropdown(IDispatch* Dispatch, BSTR text, LONG x, LONG y, VARIANT_BOOL* result)
    InvokeDropdownは、ポップアップメニュ-を表示するタイミングで呼び出されます。
    ポップアップメニューの表示は本体側で行うこともプラグイン側で処理することも可能です。
    プラグイン側で独自のポップアップメニューを表示したい場合はこのメソッドで処理を行います。
    プラグイン側で処理を行ったかどうかは、resultに格納します。プラグイン側で処理を行っていない場合、このメソッド呼び出しの後にGetMenuメソッドが呼び出されます。
    今回はポップアップメニューを表示しませんので、resultにVARIANT_FALSEを設定しておきます。実際にはポップアップメニューを表示するスタイルを指定していないので、予備だsれることはありません。
    *result = VARIANT_FALSE;

これで一通りの実装ができましたので、実際に動作するか確認してみましょう。

動作確認 bookmark

Marineツールバーのプラグインは、MarineツールバーをインストールしたPluginフォルダに格納します。
コンパイル後に生成されたSamplePlugin.dllを該当のフォルダにコピーします。

Internet Explorerを起動して、Marineツールバーから[プラグインの追加と削除]メニューを選択します。

表示されたダイアログに今回作成されたプラグインが表示されていることを確認します。

Marineツールバー

[利用できるプラグイン]に表示されたサンプルを追加して、プラグインを利用できるように設定します。[OK]ボタンをクリックしてダイアログを閉じると、画面上に今回作成したプラグインが表示されます。

Marineツールバー

ボタンの上にマウスを当てるとツールチップも表示されます。

実際にボタンをクリックしてみましょう。

Marineツールバー

InvokeCommandメソッドで追加したMessageBoxが実行されて、画面にメッセージが表示されました。

単純なプラグインではありますが、立派に動作することが確認できると思います。

ご注意 bookmark

このサンプルプラグイン作成時に生成されたGUID、CLSID等をそのままの状態で公開しないでください。COMオブジェクトに割り当てられたGUID、CLSID値が複数存在することはできないからです。