シェル データ オブジェクト

データ オブジェクトは、すべてのシェル データ転送の中心です。 これは主に、転送されたデータを保持するコンテナーです。 ただし、ターゲットはデータ オブジェクトと通信して、最適化された移動などの特殊な種類のシェル データ転送を容易にすることもできます。 このトピックでは、Shell データ オブジェクトの動作、それらがソースによってどのように構築されるか、およびターゲットによってどのように処理されるかについて一般的に説明します。 データ オブジェクトを使用してさまざまな種類のシェル データを転送する方法の詳細については、「 シェル データ転送シナリオの処理」を参照してください。

データ オブジェクトのしくみ

データ オブジェクトは、ターゲットにデータを転送するためにデータ ソースによって作成されたコンポーネント オブジェクト モデル (COM) オブジェクトです。 通常、これらは複数の項目のデータを保持します。 この方法には、次の 2 つの理由があります。

  • データ オブジェクトを使用してほぼすべての種類のデータを転送できますが、ソースは通常、ターゲットが受け入れ可能なデータの種類を認識しません。 たとえば、データは書式設定されたテキスト ドキュメントの一部である可能性があります。 ターゲットは複雑な書式設定情報を処理できる場合もありますが、ANSI テキストのみを受け入れることもできます。 このため、データ オブジェクトには、同じデータが複数の異なる形式で含まれることがよくあります。 その後、ターゲットは、処理できる形式でデータを抽出できます。
  • データ オブジェクトには、ソース データのバージョンではない補助データ項目を含めることもできます。 この種類のデータ項目は、通常、データ転送操作に関する追加情報を提供します。 たとえば、シェルは補助データ項目を使用して、ファイルをコピーするか移動するかを示します。

クリップボードの形式

データ オブジェクト内のデータの各項目には、通常はクリップボード形式と呼ばれる形式が関連付 けられています。 Winuser.h で宣言されている標準のクリップボード形式は、一般的に使用されるデータ型に対応しています。 クリップボード形式は整数ですが、通常は、CF_XXX という形式の同等の名前で参照されます。 たとえば、ANSI テキストのクリップボード形式はCF_TEXT。

アプリケーションでは、プライベート形式を定義することで、使用可能なクリップボード形式の範囲を拡張できます。 プライベート形式を定義するために、アプリケーションは形式を識別する文字列を使用して RegisterClipboardFormat を呼び出します。 関数から返される符号なし整数は、標準のクリップボード形式と同様に使用できる有効な書式値です。 ただし、使用するには、ソースとターゲットの両方で形式を登録する必要があります。 1 つの例外 (CF_HDROP) では、シェル データの転送に使用されるクリップボード形式はプライベート形式として定義されます。 使用するには、ソースとターゲットによって登録する必要があります。 使用可能なシェル クリップボード形式の説明については、「シェル クリップボード形式」を参照してください。

一部の例外がありますが、通常、データ オブジェクトには、サポートされているクリップボード形式ごとに 1 つのデータ項目のみが含まれます。 この形式とデータの間の 1 対 1 の関連付けにより、関連付けられたデータ項目の識別子として書式値を使用できます。 実際、データ オブジェクトの内容について説明する場合、データの特定の項目は通常、"形式" と呼ばれ、その形式名で参照されます。 たとえば、「CF_TEXT形式を抽出する...」などの語句です。は通常、データ オブジェクトの ANSI テキスト データ項目について説明するときに使用されます。

ドロップ ターゲットがデータ オブジェクトへのポインターを受け取ると、ドロップ ターゲットは使用可能な形式を列挙して、使用可能なデータの種類を決定します。 次に、使用可能な形式の 1 つ以上を要求し、データを抽出します。 ターゲットがデータ オブジェクトからシェル データを抽出する具体的な方法は、形式によって異なります。これについては、「 ターゲットがデータ オブジェクトを処理する方法」で詳しく説明されています。

単純なクリップボード データ転送では、データはグローバル メモリ オブジェクトに配置されます。 そのオブジェクトのアドレスは、その形式と共にクリップボードに配置されます。 クリップボード形式は、関連付けられているアドレスで検索されるデータの種類をターゲットに通知します。 簡単なクリップボード転送は実装が簡単ですが、

  • データ オブジェクトは、データを転送するはるかに柔軟な方法を提供します。
  • データ オブジェクトは、大量のデータを転送する場合に適しています。
  • データ オブジェクトは、ドラッグ アンド ドロップ操作でデータを転送するために使用する必要があります。

これらの理由から、すべてのシェル データ転送ではデータ オブジェクトが使用されます。 データ オブジェクトでは、クリップボード形式は直接使用されません。 代わりに、データ項目はクリップボード形式の一般化 ( FORMATETC 構造体) で識別されます。

FORMATETC 構造体

