シェル データ転送シナリオの処理

シェル データ オブジェクトに関するドキュメントでは、ドラッグ アンド ドロップまたはクリップボードを使用してシェル データを転送するために使用される一般的な方法について説明しました。 ただし、アプリケーションでシェル データ転送を実装するには、シェル データを転送するさまざまな方法にこれらの一般的な原則と手法を適用する方法も理解する必要があります。 このドキュメントでは、一般的なシェル データ転送シナリオについて説明し、アプリケーションでそれぞれを実装する方法について説明します。

注意

これらの各シナリオでは特定のデータ転送操作について説明しますが、その多くはさまざまな関連シナリオに適用されます。 たとえば、ほとんどのクリップボード転送とドラッグ アンド ドロップ転送の主な違いは、データ オブジェクトがターゲットに到着する方法です。 ターゲットがデータ オブジェクトの IDataObject インターフェイスへのポインターを持つ場合、情報を抽出する手順は、両方の種類のデータ転送でほとんど同じです。 ただし、一部のシナリオは特定の種類の操作に制限されています。 詳細については、個々のシナリオを参照してください。

 

一般的なガイドライン

以下の各セクションでは、かなり具体的な 1 つのデータ転送シナリオについて説明します。 ただし、データ転送は多くの場合、より複雑であり、いくつかのシナリオの側面を伴う場合があります。 通常、どのシナリオを実際に処理する必要があるかわからない場合があります。 注意すべき一般的なガイドラインをいくつか次に示します。

データ ソースの場合:

  • シェル クリップボード形式は、 CF_HDROPを除き、事前に定義されていません。 使用する各形式は、 RegisterClipboardFormat を呼び出して登録する必要があります。
  • データ オブジェクトの形式は、ソースからの優先順に提供されます。 データ オブジェクトを列挙し、使用できる最初のオブジェクトを選択します。
  • サポートできる限り多くの形式を含めます。 通常、データ オブジェクトが削除される場所はわかりません。 この方法により、ドロップ ターゲットで受け入れられる形式がデータ オブジェクトに含まれる確率が向上します。
  • 既存のファイルは 、CF_HDROP 形式で提供する必要があります。
  • CFSTR_FILECONTENTS CFSTR_FILEDESCRIPTOR/形式のファイルに似たデータ提供します。 この方法により、ターゲットは、基になるデータ ストレージについて何も知らなくても、データ オブジェクトからファイルを作成できます。 通常、データは IStream インターフェイスとして表示する必要があります。 このデータ転送メカニズムは、グローバル メモリ オブジェクトよりも柔軟性が高く、はるかに少ないメモリを使用します。
  • ドラッグ ソースは、シェル項目をドラッグするときに CFSTR_SHELLIDLIST 形式を提供する必要があります。 項目のデータ オブジェクトは、 IShellFolder::GetUIObjectOf メソッドまたは IShellItem::BindToHandler メソッドを使用して取得できます。 データ ソースは、SHCreateDataObject を使用して、CFSTR_SHELLIDLIST形式をサポートする標準のデータ オブジェクト実装を作成できます。
  • シェル項目プログラミング モデルを使用してドラッグされる項目について推論するターゲットをドロップすると、SHCreateShellItemArrayFromDataObject を使用して IDataObjectIShellItemArray に変換できます。
  • 標準のフィードバック カーソルを使用します。
  • 左右のドラッグをサポートします。
  • 埋め込みオブジェクトからデータ オブジェクト自体を使用します。 この方法により、アプリケーションは、データ オブジェクトが提供する必要がある追加の形式を取得でき、追加の包含レイヤーが作成されるのを回避できます。 たとえば、サーバー 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 インターフェイスを実装して、ストレージからのデータをファイルとして表示します。
  • シェルでは、ファイルの 移動時に最適化された移動 操作または 貼り付け時の削除 操作が使用される場合があることに注意してください。 アプリケーションは、これらの操作を認識し、適切に応答できる必要があります。

クリップボードからアプリケーションへのファイル名のコピー

シナリオ:ユーザーが Windows エクスプローラーで 1 つ以上のファイルを選択し、クリップボードにコピーします。 アプリケーションによってファイル名が抽出され、ドキュメントに貼り付けられます。

