シェル データ転送シナリオの処理
シェル データ オブジェクトのドキュメントでは、ドラッグ アンド ドロップまたはクリップボードを使用してシェル データを転送するために使用される一般的な方法について説明しました。 ただし、アプリケーションでシェル データ転送を実装するには、シェル データを転送するさまざまな方法にこれらの一般的な原則と手法を適用する方法も理解する必要があります。 このドキュメントでは、シェルの一般的なデータ転送シナリオについて説明し、アプリケーションでそれぞれを実装する方法について説明します。
- 一般的なガイドライン
- クリップボードからアプリケーションへのファイル名のコピー
- ドロップされたファイルの内容をアプリケーションにコピーする
- 最適化された移動操作の処理
- 貼り付け時の削除操作の処理
- 仮想フォルダーとの間でデータを転送する
- ごみ箱にファイルをドロップする
- スクラップ ファイルの作成とインポート
- シェル オブジェクトの非同期的なドラッグ アンド ドロップ
Note
これらの各シナリオでは、特定のデータ転送操作について説明しますが、その多くはさまざまな関連シナリオに適用されます。 たとえば、ほとんどのクリップボード転送とドラッグ アンド ドロップ転送の主な違いは、データ オブジェクトがターゲットに到達する方法です。 ターゲットがデータ オブジェクトの IDataObject インターフェイスへのポインターを持つ場合、情報を抽出する手順は、両方の種類のデータ転送でほとんど同じです。 ただし、一部のシナリオは特定の種類の操作に限定されます。 詳細については、個々のシナリオを参照してください。
一般的なガイドライン
以降の各セクションでは、かなり具体的な単一のデータ転送シナリオについて説明します。 ただし、多くの場合、データ転送はより複雑になり、いくつかのシナリオの側面が伴う場合があります。 通常、実際に処理する必要があるシナリオは事前にわかりません。 注意すべき一般的なガイドラインをいくつか次に示します。
データ ソースの場合:
- シェル クリップボードの形式は、CF_HDROPを除き、定義済みではありません。 使用する各形式は、RegisterClipboardFormat を呼び出して登録する必要があります。
- データ オブジェクトの形式は、ソースからの優先順に提供されます。 データ オブジェクトを列挙し、使用できる最初のオブジェクトを選択します。
- サポートできる数の形式を含めます。 通常、データ オブジェクトが削除される場所はわかりません。 この方法により、ドロップ ターゲットが受け入れ可能な形式をデータ オブジェクトに含める確率が向上します。
- 既存のファイルは、CF_HDROP形式で提供する必要があります。
- CFSTR_FILECONTENTS CFSTR_FILEDESCRIPTOR/形式でファイルのようなデータを提供します。 この方法により、ターゲットは基になるデータ ストレージについて何も知らなくても、データ オブジェクトからファイルを作成できます。 通常、データは IStream インターフェイスとして表示する必要があります。 このデータ転送メカニズムは、グローバル メモリ オブジェクトよりも柔軟性が高く、使用するメモリがはるかに少なくなります。
- ドラッグ ソースは、シェル項目を ドラッグするときにCFSTR_SHELLIDLIST 形式を提供する必要があります。 項目のデータ オブジェクトは、IShellFolder::GetUIObjectOf メソッドまたは IShellItem::BindToHandler メソッドを使用して取得できます。 データ ソースでは、SHCreateDataObject を使用して、CFSTR_SHELLIDLIST形式をサポートする標準のデータ オブジェクト実装を作成できます。
- シェル項目プログラミング モデルを使用してドラッグされる項目を理由にするターゲットをドロップすると、SHCreateShellItemArrayFromDataObject を使用して IDataObject を IShellItemArray に変換できます。
- 標準のフィードバック カーソルを使用します。
- 左右のドラッグをサポートします。
- 埋め込みオブジェクトのデータ オブジェクト自体を使用します。 この方法により、アプリケーションは、データ オブジェクトが提供する必要がある追加の形式を取得でき、追加の包含レイヤーを作成することを回避できます。 たとえば、サーバー A の埋め込みオブジェクトがサーバー/コンテナー B からドラッグされ、コンテナー C にドロップされます。C は、サーバー A の埋め込みオブジェクトを含むサーバー B の埋め込みオブジェクトではなく、サーバー A の埋め込みオブジェクトを作成する必要があります。
- シェルでは、ファイルの移動時に最適化された移動操作または貼り付け時の削除操作が使用される場合があることに注意してください。 アプリケーションは、これらの操作を認識し、適切に応答できる必要があります。
データ ターゲットの場合:
- シェル クリップボードの形式は、CF_HDROPを除き、定義済みではありません。 使用する各形式は、RegisterClipboardFormat を呼び出して登録する必要があります。
- OLE ドロップ ターゲットを実装して登録します。 可能であれば、Windows 3.1 ターゲットまたは WM_DROPFILES メッセージを使用しないでください。
- データ オブジェクトに含まれる形式は、オブジェクトの取得元によって異なります。 通常、データ オブジェクトがどこから来ているかは事前にわからないため、特定の形式が存在するとは想定しないでください。 データ オブジェクトは、最適な形式から順に形式を列挙する必要があります。 したがって、使用可能な最適な形式を取得するために、アプリケーションは通常、使用可能な形式を列挙し、サポートできる列挙型の最初の形式を使用します。
- 右ドラッグをサポートします。 ドラッグ アンド ドロップ ハンドラーを作成することで、ドラッグ ショートカット メニューをカスタマイズできます。
- アプリケーションが既存のファイルを受け入れる場合は、CF_HDROP形式を処理できる必要があります。
- 一般に、ファイルを受け入れるアプリケーションでは、CFSTR_FILECONTENTS CFSTR_FILEDESCRIPTOR/形式も処理する必要があります。 ファイル システムのファイルはCF_HDROP形式ですが、名前空間拡張などのプロバイダーのファイルでは、通常、CFSTR_FILECONTENTS CFSTR_FILEDESCRIPTOR/が使用されます。 たとえば、Windows CE フォルダー、ファイル転送プロトコル (FTP) フォルダー、Web フォルダー、CAB フォルダーなどがあります。 ソースは通常、IStream インターフェイスを実装して、ストレージからのデータをファイルとして表示します。
- シェルでは、ファイルの移動時に最適化された移動操作または貼り付け時の削除操作が使用される場合があることに注意してください。 アプリケーションは、これらの操作を認識し、適切に応答できる必要があります。
クリップボードからアプリケーションへのファイル名のコピー
シナリオ: ユーザーがエクスプローラーで 1 つ以上のファイルを選択し、クリップボードにコピーします。 アプリケーションによってファイル名が抽出され、ドキュメントに貼り付けられます。
このシナリオは、たとえば、ユーザーがアプリケーションにファイルを切り取って貼り付けることで HTML リンクを作成できるようにするために使用できます。 その後、アプリケーションはデータ オブジェクトからファイル名を抽出し、それを処理してアンカー タグを作成できます。
ユーザーがエクスプローラーでファイルを選択してクリップボードにコピーすると、シェルによってデータ オブジェクトが作成されます。 次に、OleSetClipboard を呼び出して、データ オブジェクトの IDataObject インターフェイスへのポインターをクリップボードに配置します。
ユーザーがアプリケーションの メニューまたはツール バーから [貼り付け] コマンドを選択した場合:
- OleGetClipboard を呼び出して、データ オブジェクトの IDataObject インターフェイスを取得します。
- IDataObject::EnumFormatEtc を呼び出して列挙子オブジェクトを要求します。
- 列挙子オブジェクトの IEnumFORMATETC インターフェイスを使用して、データ オブジェクトに含まれる形式を列挙します。
Note
この手順の最後の 2 つの手順は、完全のために含まれています。 通常、単純なファイル転送には必要ありません。 この種類のデータ転送に使用されるすべてのデータ オブジェクトには、オブジェクトに 含まれるファイルの名前を決定するために使用できるCF_HDROP 形式が含まれている必要があります。 ただし、より一般的なデータ転送の場合は、形式を列挙し、アプリケーションで処理できる最適な形式を選択する必要があります。
データ オブジェクトからのファイル名の抽出
次の手順では、データ オブジェクトから 1 つ以上のファイル名を抽出し、アプリケーションに貼り付けます。 このセクションで説明するデータ オブジェクトからファイル名を抽出する手順は、ドラッグ アンド ドロップ転送にも同様に適用されることに注意してください。
データ オブジェクトからファイル名を取得する最も簡単な方法は、 CF_HDROP 形式です。
IDataObject::GetData を呼び出します。 FORMATETC 構造体の cfFormat メンバーを CF_HDROP に設定し、tymed メンバーを TYMED_HGLOBAL に設定します。 dwAspect メンバーは通常、DVASPECT_CONTENTに設定されます。 ただし、ファイルのパスを短い (8.3) 形式にする必要がある場合は、dwAspect を DVASPECT_SHORT に設定します。
IDataObject::GetData が返されると、STGMEDIUM 構造体の hGlobal メンバーは、データを含むグローバル メモリ オブジェクトを指します。
HDROP 変数を作成し、STGMEDIUM 構造体の hGlobal メンバーに設定します。 HDROP 変数は、DROPFILES 構造体へのハンドルの後に、コピーされたファイルの完全修飾ファイル パスを含む null で終わる 2 つの文字列になりました。
iFile パラメーターを 0xFFFFFFFF に設定して DragQueryFile を呼び出して、リスト内のファイル パスの数を確認します。 この関数は、リスト内のファイル パスの数を返します。 このリストのファイル パスの 0 から始まるインデックスは、次の手順で特定のパスを識別するために使用されます。
iFile をファイルのインデックスに設定して、ファイルごとに DragQueryFile を 1 回呼び出して、グローバル メモリ オブジェクトからファイル パスを抽出します。
必要に応じてファイル パスを処理し、アプリケーションに貼り付けます。
ReleaseStgMedium を呼び出し、手順 1 で IDataObject::GetData に渡した STGMEDIUM 構造体へのポインターを渡します。 構造体を解放すると、手順 2 で作成した HDROP 値は無効になり、使用できません。
ドロップされたファイルの内容をアプリケーションにコピーする
シナリオ: ユーザーがエクスプローラーから 1 つ以上のファイルをドラッグし、アプリケーションのウィンドウにドロップします。 アプリケーションがファイルの内容を抽出し、アプリケーションに貼り付けます。
このシナリオでは、ドラッグ アンド ドロップを使用して Windows エクスプローラーからアプリケーションにファイルを転送します。 操作の前に、アプリケーションで次の操作を行う必要があります。
- RegisterClipboardFormat を呼び出して、必要なシェル クリップボード形式を登録します。
- RegisterDragDrop を呼び出して、ターゲット ウィンドウとアプリケーションの IDropTarget インターフェイスを登録します。
ユーザーが操作を開始した後、1 つ以上のファイルを選択してドラッグを開始します。
- Windows エクスプローラーでは、データ オブジェクトが作成され、サポートされている形式が読み込まれます。
- Windows エクスプローラーは DoDragDrop を呼び出してドラッグ ループを開始します。
- ドラッグ イメージがターゲット ウィンドウに到達すると、IDropTarget::D ragEnter を呼び出すことによってシステムから通知されます。
- データ オブジェクトに含まれるものを特定するには、データ オブジェクトの IDataObject::EnumFormatEtc メソッドを呼び出します。 データ オブジェクトに含まれる形式を列挙するには、メソッドによって返される列挙子オブジェクトを使用します。 アプリケーションでこれらの形式を受け入れたくない場合は、DROPEFFECT_NONEを返します。 このシナリオでは、CF_HDROPなど、ファイルの転送に使用される形式を含まないデータ オブジェクトはアプリケーションで無視する必要があります。
- ユーザーがデータを削除すると、システムは IDropTarget::D rop を呼び出します。
- IDataObject インターフェイスを使用して、ファイルの内容を抽出します。
データ オブジェクトからシェル オブジェクトの内容を抽出するには、いくつかの方法があります。 一般に、次の順序を使用します。
- ファイルにCF_TEXT形式が含まれている場合、データは ANSI テキストです。 ファイル自体を開くのではなく、CF_TEXT形式を使用してデータを抽出できます。
- ファイルにリンク OLE オブジェクトまたは埋め込み OLE オブジェクトが含まれている場合、データ オブジェクトにはCF_EMBEDDEDOBJECT形式が含まれます。 標準の OLE 手法を使用してデータを抽出します。 スクラップ ファイル には常にCF_EMBEDDEDOBJECT形式が含まれます。
- Shell オブジェクトがファイル システムの場合、データ オブジェクトにはファイルの名前を 含むCF_HDROP 形式が含まれます。 CF_HDROPからファイル名を抽出し、OleCreateFromFile を呼び出して、新しいリンクオブジェクトまたは埋め込みオブジェクトを作成します。 CF_HDROP形式からファイル名を取得する方法については、「クリップボードからアプリケーションへのファイル名のコピー」を参照してください。
- データ オブジェクトにCFSTR_FILEDESCRIPTOR形式が含まれている場合は、ファイルのCFSTR_FILECONTENTS形式からファイルの内容を抽出できます。 この手順の詳細については、「CFSTR_FILECONTENTS形式を使用してファイルからデータを抽出する」を参照してください。
- シェル バージョン 4.71 より前のアプリケーションは、FILEDESCRIPTOR 構造体の dwFlags メンバーにFD_LINKUIを設定して、ショートカット ファイルの種類を転送していることを示していました。 以降のバージョンのシェルでは、ショートカットが転送されていることを示す方法として、DROPEFFECT_LINKに設定されたCFSTR_PREFERREDDROPEFFECT形式を使用することをお勧めします。 この方法は、フラグを確認するためだけに FILEDESCRIPTOR 構造体を抽出するよりもはるかに効率的です。
データ抽出プロセスに時間がかかる場合は、バックグラウンド スレッドで非同期的に操作を実行できます。 その後、プライマリ スレッドは不要な遅延なしで続行できます。 非同期データ抽出を処理する方法については、「シェル オブジェクトを非同期にドラッグ アンド ドロップする」を参照してください。
CFSTR_FILECONTENTS形式を使用してファイルからデータを抽出する
CFSTR_FILECONTENTS形式は、ファイルの内容を転送する非常に柔軟で強力な方法を提供します。 データを 1 つのファイルとして格納する必要もありません。 この形式に必要なのは、データ オブジェクトがデータをファイルであるかのようにターゲットに提示することです。 たとえば、実際のデータは、テキスト ドキュメントのセクションや、データベースから抽出されたデータのブロックなどです。 ターゲットはデータをファイルとして扱うことができるので、基になるストレージ メカニズムについて何も知る必要はありません。
この形式では特定のストレージ メカニズムが想定されていないため、名前空間拡張機能では通常、CFSTR_FILECONTENTSを使用してデータを転送します。 名前空間拡張機能では、便利なストレージ メカニズムを使用でき、この形式を使用して、オブジェクトをファイルのようにアプリケーションに表示できます。
通常、CFSTR_FILECONTENTSのデータ転送メカニズムはTYMED_ISTREAM。 IStream インターフェイス ポインターを転送すると、データをグローバル メモリ オブジェクトに読み込むよりもはるかに少ないメモリが必要です。IStream は、IStorage よりもデータを表す簡単な方法です。
CFSTR_FILECONTENTS形式には、常にCFSTR_FILEDESCRIPTOR形式が伴います。 最初に、この形式の内容を確認する必要があります。 複数のファイルが転送されている場合、データ オブジェクトには実際には複数のCFSTR_FILECONTENTS形式が含まれます。ファイルごとに 1 つ。 CFSTR_FILEDESCRIPTOR形式には、各ファイルの名前と属性が含まれており、特定のファイルのCFSTR_FILECONTENTS形式を抽出するために必要な各ファイルのインデックス値が提供されます。
CFSTR_FILECONTENTS形式を抽出するには:
- CFSTR_FILEDESCRIPTOR形式をTYMED_HGLOBAL値として抽出します。
- 返された STGMEDIUM 構造体の hGlobal メンバーは、グローバル メモリ オブジェクトを指します。 hGlobal 値を GlobalLock に渡して、そのオブジェクトをロックします。
- GlobalLock によって返されたポインターを FILEGROUPDESCRIPTOR ポインターにキャストします。 FILEGROUPDESCRIPTOR 構造体の後に 1 つ以上の FILEDESCRIPTOR 構造体が続きます。 各 FILEDESCRIPTOR 構造体には、付属 のCFSTR_FILECONTENTS 形式のいずれかに含まれるファイルの説明が含まれています。
- FILEDESCRIPTOR 構造体を調べて、抽出するファイルに対応するものを特定します。 FILEDESCRIPTOR 構造体の 0 から始まるインデックスは、ファイルのCFSTR_FILECONTENTS形式を識別するために使用されます。 グローバル メモリ ブロックのサイズはバイト精度ではないため、構造体の nFileSizeLow メンバーと nFileSizeHigh メンバーを使用して、グローバル メモリ オブジェクト内のファイルを表すバイト数を確認します。
- FORMATETC 構造体の cfFormat メンバーを CFSTR_FILECONTENTS 値に設定し、lIndex メンバーを前の手順で決定したインデックスに設定して、IDataObject::GetData を呼び出します。 通常、tymed メンバーは TYMED_HGLOBAL | に設定されます。TYMED_ISTREAM |TYMED_ISTORAGE。 その後、データ オブジェクトは、優先されるデータ転送メカニズムを選択できます。
- IDataObject::GetData が返す STGMEDIUM 構造体には、ファイルのデータへのポインターが含まれます。 構造体の tymed メンバーを調べて、データ転送メカニズムを決定します。
- tymed が TYMED_ISTREAM または TYMED_ISTORAGE に設定されている場合は、インターフェイスを使用してデータを抽出します。 tymed が TYMED_HGLOBAL に設定されている場合、データはグローバル メモリ オブジェクトに含まれます。 グローバル メモリ オブジェクトからデータを抽出する方法については、「シェル データ オブジェクトのデータ オブジェクトからグローバル メモリ オブジェクトを抽出する」セクションを参照してください。
- GlobalLock を呼び出して、手順 2 でロックしたグローバル メモリ オブジェクトのロックを解除します。
最適化された移動操作の処理
シナリオ: 最適化された移動を使用して、ファイルがファイル システムから名前空間拡張機能に移動されます。
従来の移動操作では、ターゲットはデータのコピーを作成し、ソースは元のデータを削除します。 データのコピーが 2 つ必要なため、この手順は非効率的な場合があります。 データベースなどの大きなオブジェクトでは、従来の移動操作は実用的でない場合もあります。
最適化された移動では、ターゲットはデータの格納方法を理解して移動操作全体を処理します。 データの 2 番目のコピーはなく、ソースが元のデータを削除する必要はありません。 シェル データは、ターゲットがシェル API を使用して操作全体を処理できるため、最適化された移動に適しています。 一般的な例は、ファイルの移動です。 ターゲットに移動するファイルのパスが設定されたら、SHFileOperation を使用して移動できます。 ソースが元のファイルを削除する必要はありません。
Note
シェルは通常、最適化された移動を使用してファイルを移動します。 シェルのデータ転送を適切に処理するには、アプリケーションが最適化された移動を検出して処理できる必要があります。
最適化された移動は、次の方法で処理されます。
ソースは、dwEffect パラメーターを DROPEFFECT_MOVE に設定して DoDragDrop を呼び出し、ソース オブジェクトを移動できることを示します。
ターゲットは、IDropTarget メソッドの 1 つを介してDROPEFFECT_MOVE値を受け取り、移動が許可されていることを示します。
ターゲットは、オブジェクトをコピーするか (最適化されていない移動)、またはオブジェクトを移動します (最適化された移動)。
ターゲットは、元のデータを削除する必要があるかどうかをソースに通知します。
最適化された移動は既定の操作であり、データはターゲットによって削除されます。 最適化された移動が実行されたことをソースに通知するには:
-
- ターゲットは、IDropTarget::D rop メソッドで受け取った pdwEffect 値を、DROPEFFECT_MOVE以外の値に設定します。 通常、DROPEFFECT_NONEまたはDROPEFFECT_COPYに設定されます。 値は DoDragDrop によってソースに返されます。
- ターゲットは、データ オブジェクトの IDataObject::SetData メソッドも呼び出し、DROPEFFECT_NONEに設定されたCFSTR_PERFORMEDDROPEFFECT形式識別子を渡します。 一部のドロップ ターゲットで DoDragDrop の pdwEffect パラメーターが正しく設定されない可能性があるため、このメソッド呼び出しが必要です。 CFSTR_PERFORMEDDROPEFFECT形式は、最適化された移動が行われたことを示す信頼性の高い方法です。
ターゲットが最適化されていない移動を行った場合は、ソースによってデータを削除する必要があります。 最適化されていない移動が実行されたことをソースに通知するには:
-
- ターゲットは、IDropTarget::D rop メソッドで受け取った pdwEffect 値をDROPEFFECT_MOVEに設定します。 値は DoDragDrop によってソースに返されます。
- ターゲットは、データ オブジェクトの IDataObject::SetData メソッドも呼び出し、DROPEFFECT_MOVEに設定されたCFSTR_PERFORMEDDROPEFFECT形式識別子を渡します。 一部のドロップ ターゲットで DoDragDrop の pdwEffect パラメーターが正しく設定されない可能性があるため、このメソッド呼び出しが必要です。 CFSTR_PERFORMEDDROPEFFECT形式は、最適化されていない移動が行われたことを示す信頼性の高い方法です。
-
ソースは、ターゲットから返すことができる 2 つの値を検査します。 両方がDROPEFFECT_MOVEに設定されている場合は、元のデータを削除して最適化されていない移動を完了します。 それ以外の場合は、ターゲットが最適化された移動を行い、元のデータが削除されました。
貼り付け時の削除操作の処理
シナリオ: 1 つ以上のファイルが Windows エクスプローラーのフォルダーから切り取られ、名前空間の拡張機能に貼り付けられます。 Windows エクスプローラーは、貼り付け操作の結果に関するフィードバックを受け取るまで、ファイルを強調表示したのままにします。
従来、ユーザーがデータを切り取ると、すぐにビューから消えます。 これは効率的ではない可能性があり、ユーザーがデータに何が起こったかを心配すると、使いやすさの問題につながる可能性があります。 別の方法として、貼り付け時の削除操作を使用します。
貼り付け時の削除操作では、選択したデータがビューからすぐに削除されるわけではありません。 代わりに、ソース アプリケーションは、フォントまたは背景色を変更することによって、選択済みとしてマークします。 ターゲット アプリケーションは、データを貼り付けた後、操作の結果についてソースに通知します。 ターゲットが最適化された移動を実行した場合、ソースは単にその表示を更新できます。 ターゲットが通常の移動を実行した場合、ソースはデータのコピーも削除する必要があります。 貼り付けが失敗した場合、ソース アプリケーションは選択したデータを元の外観に復元します。
Note
通常、シェルは、ファイルの移動に切り取り/貼り付け操作を使用するときに、貼り付けの削除を使用します。 シェル オブジェクトでの貼り付け時の削除操作では、通常、最適化された移動を使用してファイルを移動します。 シェルのデータ転送を適切に処理するには、アプリケーションが貼り付け時の削除操作を検出して処理できる必要があります。
貼り付け時の削除の必須要件は、ターゲットが操作の結果をソースに報告する必要があるということです。 ただし、標準のクリップボード手法を使用して貼り付けの削除を実装することはできません。これは、ターゲットがソースと通信する方法を提供しないためです。 代わりに、ターゲット アプリケーションはデータ オブジェクトの IDataObject::SetData メソッドを使用して、結果をデータ オブジェクトに報告します。 その後、データ オブジェクトはプライベート インターフェイスを介してソースと通信できます。
貼り付け時の削除操作の基本的な手順は次のとおりです。
- ソースによって、選択したデータの画面表示がマークされます。
- ソースによってデータ オブジェクトが作成されます。 データ値が DROPEFFECT_MOVE の CFSTR_PREFERREDDROPEFFECT 形式を追加して、切り取り操作を示します。
- ソースは、OleSetClipboard を使用してクリップボードにデータ オブジェクトを配置します。
- ターゲットは、OleGetClipboard を使用してクリップボードからデータ オブジェクトを取得します。
- ターゲットは、CFSTR_PREFERREDDROPEFFECT データを抽出します。 DROPEFFECT_MOVEのみに設定されている場合、ターゲットは最適化された移動を行うか、単にデータをコピーできます。
- ターゲットが最適化された移動を行わない場合は、CFSTR_PERFORMEDDROPEFFECT形式を DROPEFFECT_MOVE に設定して IDataObject::SetData メソッドを呼び出します。
- 貼り付けが完了すると、ターゲットは IDataObject::SetData メソッドを呼び出し、CFSTR_PASTESUCCEEDED形式を DROPEFFECT_MOVE に設定します。
- CFSTR_PASTESUCCEEDED形式をDROPEFFECT_MOVEに設定してソースの IDataObject::SetData メソッドを呼び出す場合は、DROPEFFECT_MOVEに設定されたCFSTR_PERFORMEDDROPEFFECT形式も受け取ったかどうかを確認する必要があります。 両方の形式がターゲットによって送信される場合、ソースはデータを削除する必要があります。 CFSTR_PASTESUCCEEDED形式のみを受信した場合、ソースは単にその表示からデータを削除できます。 転送が失敗した場合、ソースは表示を元の外観に更新します。
仮想フォルダーとの間でデータを転送する
シナリオ: ユーザーがオブジェクトを仮想フォルダーからドラッグまたはドロップします。
仮想フォルダーには、通常はファイル システムに含まれていないオブジェクトが含まれています。 ごみ箱などの一部の仮想フォルダーは、ハード ディスクに格納されているデータを表すことができますが、通常のファイル システム オブジェクトとしては表されません。 一部のデータは、ハンドヘルド PC や FTP サイトなど、リモート システム上にある保存されたデータを表すことができます。 他のユーザー (Printers フォルダーなど) には、保存されているデータをまったく表さないオブジェクトが含まれています。 一部の仮想フォルダーはシステムの一部ですが、開発者は名前空間拡張機能を実装することで、カスタム仮想フォルダーを作成してインストールすることもできます。
データの種類や格納方法に関係なく、仮想フォルダーに含まれるフォルダーオブジェクトとファイル オブジェクトは、通常のファイルとフォルダーであるかのようにシェルによって表示されます。 仮想フォルダーに含まれるデータを取得し、シェルに適切に提示するのは、仮想フォルダーの役割です。 この要件は、通常、仮想フォルダーがドラッグ アンド ドロップとクリップボードのデータ転送をサポートすることを意味します。
したがって、仮想フォルダーとの間のデータ転送に関心を持つ必要がある開発者のグループは 2 つあります。
- アプリケーションが仮想フォルダーから転送されるデータを受け入れる必要がある開発者。
- 名前空間拡張機能がデータ転送を適切にサポートする必要がある開発者。
仮想フォルダーからのデータの受け入れ
仮想フォルダーは、事実上あらゆる種類のデータを表し、任意の方法でそのデータを格納できます。 一部の仮想フォルダーには、通常のファイル システム ファイルとフォルダーが実際に含まれている場合があります。 たとえば、すべてのオブジェクトを 1 つのドキュメントまたはデータベースにパックする場合があります。
ファイル システム オブジェクトがアプリケーションに転送されると、通常、データ オブジェクトには、オブジェクトの完全修飾パスを持つCF_HDROP形式が含まれます。 アプリケーションは、この文字列を抽出し、通常のファイル システム関数を使用してファイルを開き、そのデータを抽出できます。 ただし、通常、仮想フォルダーには通常のファイル システム オブジェクトが含まれていないため、通常はCF_HDROPを使用しません。
通常、データはCF_HDROPではなく、CFSTR_FILEDESCRIPTOR CFSTR_FILECONTENTS/形式の仮想フォルダーから転送されます。 CFSTR_FILECONTENTS形式には、CF_HDROPよりも 2 つの利点があります。
- データ ストレージの特定の方法は想定されません。
- この形式の方が柔軟性が高くなります。 グローバル メモリ オブジェクト、IStream インターフェイス、または IStorage インターフェイスの 3 つのデータ転送メカニズムがサポートされています。
グローバル メモリ オブジェクトは、データ全体をメモリにコピーする必要があるため、仮想オブジェクトとの間でデータを転送するために使用されることはほとんどありません。 インターフェイス ポインターを転送する場合、メモリはほとんど必要なく、はるかに効率的です。 非常に大きなファイルでは、インターフェイス ポインターが唯一の実用的なデータ転送メカニズムである可能性があります。 通常、データは IStream ポインターによって表されます。これは、そのインターフェイスが IStorage よりもやや柔軟であるためです。 ターゲットは、データ オブジェクトからポインターを抽出し、インターフェイス メソッドを使用してデータを抽出します。
CFSTR_FILEDESCRIPTOR CFSTR_FILECONTENTS形式の処理方法の詳細については、「CFSTR_FILECONTENTS/形式を使用してファイルからデータを抽出する」を参照してください。
NameSpace 拡張機能との間でのデータの転送
名前空間拡張機能を実装する場合、通常はドラッグ アンド ドロップ機能をサポートする必要があります。 一般的なガイドラインで説明されているドロップ ソースとターゲットの推奨事項に従います。 特に、名前空間拡張機能は次の必要があります。
- CFSTR_FILEDESCRIPTOR CFSTR_FILECONTENTS/形式を処理できる。 これらの 2 つの形式は、通常、名前空間拡張機能との間でオブジェクトを転送するために使用されます。
- 最適化された移動を処理できる。 シェルは、シェル オブジェクトが最適化された移動と共に移動されることを想定しています。
- 貼り付け時の削除操作を処理できる。 シェルは、オブジェクトが切り取り/貼り付け操作でシェルから移動されるときに、貼り付けの削除を使用します。
- IStream または IStorage インターフェイスを介してデータ転送を処理できる。 仮想フォルダー間のデータ転送は、通常、これら 2 つのインターフェイス ポインター (通常 は IStream ポインター) のいずれかを転送することによって処理されます。 その後、ターゲットはインターフェイス メソッドを呼び出してデータを抽出します。
-
- ドロップ ソースとして、名前空間拡張機能はストレージからデータを抽出し、このインターフェイスを介してターゲットに渡す必要があります。
- ドロップ ターゲットとして、名前空間拡張機能は、このインターフェイスを介してソースからのデータを受け入れ、適切に格納する必要があります。
-
ごみ箱にファイルをドロップする
シナリオ: ユーザーはごみ箱にファイルを ドロップします。 アプリケーションまたは名前空間の拡張機能によって元のファイルが削除されます。
ごみ箱は、不要になったファイルのリポジトリとして使用される仮想フォルダーです。 ごみ箱が空になっていない限り、ユーザーは後でファイルを回復してファイル システムに戻すことができます。
ほとんどの場合、シェル オブジェクトをごみ箱に転送することは、他のフォルダーとよく似ています。 ただし、ユーザーがごみ箱のファイルを削除すると、フォルダーからのフィードバックがコピー操作を示している場合でも、ソースは元のファイルを削除する必要があります。 通常、ドロップ ソースには、そのデータ オブジェクトがドロップされたフォルダーを知る方法はありません。 ただし、Windows 2000 以降のシステムでは、データ オブジェクトがごみ箱にドロップされると、シェルはデータ オブジェクトの IDataObject::SetData メソッドを呼び出し、CFSTR_TARGETCLSID形式をごみ箱のクラス識別子 (CLSID) (CLSID_RecycleBin) に設定します。 ごみ箱のケースを適切に処理するには、データ オブジェクトがこの形式を認識し、プライベート インターフェイスを介して情報をソースに伝達できる必要があります。
Note
CFSTR_TARGETCLSID形式を CLSID_RecycleBin に設定して IDataObject::SetData を呼び出すと、データ ソースは、メソッドから戻る前に、転送されるオブジェクトへの開いているハンドルをすべて閉じる必要があります。 そうしないと、共有違反が発生する可能性があります。
スクラップ ファイルの作成とインポート
シナリオ: ユーザーが OLE アプリケーションのデータ ファイルからデータをドラッグし、デスクトップまたは Windows エクスプローラーにドロップします。
Windows を使用すると、ユーザーは OLE アプリケーションのデータ ファイルからオブジェクトをドラッグし、デスクトップまたはファイル システム フォルダーにドロップできます。 この操作により、 データまたはデータへのリンクを含むスクラップ ファイルが作成されます。 ファイル名は、オブジェクト の CLSID およびCF_TEXT データに登録されている短い名前から取得されます。 シェルでデータを含むスクラップ ファイルを作成するには、アプリケーションの IDataObject インターフェイスでCF_EMBEDSOURCEクリップボード形式をサポートする必要があります。 リンクを含むファイルを作成するには、 IDataObject がCF_LINKSOURCE形式をサポートしている必要があります。
また、アプリケーションがスクラップ ファイルをサポートするために実装できる 3 つのオプション機能もあります。
- 往復サポート
- キャッシュされたデータ形式
- 遅延レンダリング
往復サポート
ラウンド トリップでは、データ オブジェクトを別のコンテナーに転送してから、元のドキュメントに戻します。 たとえば、ユーザーはスプレッドシートからデスクトップにセルのグループを転送し、データを含むスクラップ ファイルを作成できます。 その後、ユーザーがスクラップをスプレッドシートに転送する場合は、元の転送前と同様に、データをドキュメントに統合する必要があります。
シェルは、スクラップ ファイルを作成するときに、埋め込みオブジェクトとしてデータを表します。 スクラップが別のコンテナーに転送されると、元のドキュメントに戻される場合でも、埋め込みオブジェクトとして転送されます。 アプリケーションは、スクラップに含まれるデータ形式を決定し、必要に応じてデータをネイティブ形式に戻す必要があります。
埋め込みオブジェクトの形式を確立するには、オブジェクトのCF_OBJECTDESCRIPTOR形式を取得して CLSID を決定します。 CLSID がアプリケーションに属するデータ形式を示す場合は、OleCreateFromData を呼び出す代わりにネイティブ データを転送する必要があります。
キャッシュされたデータ形式
シェルは、スクラップ ファイルを作成するときに、レジストリで使用可能な形式の一覧を確認します。 既定では、CF_EMBEDSOURCEとCF_LINKSOURCEの 2 つの形式を使用できます。 ただし、アプリケーションでさまざまな形式のスクラップ ファイルが必要になるシナリオは多数あります。
- 埋め込みオブジェクト形式を受け入れることができない OLE 以外のコンテナーにスクラップを転送できるようにするため。
- アプリケーションスイートがプライベート形式と通信できるようにするため。
- ラウンド トリップを処理しやすくするため。
アプリケーションは、レジストリにキャッシュすることで、スクラップに形式を追加できます。 キャッシュ形式には、次の 2 種類があります。
- 優先順位キャッシュ形式。 これらの形式では、データ全体がデータ オブジェクトからスクラップにコピーされます。
- 遅延レンダリング形式。 これらの書式の場合、データオブジェクトはスクラップにコピーされません。 代わりに、ターゲットがデータを要求するまでレンダリングが遅れます。 遅延レンダリングについては、次のセクションで詳しく説明します。
優先順位キャッシュまたは遅延レンダリング形式を追加するには、データのソースであるアプリケーションの CLSID キーの下に DataFormat サブキーを作成します。 そのサブキーの下に 、PriorityCacheFormats または DelayRenderFormats サブキーを 作成します。 優先順位キャッシュまたは遅延レンダリング形式ごとに、0 から始まる番号付きサブキーを作成します。 このキーの値を、形式の登録済みの名前を持つ文字列または #X 値に設定します。X は標準のクリップボード形式の形式番号を表します。
次の例は、2 つのアプリケーションのキャッシュ形式を示しています。 MyProg1 アプリケーションには、優先キャッシュ形式としてリッチ テキスト形式、遅延レンダリング形式としてプライベート形式 "My Format" があります。 MyProg2 アプリケーションには、優先順位キャッシュ形式としてCF_BITMAP形式 (#8") があります。
HKEY_CLASSES_ROOT
CLSID
{GUID}
(Default) = MyProg1
DataFormats
PriorityCacheFormats
0
(Default) = Rich Text Format
DelayRenderFormats
0
(Default) = My Format
{GUID}
(Default) = MyProg2
DataFormats
PriorityCacheFormats
0
(Default) = #8
追加の形式を追加するには、番号付きサブキーを追加します。
遅延レンダリング
遅延レンダリング形式を使用すると、アプリケーションはスクラップ ファイルを作成できますが、ターゲットによって要求されるまでデータのレンダリングにかかる費用を遅延できます。 スクラップの IDataObject インターフェイスは、ネイティブおよびキャッシュされたデータと共に、遅延レンダリング形式をターゲットに提供します。 ターゲットが遅延レンダリング形式を要求した場合、シェルはアプリケーションを実行し、アクティブ オブジェクトからターゲットにデータを提供します。
Note
遅延レンダリングはやや危険であるため、注意して使用する必要があります。 サーバーが使用できない場合、または OLE 対応でないアプリケーションでは機能しません。
シェル オブジェクトの非同期的なドラッグ アンド ドロップ
シナリオ: ユーザーは、ソースからターゲットに大きなデータ ブロックを転送します。 両方のアプリケーションを長時間ブロックしないように、ターゲットはデータを非同期的に抽出します。
通常、ドラッグ アンド ドロップは同期操作です。 概要:
- ドロップ ソースは DoDragDrop を呼び出し、関数が戻るまでそのプライマリ スレッドをブロックします。 通常、プライマリ スレッドをブロックすると、UI 処理がブロックされます。
- ターゲットの IDropTarget::D rop メソッドが呼び出されると、ターゲットはプライマリ スレッド上のデータ オブジェクトからデータを抽出します。 通常、このプロシージャは、抽出プロセスの間、ターゲットの UI 処理をブロックします。
- データが抽出されると、ターゲットは IDropTarget::D rop 呼び出しを返し、システムは DoDragDrop を返し、両方のスレッドを続行できます。
つまり、同期データ転送では、両方のアプリケーションのプライマリ スレッドを長時間ブロックできます。 特に、ターゲットがデータを抽出するまで、両方のスレッドが待機する必要があります。 少量のデータの場合、データの抽出に必要な時間は少なく、同期データ転送は非常にうまく機能します。 ただし、大量のデータを同期的に抽出すると、長い遅延が発生し、ターゲットとソースの両方の UI に干渉する可能性があります。
IAsyncOperation/IDataObjectAsyncCapability インターフェイスは、データ オブジェクトによって実装できる省略可能なインターフェイスです。 ドロップ ターゲットは、バックグラウンド スレッドでデータ オブジェクトから非同期的にデータを抽出する機能を提供します。 データ抽出がバックグラウンド スレッドに渡されると、両方のアプリケーションのプライマリ スレッドは自由に続行できます。
IASyncOperation/IDataObjectAsyncCapability の使用
Note
インターフェイスはもともと IAsyncOperation という名前でしたが、これは後で IDataObjectAsyncCapability に変更されました。 それ以外の場合、2 つのインターフェイスは同じです。
IAsyncOperation/IDataObjectAsyncCapability の目的は、ドロップ ソースとドロップ ターゲットがデータを非同期的に抽出できるかどうかをネゴシエートできるようにすることです。 次の手順では、ドロップ ソースがインターフェイスを使用する方法について説明します。
- IAsyncOperation/IDataObjectAsyncCapability を公開するデータ オブジェクトを作成します。
- fDoOpAsync を VARIANT_TRUE に設定して SetAsyncMode を呼び出し、非同期操作がサポートされていることを示します。
- DoDragDrop が返されたら、InOperation を呼び出します。
- InOperation が失敗するか、VARIANT_FALSEを返した場合、通常の同期データ転送が行われ、データ抽出プロセスが完了します。 ソースは、必要なクリーンアップを行い、続行する必要があります。
- InOperation がVARIANT_TRUEを返す場合、データは非同期的に抽出されます。 クリーンアップ操作は EndOperation で処理する必要があります。
- データ オブジェクトを解放します。
- 非同期データ転送が完了すると、データ オブジェクトは通常、プライベート インターフェイスを介してソースに通知します。
次の手順では、ドロップ ターゲットが IAsyncOperation/IDataObjectAsyncCapability インターフェイスを使用してデータを非同期的に抽出する方法について説明します。
- システムが IDropTarget::D rop を呼び出すときは、IDataObject::QueryInterface を呼び出し、データ オブジェクトから IAsyncOperation/IDataObjectAsyncCapability インターフェイス (IID_IAsyncOperation/IID_IDataObjectAsyncCapability) を要求します。
- GetAsyncMode を呼び出 します。 メソッドがVARIANT_TRUEを返す場合、データ オブジェクトは非同期データ抽出をサポートします。
- データ抽出を処理し、StartOperation を呼び出す 別のスレッドを作成します。
- 通常の データ転送操作の場合と同様に、IDropTarget::D rop 呼び出しを返します。 DoDragDrop はドロップ ソースを返し、ブロックを解除します。 最適化された移動操作または貼り付け時の削除操作の結果を示すために IDataObject::SetData を呼び出さないでください。 操作が完了するまで待ちます。
- バックグラウンド スレッドでデータを抽出します。 ターゲットのプライマリ スレッドのブロックが解除され、自由に続行できます。
- データ転送が最適化された移動操作または貼り付け時の削除操作であった場合は、IDataObject::SetData を呼び出して結果を示します。
- EndOperation を呼び出 して抽出が完了したことをデータ オブジェクトに通知します。