FORMATETC 構造体は、クリップボード形式の拡張バージョンです。 シェル データ転送に使用される FORMATETC 構造体には、次の特性があります。

  • データ項目は、引き続き cfFormat メンバーのクリップボード形式で識別されます。

  • データ転送は、グローバル メモリ オブジェクトに限定されません。 タイメド・メンバーは、関連する STGMEDIUM 構造体に含まれるデータ転送メカニズムを示すために使用されます。 TYMED_XXX値のいずれかに設定されます。

  • Shell は、CFSTR_FILECONTENTS形式の lIndex メンバーを使用して、データ オブジェクトに 1 つの形式ごとに複数のデータ項目を格納できるようにします。 この形式の使用方法については、「シェル データ転送シナリオの処理」の「CFSTR_FILECONTENTS形式を使用してファイルからデータを抽出する」セクションを参照してください。

  • dwAspect メンバーは通常、DVASPECT_CONTENTに設定されます。 ただし、Shlobj.h には、シェル データ転送に使用できる 3 つの値が定義されています。

    説明
    DVASPECT_COPY 形式がデータのコピーを表していることを示すために使用されます。
    DVASPECT_LINK 形式がデータへのショートカットを表していることを示すために使用します。
    DVASPECT_SHORTNAME CF_HDROP形式で使用され、名前が 8.3 形式に短縮されたファイル パスを要求します。

     

  • ptd メンバーはシェルのデータ転送には使用されず、通常は NULL に設定されます。

STGMEDIUM 構造体

STGMEDIUM 構造体は、転送されるデータへのアクセスを提供します。 シェル データでは、次の 3 つのデータ転送メカニズムがサポートされています。

  • グローバル メモリ オブジェクト。
  • IStream インターフェイス。
  • IStorage インターフェイス。

STGMEDIUM 構造体の tymed メンバーは、データ転送メカニズムを識別するTYMED_XXX値です。 2 番目のメンバーは、データを抽出するためにターゲットによって使用されるポインターです。 ポインターには、 に応じて、さまざまな型のいずれかを指定できます。 シェルのデータ転送に使用される 3 つの型指定 された 値を、対応する STGMEDIUM メンバー名と共に次の表にまとめます。

tymed 値 メンバー名 説明
TYMED_HGLOBAL hGlobal グローバル メモリ オブジェクトへのポインター。 このポインター型は、通常、少量のデータを転送するために使用されます。 たとえば、シェルはグローバル メモリ オブジェクトを使用して、ファイル名や URL などの短いテキスト文字列を転送します。
TYMED_ISTREAM pstm IStream インターフェイスへのポインター。 このポインター型は、ほとんどのシェル データ転送に適しています。これは、TYMED_HGLOBALに比べてメモリが比較的少ないためです。 また、TYMED_ISTREAMデータ転送メカニズムでは、ソースがそのデータを特定の方法で格納する必要はありません。
TYMED_ISTORAGE pstg IStorage インターフェイスへのポインター。 ターゲットはインターフェイス メソッドを呼び出してデータを抽出します。 TYMED_ISTREAMと同様に、このポインター型は比較的少ないメモリを必要とします。 ただし、TYMED_ISTORAGEはTYMED_ISTREAMよりも柔軟性が低いため、一般的には使用されません。

 

ソースがデータ オブジェクトを作成する方法

ユーザーがシェル データ転送を開始すると、ソースはデータ オブジェクトを作成し、データと共に読み込む役割を担います。 次の手順では、プロセスの概要を示します。

  1. RegisterClipboardFormat を呼び出して、データ オブジェクトに含まれるシェル形式ごとに有効なクリップボード形式の値を取得します。 CF_HDROPは既に有効なクリップボード形式であり、登録する必要はありません。
  2. 転送する各形式について、関連付けられたデータをグローバル メモリ オブジェクトに配置するか、 IStream または IStorage インターフェイスを介してそのデータへのアクセスを提供するオブジェクトを作成します。 IStream インターフェイスと IStorage インターフェイスは、標準の COM 手法を使用して作成されます。 グローバル メモリ オブジェクトの処理方法については、「データ オブジェクト にグローバル メモリ オブジェクトを追加する方法」を参照してください。
  3. 書式ごとに FORMATETC および STGMEDIUM 構造体を作成します。
  4. データ オブジェクトをインスタンス化します。
  5. サポートされている各形式の IDataObject::SetData メソッドを呼び出し、形式の FORMATETC および STGMEDIUM 構造体を渡して、データ オブジェクトにデータを読み込みます。
  6. クリップボードのデータ転送では、 OleSetClipboard を呼び出して、クリップボード上のデータ オブジェクトの IDataObject インターフェイスへのポインターを配置します。 ドラッグ アンド ドロップ転送の場合は、DoDragDrop を呼び出してドラッグ ループを開始します。 IDataObject ポインターは、データがドロップされるとドロップ ターゲットに渡され、ドラッグ ループが終了します。