たとえば、このシナリオを使用すると、ファイルを切り取ってアプリケーションに貼り付けることで、ユーザーが HTML リンクを作成できます。 アプリケーションでは、データ オブジェクトからファイル名を抽出し、それを処理してアンカー タグを作成できます。

ユーザーが Windows エクスプローラーでファイルを選択し、クリップボードにコピーすると、シェルによってデータ オブジェクトが作成されます。 次に 、OleSetClipboard を呼び出して、データ オブジェクトの IDataObject インターフェイスへのポインターをクリップボードに配置します。

ユーザーがアプリケーションのメニューまたはツール バーから [貼り付け] コマンドを選択すると、次の操作が行われます。

  1. OleGetClipboard を呼び出して、データ オブジェクトの IDataObject インターフェイスを取得します。
  2. IDataObject::EnumFormatEtc を呼び出して列挙子オブジェクトを要求します。
  3. 列挙子オブジェクトの IEnumFORMATETC インターフェイスを使用して、データ オブジェクトに含まれる形式を列挙します。

注意

この手順の最後の 2 つの手順は、完全性のために含まれています。 通常、単純なファイル転送には必要ありません。 この種類のデータ転送に使用されるすべてのデータ オブジェクトには 、オブジェクト に含まれるファイルの名前を決定するために使用できるCF_HDROP形式が含まれている必要があります。 ただし、一般的なデータ転送の場合は、形式を列挙し、アプリケーションで処理できる最適な形式を選択する必要があります。

 

データ オブジェクトからファイル名を抽出する

次の手順では、データ オブジェクトから 1 つ以上のファイル名を抽出し、アプリケーションに貼り付けます。 このセクションで説明するデータ オブジェクトからファイル名を抽出する手順は、ドラッグ アンド ドロップ転送にも同様に適用されることに注意してください。

データ オブジェクトからファイル名を取得する最も簡単な方法は、 CF_HDROP 形式です。

  1. IDataObject::GetData を呼び出します。 FORMATETC 構造体の cfFormat メンバーを CF_HDROP に設定し、tymed メンバーを TYMED_HGLOBAL に設定します。 dwAspect メンバーは通常、DVASPECT_CONTENTに設定されます。 ただし、ファイルのパスを短い (8.3) 形式にする必要がある場合は、 dwAspect を DVASPECT_SHORT に設定します。

    IDataObject::GetData が返されると、STGMEDIUM 構造体の hGlobal メンバーは、データを含むグローバル メモリ オブジェクトを指します。

  2. HDROP 変数を作成し、STGMEDIUM 構造体の hGlobal メンバーに設定します。 HDROP 変数は 、DROPFILES 構造体へのハンドルの後に、コピーされたファイルの完全修飾ファイル パスを含む 2 つの null で終わる文字列になりました。

  3. iFile パラメーターを 0xFFFFFFFF に設定して DragQueryFile を呼び出して、リスト内のファイル パスの数を確認します。 関数は、リスト内のファイル パスの数を返します。 この一覧のファイル パスの 0 から始まるインデックスは、次の手順で特定のパスを識別するために使用されます。

  4. ファイルごとに DragQueryFile を 1 回呼び出し、 iFile をファイルのインデックスに設定して、グローバル メモリ オブジェクトからファイル パスを抽出します。

  5. 必要に応じてファイル パスを処理し、アプリケーションに貼り付けます。

  6. ReleaseStgMedium を呼び出し、手順 1 で IDataObject::GetData に渡した STGMEDIUM 構造体へのポインターを渡します。 構造体を解放すると、手順 2 で作成した HDROP 値は無効になり、使用しないでください。

ドロップされたファイルの内容をアプリケーションにコピーする

シナリオ:ユーザーが Windows エクスプローラーから 1 つ以上のファイルをドラッグし、アプリケーションのウィンドウにドロップします。 アプリケーションがファイルの内容を抽出し、アプリケーションに貼り付けます。

このシナリオでは、ドラッグ アンド ドロップを使用して、Windows エクスプローラーからアプリケーションにファイルを転送します。 操作の前に、アプリケーションで次の操作を行う必要があります。

  1. RegisterClipboardFormat を呼び出して、必要なシェル クリップボード形式を登録します。
  2. RegisterDragDrop を呼び出して、ターゲット ウィンドウとアプリケーションの IDropTarget インターフェイスを登録します。

