Chapter 7.
IDropTargetとOLEドラッグ&ドロップ



基本だけだと(私が)飽きるので、前回までの学習を生かして実際の使われ方を見ていきましょう。 前回でIUnknownの学習が完了しましたが、実際にはどのように使われているのでしょうか?
OLEにおけるCOMインターフェースの使われ方を見ていきます。

今回の解説対象となるOLEの機能は、OLEドラッグ&ドロップです。このドラッグ&ドロップされる側(以下、D&Dターゲット)に関して解説します。また、ドラッグ&ドロップ操作にはウィンドウが必要となるため、この章ではコンソールアプリケーションではなく、非コンソールアプリケーション(GUIアプリケーション)を前提に話を進めます。

D&Dターゲット側の処理は極めて簡単です。対象となるウィンドウを引数にRegisterDragDrop関数を呼び出すだけです。しかし、この関数にはもう1つ引数があり、それがCOMインターフェースポインタなのです。つまり、アプリケーション側でそのインターフェースを実装し、そのポインタを渡してやる必要があるということです。
ここで実装するべきインターフェースとは、この章のタイトルでもあるIDropTargetです。
このインターフェースの仕様はシステムで定義されており、アプリケーションはその仕様通りにインターフェースを実装するだけです。
また、RegisterDragDrop関数で登録した内容は、ドラッグ&ドロップ操作が不要になった時点で、RevokeDragDrop関数を呼び出して登録を解除する必要があります。

上記内容を実装したのが次のソースです。サイズの都合から別ページに掲載します。
simpledroptarget.h simpledroptarget.cpp chap7.cpp
chap7.rc
resource.h



simpledroptarget.h
このファイルは、IDropTargetを実装するCSimpleDropTargetクラスを定義しています。

今回のサンプルではコンストラクタが、publicからprivateに変更されています。これはこのクラスのインスタンスの作成をCreateInstnace関数以外で行えないようにするための配慮です。
それ以外では、特に目新しいところはありません。インターフェースを実装するクラスの定義部分は毎回同じようなものになるということです。

23行目~26行目IDropTargetインターフェースの4つの関数を定義しています。
この最初と最後の関数は、IDataObjectインターフェースのポインタを引数に取ります。このインターフェースの役目は、OLEのデータ転送をカプセル化することです。詳細に関しては、この章の範囲を超えるので特に解説しません。興味のある方は、MSDNライブラリ等で検索してみてください。



simpledroptarget.cpp
このファイルは、CSimpleDropTargetクラスの実装部分です。

CreateInstance関数が、最初に実装されていますが、これは、publicメンバを最初に定義・実装するという私のコーディングスタイルによるものです。
前回と同様、この関数の呼び出し後に参照カウンタが1になるように、関数を呼び出しています。

QueryInterface関数の実装では、IDropTargetインターフェースを要求された場合に、それに応答するようにコーディング(25~26行目)しています。
AddRef関数とRelease関数は、前回と同じです。これの実装方法は単純であるため、次回からは特に解説しません。

IDropTargetインターフェースの実装ですが、現在では、全てデバッガを通して呼び出された関数名と座標を表示しているだけ(61行目~94行目)です。このインターフェースの完全な実装は、アプリケーションの構造に依存するため、ここでは詳細には行いません。



chap7.cpp
chap7.rc
resource.h

chap7.cppには、アプリケーションのエントリーポイントを実装しています。
chap7.rcとresource.hに関して特に解説を行いません。

これは通常のダイアログベースアプリケーションの記述方法と同じですが、ダイアログを作成する前に、OleInitialize関数を呼び出しています(11行目)。この関数はOLEシステムを使用する前に呼び出す必要のある関数で、今回はドラッグ&ドロップのOLEシステムを使用するので、ここで呼び出しています。これと対になる関数が、OleUninitialize関数で、ダイアログが終了しOLEシステムが使用されなくなった時点で呼び出しています(17行目)

ダイアログプロシージャ内のWM_INITDIALOGメッセージハンドラで、このダイアログをD&Dターゲットとして登録しています(36行目)
ここで特筆すべきは、RegisterDragDrop関数を呼び出した後で、IDropTargetへの参照を解除していることです(40行目)。CreateInstance関数呼び出し後、参照カウンタは1になっており、ここでRelease関数を呼び出すとインスタンスが破棄されてしまうのではないか?と危惧する方が居られるかもしれません。
OLEシステムが受け取ったIDropTargetインターフェースポインタを使用するのは、登録関数を呼び出した後です。そのためOLEシステムは、このIDropTargetインターフェースポインタのコピーを作成します。コピーを作成すると言うことは、新しい参照を取得するのと同じ意味を持ちます。
そのため、RegisterDragDrop関数内部で、コンポーネントのAddRef関数を呼び出しています。(これはVC++などのデバッガでブレークポイントを設定して確認するとよく分かると思います。)
上記の動作を踏まえれば、ここで不要になったインターフェースへの参照を解除するのは、アプリケーションとして当然のことだと言えます。

最後にダイアログプロシージャのWM_CLOSEメッセージハンドラで、RevokeDragDrop関数を呼び出し(47行目)登録を解除しています。RegisterDragDropでインクリメントされた参照カウンタをデクリメントするため、OLEシステムは、RevokeDragDrop関数内部で、コンポーネントのRelease関数を呼び出します。
このサンプルの場合、この時点で参照カウンタが0となり、インスタンスの削除が行われます。

この章での重要な項目を要約すると
インターフェースポインタのコピーとは、新しい参照の獲得であり、AddRef関数を呼び出す必要がある
ということです。



以上で、OLEドラッグ&ドロップのターゲット側の解説を終わります。
実践と言いながら、基礎で抜けていた部分の解説を行っていますが・・・(汗

このIDropTargetインターフェースの実装は比較的簡単なので、これの完全な実装を行ってみるのも面白いかと思います。それは読者への課題として残しておきましょう。(私が楽だから)

最後に上記5ファイルを固めたソースです。Win32アプリケーションとして、ビルド&実行してください。
C++用ソース:chap7.lzh(4KB未満)


前へ 次へ
COM研究室へ
総合トップへ