これで、データ オブジェクトをターゲットに転送する準備が整いました。 クリップボードデータ転送の場合、オブジェクトは OleGetClipboard を呼び出してターゲットが要求するまで保持されます。 データ転送をドラッグ アンド ドロップする場合、データ オブジェクトは、データを表すアイコンを作成し、ユーザーがカーソルを移動する際に移動する役割を担います。 オブジェクトがドラッグ ループ内にある間、ソースは IDropSource インターフェイスを介して状態情報を受け取ります。 詳細については、「 IDropSource の実装」を参照してください。

データ オブジェクトがクリップボードからターゲットによって取得された場合、ソースは通知を受け取らなくなります。 ドラッグ アンド ドロップ操作によってオブジェクトがターゲットにドロップされると、ドラッグ ループを開始するために呼び出された DoDragDrop 関数が返されます。

グローバル メモリ オブジェクトをデータ オブジェクトに追加する方法

シェル データ形式の多くは、グローバル メモリ オブジェクトの形式です。 グローバル メモリ オブジェクトを含む形式を作成し、データ オブジェクトに読み込むには、次の手順に従います。

  1. FORMATETC 構造体を作成します。 cfFormat メンバーを適切なクリップボード形式の値に設定し、tymed メンバーを TYMED_HGLOBALに設定します。
  2. STGMEDIUM 構造体を作成します。 tymed メンバーを TYMED_HGLOBAL に設定します。
  3. GlobalAlloc を呼び出してグローバル メモリ オブジェクトを作成し、適切なサイズのメモリ ブロックを割り当てます。
  4. GlobalAlloc から返されるアドレスに転送するデータブロックを割り当てます。
  5. グローバル メモリ オブジェクトのアドレスを STGMEDIUM 構造体の hGlobal メンバーに割り当てます。
  6. IDataObject::SetData を呼び出し、前の手順で作成した FORMATETC および STGMEDIUM 構造体を渡して、データ オブジェクトに書式を読み込みます。

次のサンプル関数は、 DWORD 値を含むグローバル メモリ オブジェクトを作成し、データ オブジェクトに読み込みます。 pdtobj パラメーターは、データ オブジェクトの IDataObject インターフェイスへのポインターであり、cf はクリップボード形式の値、dw はデータ値です。

STDAPI DataObj_SetDWORD(IDataObject *pdtobj, UINT cf, DWORD dw)
{
    FORMATETC fmte = {(CLIPFORMAT) cf, 
                      NULL, 
                      DVASPECT_CONTENT, 
                      -1, 
                      TYMED_HGLOBAL};
    STGMEDIUM medium;

    HRESULT hres = E_OUTOFMEMORY;
    DWORD *pdw = (DWORD *)GlobalAlloc(GPTR, sizeof(DWORD));
    
    if (pdw)
    {
        *pdw = dw;       
        medium.tymed = TYMED_HGLOBAL;
        medium.hGlobal = pdw;
        medium.pUnkForRelease = NULL;

        hres = pdtobj->SetData(&fmte, &medium, TRUE);
 
        if (FAILED(hres))
            GlobalFree((HGLOBAL)pdw);
    }
    return hres;
}

IDataObject の実装

IDataObject は、データ オブジェクトのプライマリ インターフェイスです。 すべてのデータ オブジェクトで実装する必要があります。 ソースとターゲットの両方で、次のようなさまざまな目的で使用されます。

  • データ オブジェクトへのデータの読み込み。
  • データ オブジェクトからのデータの抽出。
  • データ オブジェクトに含まれるデータの種類の決定。
  • データ転送の結果に関するフィードバックをデータ オブジェクトに提供する。

IDataObject では 、さまざまなメソッドがサポートされています。 このセクションでは、シェル データ オブジェクト、 SetDataEnumFormatEtcGetData の 3 つの最も重要なメソッドを実装する方法について説明します。 その他のメソッドについては、 IDataObject リファレンスを参照してください。

SetData メソッド

IDataObject::SetData メソッドの主な機能は、ソースがデータ オブジェクトにデータを読み込むのを許可することです。 含める各形式について、ソースは FORMATETC 構造体を作成して形式を識別し、 STGMEDIUM 構造体を作成してデータへのポインターを保持します。 次に、ソースはオブジェクトの IDataObject::SetData メソッドを呼び出し、形式の FORMATETC および STGMEDIUM 構造体を渡します。 メソッドは、ターゲットが IDataObject::GetData を呼び出してオブジェクトからデータを抽出するときに使用できるように、この情報を格納する必要があります。

ただし、ファイルを転送する場合、シェルは、各ファイルの情報を別の CFSTR_FILECONTENTS 形式に転送することがよくあります。 異なるファイルを区別するために、各ファイルの FORMATETC 構造体の lIndex メンバーは、特定のファイルを識別するインデックス値に設定されます。 IDataObject::SetData 実装では、lIndex メンバーによってのみ異なる複数のCFSTR_FILECONTENTS形式を格納できる必要があります。