ユーザーが操作を開始した後、1 つ以上のファイルを選択してドラッグを開始します。

  1. Windows エクスプローラーでは、データ オブジェクトが作成され、サポートされている形式が読み込まれます。
  2. Windows エクスプローラーは DoDragDrop を呼び出してドラッグ ループを開始します。
  3. ドラッグ イメージがターゲット ウィンドウに到達すると、 IDropTarget::D ragEnter を呼び出して通知されます。
  4. データ オブジェクトに含まれる内容を確認するには、データ オブジェクトの IDataObject::EnumFormatEtc メソッドを 呼び出します。 データ オブジェクトに含まれる形式を列挙するには、 メソッドによって返される列挙子オブジェクトを使用します。 アプリケーションでこれらの形式を受け入れたくない場合は、DROPEFFECT_NONEを返します。 このシナリオでは、アプリケーションでは、ファイルの転送に使用される形式 ( CF_HDROPなど) を含まないデータ オブジェクトは無視する必要があります。
  5. ユーザーがデータを削除すると、システムは IDropTarget::D rop を呼び出します。
  6. IDataObject インターフェイスを使用して、ファイルの内容を抽出します。

データ オブジェクトから Shell オブジェクトの内容を抽出するには、いくつかの方法があります。 一般に、次の順序を使用します。

  • ファイルに 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_ISTREAMIStream インターフェイス ポインターを転送する場合は、データをグローバル メモリ オブジェクトに読み込むよりもはるかに少ないメモリが必要であり、IStreamIStorage よりもデータを表す簡単な方法です。

CFSTR_FILECONTENTS形式には、常にCFSTR_FILEDESCRIPTOR形式が伴います。 この形式の内容を最初に確認する必要があります。 複数のファイルが転送されている場合、データ オブジェクトには実際には複数 のCFSTR_FILECONTENTS 形式 (ファイルごとに 1 つ) が含まれます。 CFSTR_FILEDESCRIPTOR形式には、各ファイルの名前と属性が含まれており、特定のファイルのCFSTR_FILECONTENTS形式を抽出するために必要な各ファイルのインデックス値が提供されます。

CFSTR_FILECONTENTS形式を抽出するには:

  1. CFSTR_FILEDESCRIPTOR形式をTYMED_HGLOBAL値として抽出します。
  2. 返された STGMEDIUM 構造体の hGlobal メンバーは、グローバル メモリ オブジェクトを指します。 hGlobal 値を GlobalLock に渡して、そのオブジェクトをロックします
  3. GlobalLock によって返されたポインターを FILEGROUPDESCRIPTOR ポインターにキャストします。 FILEGROUPDESCRIPTOR 構造体の後に 1 つ以上の FILEDESCRIPTOR 構造体が続きます。 各 FILEDESCRIPTOR 構造体には、付随する CFSTR_FILECONTENTS 形式の 1 つに含まれるファイルの説明が含まれています。
  4. FILEDESCRIPTOR 構造体を調べて、抽出するファイルに対応するものを確認します。 その FILEDESCRIPTOR 構造体の 0 から始まるインデックスは、ファイルの CFSTR_FILECONTENTS 形式を識別するために使用されます。 グローバル メモリ ブロックのサイズはバイト精度ではないので、構造体の nFileSizeLow メンバーと nFileSizeHigh メンバーを使用して、グローバル メモリ オブジェクト内のファイルを表すバイト数を確認します。
  5. FORMATETC 構造体の cfFormat メンバーをCFSTR_FILECONTENTS値に設定し、lIndex メンバーを前の手順で決定したインデックスに設定して、IDataObject::GetData を呼び出します。 通常、 tymed メンバーは TYMED_HGLOBAL | に設定されます。TYMED_ISTREAM |TYMED_ISTORAGE。 その後、データ オブジェクトは、推奨されるデータ転送メカニズムを選択できます。
  6. IDataObject::GetData が返す STGMEDIUM 構造体には、ファイルのデータへのポインターが含まれます。 構造体の 型指定された メンバーを調べて、データ転送メカニズムを決定します。
  7. tymedTYMED_ISTREAM または TYMED_ISTORAGE に設定されている場合は、 インターフェイスを使用してデータを抽出します。 tymed が TYMED_HGLOBAL に設定されている場合、データはグローバル メモリ オブジェクトに格納されます。 グローバル メモリ オブジェクトからデータを抽出する方法については、「シェル データ オブジェクト」の「データ オブジェクトからグローバル メモリ オブジェクトを抽出する」セクションを参照してください。
  8. GlobalLock を呼び出して、手順 2 でロックしたグローバル メモリ オブジェクトのロックを解除します。

