非同期ドラッグ&ドロップ
今回は非同期でドラッグ&ドロップ処理を実行させる方法です。コード上は前回(非実在ファイルをエクスプローラにD&Dする)の続きになります。
通常、ドロップ(IDataObject::GetDataの呼び出し)が行われるのはDoDragDrop関数から返ってくる前です。一方、非同期なドロップではDoDragDropから返ってきた後にドロップ処理が行われるようになります。
以下がコードです。前回のコードを使い回していますが、CFSTR_FILECONTENTSおよびCFSTR_FILEDESCRIPTORとは無関係に実装できるはずです。
非同期対応に必要な手順はこうです。
- IDataObjectを実装しているオブジェクトに対して、IDataObjectAsyncCapabilityも実装する。
- IDataObjectAsyncCapability::GetAsyncModeでTRUEを返す。
その後、IDataObjectAsyncCapability::StartOperationと同::EndOperationが呼び出されます。この間、こちら側ではGetDataなどでドロップしたデータが読み出せることを保障する必要があります。
たとえば、もしSetDataで中身を書き換えられる実装であれば配慮が必要になるでしょう。すなわち、SetDataを呼ばないように注意したり、あるいはSetDataをロック中だとしてHRESULTエラー値を突き返す実装にしたりなどといったことをすればよいと思います。
IDataObjectAsyncCapabilityはもともとIAsyncOperationという名前でしたが、Windows 8の登場時に現在の名前に変わったようです。IIDは変わっていないので同じインタフェースです。
今回のコードにはOutputDebugStringを仕込んでおり、その出力はこうなります。
[132356] Before DoDragDrop: 131780 [132356] GetData(CFSTR_FILEDESCRIPTOR): 131780 [132356] GetData(CFSTR_FILEDESCRIPTOR): 131780 [132356] StartOperation: 131780 [132356] After DoDragDrop: 131780 [132356] GetData(CFSTR_FILEDESCRIPTOR): 131780 [132356] GetData(CFSTR_FILECONTENTS): 131780 [132356] EndOperation: 131780
なお、IDataObjectおよびIDataObjectAsyncCapabilityの実装にフリースレッド・マーシャラーを集成すると、StartOperation/GetData/EndOperationが別のスレッドから呼び出されるようになります。以下がそのようにしたコードです。
WPF版が今回ないのは、WPFのSystem.Windows.DataObjectがsealedだからです。Windows Forms版の実装では、System.Windows.Forms.DataObjectから派生したクラスを作ることで、IDataObjectAsyncCapabilityを実装しています。これがWPFのDataObjectではできないわけです。
.NETだと何もしなくてもフリースレッド・マーシャラーを集成したかのような挙動になるので、こちらで取り上げています。これについてはOLE on .NET Framework – Chapter.30 はまり道(3)が詳しいです。
今回の話のネタ元はMSDNライブラリのHandling Shell Data Transfer Scenariosです。
ところで、この非同期ドラッグ&ドロップ、Windowsエクスプローラ以外の受け手(IDropTarget実装)の場合でも使えるのでしょうか?私は試していません。
スポンサード リンク |