カーソルがターゲット ウィンドウの上にある間、ターゲットは ドラッグ アンド ドロップ ヘルパー オブジェクト を使用してドラッグ イメージを指定できます。 ドラッグ アンド ドロップ ヘルパー オブジェクトは IDataObject::SetData を呼び出して、プロセス間のサポートに使用されるデータ オブジェクトにプライベート形式を読み込みます。 ドラッグ アンド ドロップ ヘルパー オブジェクトをサポートするには、 IDataObject::SetData 実装で任意のプライベート形式を受け入れて格納できる必要があります。

データが削除された後、一部の種類のシェル データ転送では、ターゲットが IDataObject::SetData を呼び出して、ドロップ操作の結果に関する情報をデータ オブジェクトに提供する必要があります。 たとえば、最適化された移動操作でファイルを移動する場合、ターゲットは通常、元のファイルを削除しますが、削除する必要はありません。 ターゲットは、CFSTR_LOGICALPERFORMEDDROPEFFECT形式で IDataObject::SetData を呼び出してファイルを削除したかどうかをデータ オブジェクトに通知します。 他にも、データ オブジェクトに情報を渡すためにターゲットによって使用される シェル クリップボード形式 がいくつかあります。 IDataObject::SetData 実装では、これらの形式を認識し、適切に応答できる必要があります。 詳細については、「 シェル データ転送シナリオの処理」を参照してください。

EnumFormatEtc メソッド

ターゲットは、データ オブジェクトを受け取ると、通常 FORMATETC を呼び出して、オブジェクトに含まれる書式を決定します。 メソッドは OLE 列挙オブジェクトを作成し、オブジェクトの IEnumFORMATETC インターフェイスへのポインターを返します。 次に、ターゲットは インターフェイスを使用して使用可能な形式を列挙します。

列挙オブジェクトは常に、最適な形式から順に使用可能な形式を列挙する必要があります。 形式の相対的な品質は、ドロップ ソースによって定義されます。 一般に、最高品質の形式には、最も豊富で最も完全なデータが含まれています。 たとえば、通常、24 ビットのカラー イメージは、そのイメージのグレースケール バージョンよりも高品質と見なされます。 形式を品質順に列挙する理由は、通常、ターゲットがサポートする形式になるまで列挙し、その形式を使用してデータを抽出するためです。 この手順で、ターゲットがサポートできる最適な形式を生成するには、形式を品質の順に列挙する必要があります。

シェル データの列挙オブジェクトは、他の種類のデータ転送の場合とほぼ同じ方法で実装され、1 つの注目すべき例外があります。 通常、データ オブジェクトには形式ごとに 1 つのデータ項目しか含めないため、通常は IDataObject::SetData に渡されるすべての形式を列挙します。 ただし、「 SetData メソッド 」セクションで説明されているように、シェル データ オブジェクトには複数 のCFSTR_FILECONTENTS 形式を含めることができます。

IDataObject::EnumFormatEtc の目的は、ターゲットが存在するデータの種類を判断できるようにすることであるため、複数のCFSTR_FILECONTENTS形式を列挙する必要はありません。 ターゲットがデータ オブジェクトに含まれるこれらの形式の数を把握する必要がある場合、ターゲットは、付随するCFSTR_FILEDESCRIPTOR形式からその情報を取得できます。 IDataObject::EnumFormatEtc を実装する方法の詳細については、メソッドのリファレンス ドキュメントを参照してください。

GetData メソッド

ターゲットは IDataObject::GetData を呼び出して、特定のデータ形式を抽出します。 ターゲットは、適切な FORMATETC 構造体を渡すことによって形式を指定します。 IDataObject::GetData は、形式の STGMEDIUM 構造体を 返します。

ターゲットは、FORMATETC 構造体の tymed メンバーを特定のTYMED_XXX 値に設定して、データの抽出に使用するデータ転送メカニズムを指定できます。 ただし、ターゲットは、より汎用的な要求を行い、データ オブジェクトが決定できるようにすることもできます。 データ転送メカニズムの選択をデータ オブジェクトに求めるために、ターゲットは、サポートされているすべてのTYMED_XXX 値を設定します。 IDataObject::GetData は、これらのデータ転送メカニズムのいずれかを選択し、適切な STGMEDIUM 構造体を返します。 たとえば、 通常、tymed は TYMED_HGLOBAL | に設定されます。TYMED_ISTREAM |TYMED_ISTORAGE、3 つのシェル データ転送メカニズムのいずれかを要求します。

Note

複数のCFSTR_FILECONTENTS形式が存在する可能性があるため、FORMATETC 構造体の cfFormat および tymed メンバーでは、どの STGMEDIUM 構造体 IDataObject::GetData を返す必要があるかを示すには不十分です。 CFSTR_FILECONTENTS形式の場合、IDataObject::GetData は、正しい STGMEDIUM 構造体を返すために FORMATETC 構造体の lIndex メンバーも調べる必要があります。

 