最適化された移動操作の処理

シナリオ: ファイルは、最適化された移動を使用して、ファイル システムから名前空間拡張機能に移動されます。

従来の移動操作では、ターゲットはデータのコピーを作成し、ソースは元のデータを削除します。 この手順は、データの 2 つのコピーを必要とするため、非効率的な場合があります。 データベースなどの大きなオブジェクトでは、従来の移動操作は実用的でない場合もあります。

最適化された移動では、ターゲットはデータの格納方法を理解して移動操作全体を処理します。 データの 2 つ目のコピーは存在せず、ソースが元のデータを削除する必要はありません。 シェル データは、ターゲットがシェル API を使用して操作全体を処理できるため、最適化された移動に適しています。 一般的な例は、ファイルの移動です。 ターゲットが移動するファイルのパスを取得すると、 SHFileOperation を使用して移動できます。 ソースが元のファイルを削除する必要はありません。

注意

シェルは通常、最適化された移動を使用してファイルを移動します。 シェルデータ転送を適切に処理するには、アプリケーションが最適化された移動を検出して処理できる必要があります。

 

最適化された移動は、次の方法で処理されます。

  1. ソースは DoDragDrop を呼び出し、 dwEffect パラメーターを DROPEFFECT_MOVE に設定して、ソース オブジェクトを移動できることを示します。

  2. ターゲットは、移動が許可されていることを示す IDropTarget メソッドの 1 つを介してDROPEFFECT_MOVE値を受け取ります。

  3. ターゲットは、オブジェクトをコピーするか (最適化されていない移動)、またはオブジェクトを移動します (最適化された移動)。

  4. ターゲットは、元のデータを削除する必要があるかどうかをソースに通知します。

    最適化された移動は、ターゲットによって削除されたデータを含む既定の操作です。 最適化された移動が実行されたことをソースに通知するには、

      • ターゲットは、IDropTarget::D rop メソッドを介して受け取った pdwEffect 値を、DROPEFFECT_MOVE以外の値に設定します。 通常、DROPEFFECT_NONEまたはDROPEFFECT_COPYに設定されます。 値は DoDragDrop によってソースに返されます。
      • ターゲットは、データ オブジェクトの IDataObject::SetData メソッドも呼び出し、DROPEFFECT_NONEに設定 されたCFSTR_PERFORMEDDROPEFFECT 形式識別子を渡します。 一部のドロップ ターゲットで DoDragDroppdwEffect パラメーターが正しく設定されない可能性があるため、このメソッド呼び出しが必要です。 CFSTR_PERFORMEDDROPEFFECT形式は、最適化された移動が行われたことを示す信頼性の高い方法です。

    ターゲットが最適化されていない移動を行った場合は、ソースによってデータを削除する必要があります。 最適化されていない移動が実行されたことをソースに通知するには:

      • ターゲットは、IDropTarget::D rop メソッドで受け取った pdwEffect 値をDROPEFFECT_MOVEに設定します。 値は DoDragDrop によってソースに返されます。
      • ターゲットは、データ オブジェクトの IDataObject::SetData メソッドも呼び出し、DROPEFFECT_MOVEに設定 されたCFSTR_PERFORMEDDROPEFFECT 形式識別子を渡します。 一部のドロップ ターゲットで DoDragDroppdwEffect パラメーターが正しく設定されない可能性があるため、このメソッド呼び出しが必要です。 CFSTR_PERFORMEDDROPEFFECT形式は、最適化されていない移動が行われたことを示す信頼性の高い方法です。
  5. ソースは、ターゲットから返すことができる 2 つの値を検査します。 両方がDROPEFFECT_MOVEに設定されている場合は、元のデータを削除して最適化されていない移動を完了します。 それ以外の場合、ターゲットは最適化された移動を行い、元のデータが削除されました。

貼り付け時の削除操作の処理

シナリオ:1 つ以上のファイルが Windows エクスプローラーのフォルダーから切り取られ、名前空間拡張機能に貼り付けられます。 Windows エクスプローラーは、貼り付け操作の結果に関するフィードバックを受け取るまで、ファイルが強調表示されたのままにします。