CFSTR_INDRAGLOOP形式はデータ オブジェクトに配置され、ターゲットはドラッグ アンド ドロップ ループの状態をチェックしながら、オブジェクトのデータのメモリ負荷の高いレンダリングを回避できます。 形式のデータは DWORD 値であり、データ オブジェクトがドラッグ ループ内にある場合は 0 以外の値に設定されます。 データが削除された場合、形式のデータ値は 0 に設定されます。 ターゲットがこの形式を要求し、ソースによって読み込まれていない場合、 IDataObject::GetData は、ソースが 0 の値で形式を読み込んだかのように応答する必要があります。

カーソルがターゲット ウィンドウの上にある間、ターゲットは ドラッグ アンド ドロップ ヘルパー オブジェクト を使用してドラッグ イメージを指定できます。 ドラッグ アンド ドロップ ヘルパー オブジェクトは IDataObject::SetData を呼び出して、プロセス間のサポートに使用されるデータ オブジェクトにプライベート形式を読み込みます。 後で IDataObject::GetData を呼び出して取得します。 ドラッグ アンド ドロップ ヘルパー オブジェクトをサポートするには、シェル データ オブジェクトの実装で、要求されたときに任意のプライベート形式を返すことができる必要があります。

IDropSource の実装

ソースは、 IDropSource インターフェイスを公開するオブジェクトを作成する必要があります。 このインターフェイスを使用すると、ソースはカーソルの現在位置を示す ドラッグ イメージ を更新し、ドラッグ アンド ドロップ操作を終了する方法に関するフィードバックをシステムに提供できます。 IDropSource には、 GiveFeedbackQueryContinueDrag の 2 つのメソッドがあります。

GiveFeedback メソッド

ドラッグ ループでは、ドロップ ソースがカーソル位置を追跡し、適切なドラッグ イメージを表示します。 ただし、場合によっては、ドロップ ターゲットのウィンドウの上にあるドラッグ イメージの外観を変更する必要があります。

カーソルがターゲット ウィンドウに入ったり離れたりするときに、ターゲット ウィンドウの上を移動している間に、システムはターゲットの IDropTarget インターフェイスを定期的に呼び出します。 ターゲットは、GiveFeedback メソッドを使用してソースに転送される DROPEFFECT 値で応答します。 必要に応じて、ソースは DROPEFFECT 値に基づいてカーソルの外観を変更できます。 詳細については、 GiveFeedbackDoDragDrop のリファレンスを参照してください。

QueryContinueDrag メソッド

このメソッドは、データ オブジェクトがドラッグ ループ中にマウス ボタンまたはキーボードの状態が変更された場合に呼び出されます。 ESC キーが押されたかどうかをソースに通知し、Ctrl キーや Shift キーなどのキーボード修飾子キーの現在の状態を提供します。 QueryContinueDrag メソッドの戻り値は、次の 3 つのアクションのいずれかを指定します。

  • S_ok。 ドラッグ操作を続行する
  • DRAGDROP_S_DROP。 データを削除します。 その後、システムはターゲットの IDropTarget::D rop メソッドを 呼び出します。
  • DRAGDROP_S_CANCEL。 データを削除せずにドラッグ ループを終了します。 この値は通常、ESCAPE キーが押された場合に返されます。

詳細については、 QueryContinueDragDoDragDrop のリファレンスを参照してください。

ターゲットがデータ オブジェクトを処理する方法

クリップボードからデータ オブジェクトを取得するか、ユーザーがターゲット ウィンドウにドロップしたときに、ターゲットはデータ オブジェクトを受け取ります。 その後、ターゲットはデータ オブジェクトからデータを抽出できます。 必要に応じて、ターゲットは操作の結果をデータ オブジェクトに通知することもできます。 シェル データ転送の前に、ドロップ ターゲットは操作に備える必要があります。

  1. ターゲットは RegisterClipboardFormat を呼び出して、データ オブジェクトに含まれる可能性があるすべてのシェル形式 ( CF_HDROP以外) の有効なクリップボード形式の値を取得する必要があります。 CF_HDROPは既に有効なクリップボード形式であり、登録する必要はありません。
  2. ドラッグ アンド ドロップ操作をサポートするには、ターゲットに IDropTarget インターフェイスを実装し、ターゲット ウィンドウを登録する必要があります。 ターゲット ウィンドウを登録するために、ターゲットは RegisterDragDrop を 呼び出し、ウィンドウのハンドルと IDropTarget インターフェイス ポインターを渡します。

クリップボード転送の場合、ターゲットはデータ オブジェクトがクリップボードに配置されたことを示す通知を受け取りません。 通常、アプリケーションは、アプリケーションのツール バーの [貼り付け] ボタンをクリックするなど、ユーザー アクションによってオブジェクトがクリップボードにあることを通知します。 次に、OleGetClipboard を呼び出して、クリップボードからデータ オブジェクトの IDataObject ポインターを取得します。 ドラッグ アンド ドロップデータ転送の場合、システムはターゲットの IDropTarget インターフェイスを使用して、データ転送の進行状況に関する情報をターゲットに提供します。

  • カーソルがターゲット ウィンドウに入ると、システムは IDropTarget::D ragEnter を呼び出します。
  • カーソルがターゲット ウィンドウを通過すると、システムは IDropTarget::D ragOver を定期的に呼び出して、ターゲットに現在のカーソル位置を与えます。
  • カーソルがターゲット ウィンドウから離れると、システムは IDropTarget::D ragLeave を呼び出します。
  • ユーザーがターゲット ウィンドウでデータ オブジェクトを削除すると、システムは IDropTarget::D rop を呼び出します。

これらのメソッドを実装する方法については、「 IDropTarget」を参照してください。

データが削除されると、 IDropTarget::D rop はターゲットにデータ オブジェクトの IDataObject インターフェイスへのポインターを提供します。 その後、ターゲットはこのインターフェイスを使用してデータ オブジェクトからデータを抽出します。

データ オブジェクトからのシェル データの抽出

データ オブジェクトがクリップボードから削除または取得されると、ターゲットは必要なデータを抽出できます。 抽出プロセスの最初の手順は、通常、データ オブジェクトに含まれる形式を列挙することです。

  • IDataObject::EnumFormatEtc を呼び出します。 データ オブジェクトは、標準の OLE 列挙オブジェクトを作成し、その IEnumFORMATETC インターフェイスへのポインターを返します。
  • データ オブジェクトに含まれる形式を列挙するには、 IEnumFORMATETC メソッドを使用します。 通常、この操作では、オブジェクトに含まれる形式ごとに 1 つの FORMATETC 構造体が取得されます。 ただし、通常、列挙オブジェクトは、データ オブジェクトに含まれる形式の数に関係なく、CFSTR_FILECONTENTS形式に対して 1 つの FORMATETC 構造体のみを返します。
  • 抽出する 1 つ以上の形式を選択し、 FORMATETC 構造体を格納します。

特定の形式を取得するには、関連付けられている FORMATETC 構造体を IDataObject::GetData に渡します。 このメソッドは、データへのアクセスを提供する STGMEDIUM 構造体を返します。 特定のデータ転送メカニズムを指定するには、FORMATETC 構造体の tymed 値を対応する TYMED_XXX 値に設定します。 データ転送メカニズムの選択をデータ オブジェクトに求めるために、ターゲットは、ターゲットが処理できるすべてのデータ転送メカニズムに対してTYMED_XXX 値を設定します。 データ オブジェクトは、これらのデータ転送メカニズムのいずれかを選択し、適切な STGMEDIUM 構造体を返します。

ほとんどの形式の場合、ターゲットは、使用可能な形式を列挙したときに受け取った FORMATETC 構造体を渡すことによって、データを取得できます。 このルールの 1 つの例外は 、CFSTR_FILECONTENTSです。 データ オブジェクトにはこの形式の複数のインスタンスを含めることができるため、列挙子によって返される FORMATETC 構造体が、抽出する特定の形式に対応していない可能性があります。 cfFormat メンバーと tymed メンバーを指定するだけでなく、lIndex メンバーをファイルのインデックス値に設定する必要もあります。 詳細については、「シェル データ転送シナリオの処理」の「CFSTR_FILECONTENTS形式を使用してファイルからデータを抽出する」セクションを参照してください。

データ抽出プロセスは、返される STGMEDIUM 構造体に含まれるポインターの型によって異なります。 構造体に IStream または IStorage インターフェイスへのポインターが含まれている場合は、インターフェイス メソッドを使用してデータを抽出します。 グローバル メモリ オブジェクトからデータを抽出するプロセスについては、次のセクションで説明します。

データ オブジェクトからグローバル メモリ オブジェクトを抽出する

シェル データ形式の多くは、グローバル メモリ オブジェクトの形式です。 データ オブジェクトからグローバル メモリ オブジェクトを含む形式を抽出し、そのデータをローカル変数に割り当てるには、次の手順に従います。

  1. FORMATETC 構造体を作成します。 cfFormat メンバーを適切なクリップボード形式の値に設定し、tymed メンバーを TYMED_HGLOBALに設定します。

  2. 空の STGMEDIUM 構造体を 作成します。

  3. IDataObject::GetData を呼び出し、FORMATETC および STGMEDIUM 構造体へのポインターを渡します。

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

  4. GlobalLock を呼び出し、STGMEDIUM 構造体の hGlobal メンバーを渡して、ローカル変数にデータを割り当てます。

  5. GlobalUnlock を呼び出して、グローバル メモリ オブジェクトのロックを解放します。

  6. ReleaseStgMedium を呼び出して、グローバル メモリ オブジェクトを解放します。

Note

GlobalFree ではなく、ReleaseStgMedium を使用してグローバル メモリ オブジェクトを解放する必要があります。

 

次の例は、データ オブジェクトからグローバル メモリ オブジェクトとして格納されている DWORD 値を抽出する方法を示しています。 pdtobj パラメーターはデータ オブジェクトの IDataObject インターフェイスへのポインターであり、cf は目的のデータを識別するクリップボード形式であり、pdwOut を使用してデータ値を返します。