従来、ユーザーがデータを切り取ると、すぐにビューから消えます。 これは効率的ではない可能性があり、ユーザーがデータに何が起こったかを心配すると、使いやすさの問題につながる可能性があります。 もう 1 つの方法は、貼り付け時の削除操作を使用することです。

貼り付け時の削除操作では、選択したデータがビューからすぐに削除されるわけではありません。 代わりに、ソース アプリケーションは、フォントまたは背景色を変更することによって、選択済みとしてマークします。 ターゲット アプリケーションは、データを貼り付けた後、操作の結果についてソースに通知します。 ターゲットが 最適化された移動を実行した場合、ソースは単にその表示を更新できます。 ターゲットが通常の移動を実行した場合、ソースはデータのコピーも削除する必要があります。 貼り付けが失敗した場合、ソース アプリケーションは選択したデータを元の外観に復元します。

注意

シェルは通常、ファイルの移動に切り取り/貼り付け操作を使用する場合に、貼り付けの削除を使用します。 シェル オブジェクトを使用した貼り付け時の削除操作では、通常、 最適化された移動 を使用してファイルを移動します。 シェルのデータ転送を適切に処理するには、アプリケーションが貼り付け時の削除操作を検出して処理できる必要があります。

 

貼り付け時の削除の必須要件は、ターゲットが操作の結果をソースに報告する必要があるということです。 ただし、標準のクリップボード手法は、ターゲットがソースと通信する方法を提供しないため、貼り付けの削除を実装するために使用できません。 代わりに、ターゲット アプリケーションはデータ オブジェクトの IDataObject::SetData メソッドを使用して、結果をデータ オブジェクトに報告します。 データ オブジェクトは、プライベート インターフェイスを介してソースと通信できます。

貼り付け時の削除操作の基本的な手順は次のとおりです。

  1. ソースによって、選択したデータの画面表示がマークされます。
  2. ソースによってデータ オブジェクトが作成されます。 これは、データ値が DROPEFFECT_MOVE のCFSTR_PREFERREDDROPEFFECT 形式を追加して、切り取り操作を示します。
  3. ソースは 、OleSetClipboard を使用してデータ オブジェクトをクリップボードに配置します。
  4. ターゲットは、 OleGetClipboard を使用してクリップボードからデータ オブジェクトを取得します。
  5. ターゲットは 、CFSTR_PREFERREDDROPEFFECT データを抽出します。 DROPEFFECT_MOVEのみに設定されている場合、ターゲットは最適化された移動を行うか、単にデータをコピーすることができます。
  6. ターゲットが最適化された移動を行わない場合は、CFSTR_PERFORMEDDROPEFFECT形式を DROPEFFECT_MOVE に設定して IDataObject::SetData メソッドを呼び出します。
  7. 貼り付けが完了すると、ターゲットは IDataObject::SetData メソッドを呼び出し、 CFSTR_PASTESUCCEEDED 形式を DROPEFFECT_MOVE に設定します。
  8. 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) に設定します。 ごみ箱のケースを適切に処理するには、データ オブジェクトがこの形式を認識し、プライベート インターフェイスを介してソースに情報を通信できる必要があります。

注意

IDataObject::SetData が CLSID_RecycleBin に設定されたCFSTR_TARGETCLSID形式で呼び出されると、データ ソースはメソッドから戻る前に、転送されるオブジェクトへの開いているハンドルを閉じる必要があります。 それ以外の場合は、共有違反を作成する可能性があります。

 

スクラップ ファイルの作成とインポート

シナリオ:ユーザーが 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 インターフェイスでは、遅延レンダリング形式がターゲットに提供され、ネイティブデータとキャッシュデータが提供されます。 ターゲットが遅延レンダリング形式を要求した場合、シェルはアプリケーションを実行し、アクティブ オブジェクトからターゲットにデータを提供します。

注意

遅延レンダリングはやや危険であるため、注意して使用する必要があります。 サーバーが使用できない場合、または OLE 対応でないアプリケーションでは機能しません。

 

シェル オブジェクトの非同期的なドラッグ アンド ドロップ

シナリオ: ユーザーは、ソースからターゲットに大きなデータ ブロックを転送します。 両方のアプリケーションを長時間ブロックしないように、ターゲットは非同期的にデータを抽出します。