STDAPI DataObj_GetDWORD(IDataObject *pdtobj, UINT cf, DWORD *pdwOut)
{    STGMEDIUM medium;
   FORMATETC fmte = {(CLIPFORMAT) cf, NULL, DVASPECT_CONTENT, -1, 
       TYMED_HGLOBAL};
    HRESULT hres = pdtobj->GetData(&fmte, &medium);
    if (SUCCEEDED(hres))
   {
       DWORD *pdw = (DWORD *)GlobalLock(medium.hGlobal);
       if (pdw)
       {
           *pdwOut = *pdw;
           GlobalUnlock(medium.hGlobal);
       }
       else
       {
           hres = E_UNEXPECTED;
       }
       ReleaseStgMedium(&medium);
   }
   return hres;
}

IDropTarget の実装

カーソルがターゲット ウィンドウの上にある間、システムは IDropTarget インターフェイスを使用してターゲットと通信します。 ターゲットの応答は、 IDropSource インターフェイスを介してソースに転送されます。 応答に応じて、ソースはデータを表すアイコンを変更できます。 ドロップ ターゲットでデータ アイコンを指定する必要がある場合は、 ドラッグ アンド ドロップ ヘルパー オブジェクトを作成することでこれを行うことができます。

従来のドラッグ アンド ドロップ操作では、ターゲットは IDropTarget::D roppdwEffect パラメーターを適切な DROPEFFECT 値に設定することで、操作の結果をデータ オブジェクトに通知します。 シェル データ オブジェクトでは、ターゲットで IDataObject::SetData を呼び出す必要がある場合もあります。 さまざまなデータ転送シナリオに対してターゲットが応答する方法については、「シェル データ転送 シナリオの処理」を参照してください。

次のセクションでは、 IDropTarget::D ragEnter、IDropTarget::D ragOver、および IDropTarget::Drop メソッドを実装する方法について簡単に説明します。 詳細については、リファレンス ドキュメントを参照してください。

DragEnter メソッド

カーソルがターゲット ウィンドウに入ると、システムは IDropTarget::D ragEnter メソッドを呼び出します。 そのパラメーターは、カーソルの位置、Ctrl キーなどのキーボード修飾子キーの状態、およびデータ オブジェクトの IDataObject インターフェイスへのポインターをターゲットに提供します。 ターゲットは、そのインターフェイスを使用して、データ オブジェクトに含まれるいずれかの形式を受け入れられるかどうかを判断する役割を担います。 可能な場合は、通常、 pdwEffect の値は変更されません。 データ オブジェクトのデータを受け入れることができない場合は、 pdwEffect パラメーターをDROPEFFECT_NONEに設定します。 システムは、このパラメーターの値をデータ オブジェクトの IDropSource インターフェイスに渡して、適切なドラッグ イメージを表示できるようにします。

ターゲットでは、シェル データが削除される前に 、IDataObject::GetData メソッドを使用してシェル データをレンダリングしないでください。 このような発生ごとにオブジェクトのデータを完全にレンダリングすると、ドラッグ カーソルが停止する可能性があります。 この問題を回避するために、一部のシェル オブジェクトには CFSTR_INDRAGLOOP 形式が含まれています。 この形式を抽出することで、ターゲットはドラッグ ループの状態をチェックしながら、オブジェクトのデータのメモリ消費量の多いレンダリングを回避できます。 書式のデータ値は DWORD であり、データ オブジェクトがドラッグ ループ内にある場合は 0 以外の値に設定されます。 データが削除された場合、書式のデータ値は 0 に設定されます。

ターゲットがデータ オブジェクトのデータを受け入れられる場合は、 grfKeyState を調べて、通常のドロップ動作を変更するために修飾子キーが押されたかどうかを判断する必要があります。 たとえば、既定の操作は通常移動ですが、Ctrl キーを押すと通常、コピー操作が示されます。

カーソルがターゲット ウィンドウ上にある間、ターゲットは ドラッグ アンド ドロップ ヘルパー オブジェクト を使用して、データ オブジェクトのドラッグ イメージを独自のイメージに置き換えることができます。 その場合、 IDropTarget::D ragEnterIDropTargetHelper::D ragEnter を呼び出して、 DragEnter パラメーターに含まれる情報をドラッグ アンド ドロップ ヘルパー オブジェクトに渡す必要があります。

DragOver メソッド

カーソルがターゲット ウィンドウ内で移動すると、システムは IDropTarget::D ragOver メソッドを定期的に呼び出します。 そのパラメーターは、カーソルの位置と、Ctrl キーなどのキーボード修飾子キーの状態をターゲットに提供します。 IDropTarget::D ragOverIDropTarget::D ragEnter とほぼ同じ役割を果たしており、実装は通常よく似ています。

ターゲットがドラッグ アンド ドロップ ヘルパー オブジェクトを使用している場合、 IDropTarget::D ragOverIDropTargetHelper::D ragOver を呼び出して 、DragOver パラメーターに含まれる情報をドラッグ アンド ドロップ ヘルパー オブジェクトに転送する必要があります。

Drop メソッド

システムは IDropTarget::D rop メソッドを呼び出して、ユーザーがデータを削除したことをターゲットに通知します。通常はマウス ボタンを離します。 IDropTarget::D rop には、 IDropTarget::D ragEnter と同じパラメーターがあります。 ターゲットは通常、データ オブジェクトから 1 つ以上の形式を抽出して応答します。 完了したら、ターゲットは pdwEffect パラメーターを、操作の結果を示す DROPEFFECT 値に設定する必要があります。 一部の種類のシェル データ転送の場合、ターゲットは IDataObject::SetData を呼び出して、操作の結果に関する追加情報を含む形式をデータ オブジェクトに渡す必要もあります。 詳細については、「 シェル データ転送シナリオの処理」を参照してください。

ターゲットがドラッグ アンド ドロップ ヘルパー オブジェクトを使用している場合、 IDropTarget::D ropIDropTargetHelper::D rop を呼び出して 、IDropTargetHelper::D ragOver パラメーターに含まれる情報をドラッグ アンド ドロップ ヘルパー オブジェクトに転送する必要があります。

ドラッグ アンド ドロップ ヘルパー オブジェクトの使用

ドラッグ アンド ドロップ ヘルパー オブジェクト (CLSID_DragDropHelper) はシェルによってエクスポートされ、ターゲットがターゲット ウィンドウ上にある間にドラッグ イメージを指定できます。 ドラッグ アンド ドロップ ヘルパー オブジェクトを使用するには、CLSID_DragDropHelperのクラス識別子 (CLSID) を使用して CoCreateInstance を呼び出して、インプロセス サーバー オブジェクトを作成します。 ドラッグ アンド ドロップ ヘルパー オブジェクトは、次のように使用される 2 つのインターフェイスを公開します。

  • IDragSourceHelper インターフェイスを使用すると、ドロップ ターゲットは、データ オブジェクトを表すアイコンを指定できます。
  • IDropTargetHelper インターフェイスを使用すると、ドロップ ターゲットはドラッグ アンド ドロップ ヘルパー オブジェクトにカーソル位置を通知したり、データ アイコンを表示または非表示にしたりできます。

IDragSourceHelper インターフェイスの使用

IDragSourceHelper インターフェイスは、ドラッグ アンド ドロップ ヘルパー オブジェクトによって公開され、カーソルがターゲット ウィンドウの上にある間に表示されるイメージをドロップ ターゲットが提供できるようにします。 IDragSourceHelper には、ドラッグ イメージとして使用するビットマップを指定する 2 つの代替方法が用意されています。

  • ウィンドウを持つドロップ ターゲットは、 IDragSourceHelper::InitializeFromWindow を使用してドラッグ アンド ドロップ ヘルパー オブジェクトを初期化することで、DI_GETDRAGIMAGE ウィンドウ メッセージを登録できます。 ターゲットがDI_GETDRAGIMAGEメッセージを受信すると、ハンドラーは、ドラッグイメージビットマップ情報を、メッセージの lParam 値として渡される SHDRAGIMAGE 構造体に配置します。
  • ウィンドウレス ドロップ ターゲットは、 IDragSourceHelper::InitializeFromBitmap を使用してドラッグ アンド ドロップ ヘルパー オブジェクトを初期化するときにビットマップを指定します。

IDropTargetHelper インターフェイスの使用

このインターフェイスを使用すると、カーソルがターゲットに入ったり離れたときに、ドロップ ターゲットがドラッグ アンド ドロップ ヘルパー オブジェクトに通知されます。 カーソルがターゲット ウィンドウの上にある間、 IDropTargetHelper を使用すると、ターゲットは IDropTarget インターフェイスを介してターゲットが受け取る情報をドラッグ アンド ドロップ ヘルパー オブジェクトに提供できます。

IDropTargetHelper メソッドの 4 つ (IDropTargetHelper::D ragEnterIDropTargetHelper::D ragLeaveIDropTargetHelper::D ragOverIDropTargetHelper::D rop) は、同じ名前の IDropTarget メソッドに関連付けられています。 ドラッグ アンド ドロップ ヘルパー オブジェクトを使用するには、各 IDropTarget メソッドで対応する IDropTargetHelper メソッドを呼び出して、情報をドラッグ アンド ドロップ ヘルパー オブジェクトに転送する必要があります。 5 番目の IDropTargetHelper メソッド IDropTargetHelper::Show は、ドラッグ イメージを表示または非表示にするようにドラッグ アンド ドロップ ヘルパー オブジェクトに通知します。 このメソッドは、低色深度のビデオ モードでターゲット ウィンドウ上をドラッグするときに使用されます。 これにより、ウィンドウの描画中にターゲットがドラッグイメージを非表示にすることができます。