通常、ドラッグ アンド ドロップは同期操作です。 概要:

  1. ドロップ ソースは DoDragDrop を 呼び出し、関数が戻るまでプライマリ スレッドをブロックします。 プライマリ スレッドをブロックすると、通常は UI 処理がブロックされます。
  2. ターゲットの IDropTarget::D rop メソッドが呼び出されると、ターゲットはプライマリ スレッド上のデータ オブジェクトからデータを抽出します。 通常、このプロシージャは、抽出プロセスの間、ターゲットの UI 処理をブロックします。
  3. データが抽出されると、ターゲットは IDropTarget::D rop 呼び出しを返し、システムは DoDragDrop を返し、両方のスレッドを続行できます。

つまり、同期データ転送では、両方のアプリケーションのプライマリ スレッドを大幅にブロックできます。 特に、ターゲットがデータを抽出する間、両方のスレッドが待機する必要があります。 少量のデータの場合、データの抽出に必要な時間は少なく、同期データ転送は非常に適切に機能します。 ただし、大量のデータを同期的に抽出すると、長い遅延が発生し、ターゲットとソースの両方の UI に干渉する可能性があります。

IAsyncOperation/IDataObjectAsyncCapability インターフェイスは、データ オブジェクトによって実装できる省略可能なインターフェイスです。 ドロップ ターゲットは、バックグラウンド スレッドでデータ オブジェクトから非同期的にデータを抽出する機能を提供します。 データ抽出がバックグラウンド スレッドに渡されると、両方のアプリケーションのプライマリ スレッドは自由に続行できます。

IASyncOperation/IDataObjectAsyncCapability の使用

注意

インターフェイスはもともと IAsyncOperation という名前でしたが、後で IDataObjectAsyncCapability に変更されました。 それ以外の場合、2 つのインターフェイスは同じです。

 

IAsyncOperation/IDataObjectAsyncCapability の目的は、ドロップ ソースとドロップ ターゲットがデータを非同期的に抽出できるかどうかをネゴシエートできるようにすることです。 次の手順では、ドロップ ソースがインターフェイスを使用する方法について説明します。

  1. IAsyncOperation/IDataObjectAsyncCapability を公開するデータ オブジェクトを作成します。
  2. fDoOpAsyncVARIANT_TRUE に設定して SetAsyncMode を呼び出して、非同期操作がサポートされていることを示します。
  3. DoDragDrop が返されたら、InOperation を呼び出します。
    • InOperation が失敗するか、VARIANT_FALSEを返した場合、通常の同期データ転送が実行され、データ抽出プロセスが完了します。 ソースは、必要なクリーンアップを行い、続行する必要があります。
    • InOperation がVARIANT_TRUEを返す場合、データは非同期的に抽出されます。 クリーンアップ操作は EndOperation によって処理する必要があります。
  4. データ オブジェクトを解放します。
  5. 非同期データ転送が完了すると、データ オブジェクトは通常、プライベート インターフェイスを介してソースに通知します。

次の手順では、ドロップ ターゲットが IAsyncOperation/IDataObjectAsyncCapability インターフェイスを使用してデータを非同期的に抽出する方法について説明します。

  1. システムが IDropTarget::D rop を呼び出すときは、 IDataObject::QueryInterface を呼び出し、データ オブジェクトから IAsyncOperation/IDataObjectAsyncCapability インターフェイス (IID_IAsyncOperation/IID_IDataObjectAsyncCapability) を要求します。
  2. GetAsyncMode を呼び出します。 メソッドが VARIANT_TRUEを返す場合、データ オブジェクトは非同期データ抽出をサポートします。
  3. データ抽出を処理し、 StartOperation を呼び出す別のスレッドを作成します。
  4. 通常のデータ転送操作と同様に、 IDropTarget::D rop 呼び出しを返します。 DoDragDrop はドロップ ソースを返し、ブロックを解除します。 最適化された移動操作または貼り付け時の削除操作の結果を示すために 、IDataObject::SetData を呼び出さないでください。 操作が完了するまで待ちます。
  5. バックグラウンド スレッドでデータを抽出します。 ターゲットのプライマリ スレッドのブロックが解除され、自由に続行できます。
  6. データ転送が 最適化された移動 操作または 貼り付け時の削除 操作だった場合は、 IDataObject::SetData を呼び出して結果を示します。
  7. EndOperation を呼び出して抽出が完了したことをデータ オブジェクトに通知します。