Shell 数据对象

数据对象是所有 Shell 数据传输的核心。 它主要是用于保存传输的数据的容器。 但是,目标还可以与数据对象通信,以促进某些专用类型的 Shell 数据传输,例如优化的移动。 本主题提供有关 Shell 数据对象工作原理、源构造方式以及目标处理方式的一般讨论。 有关如何使用数据对象传输不同类型的 Shell 数据的详细讨论,请参阅 处理 Shell 数据传输方案

数据对象的工作原理

数据对象是组件对象模型 (COM) 对象,由数据源创建,用于将数据传输到目标。 它们通常携带多个数据项。 这种做法有两个原因:

  • 虽然几乎可以使用数据对象传输任何类型的数据,但源通常不知道目标可以接受的数据类型。 例如,数据可能是格式化文本文档的一部分。 虽然目标可能能够处理复杂的格式设置信息,但它也可能只接受 ANSI 文本。 因此,数据对象通常以多种不同的格式包含相同的数据。 然后,目标可以采用可以处理的格式提取数据。
  • 数据对象还可以包含不是源数据的辅助数据项。 这种类型的数据项通常提供有关数据传输操作的其他信息。 例如,Shell 使用辅助数据项来指示是复制还是移动文件。

剪贴板格式

数据对象中的每个数据项都有关联的格式,通常称为 剪贴板格式。 Winuser.h 中声明了许多标准剪贴板格式,这些格式对应于常用数据类型。 剪贴板格式是整数,但它们通常由其等效名称引用,其格式为 CF_XXX。 例如,ANSI 文本的剪贴板格式CF_TEXT。

应用程序可以通过定义专用格式来扩展可用剪贴板格式的范围。 若要定义专用格式,应用程序使用标识格式的字符串调用 RegisterClipboardFormat 。 函数返回的无符号整数是一个有效的格式值,可以像标准剪贴板格式一样使用。 但是,源和目标必须注册格式才能使用它。 除了一个例外(CF_HDROP),用于传输 Shell 数据的剪贴板格式定义为专用格式。 源和目标必须注册它们,然后才能使用它们。 有关可用 Shell 剪贴板格式的说明,请参阅 Shell 剪贴板格式。

尽管存在一些例外情况,但数据对象通常只包含它们支持的每个剪贴板格式的一项数据。 格式和数据之间的这种一对一关联允许格式值用作关联数据项的标识符。 事实上,在讨论数据对象的内容时,特定数据项通常称为“格式”,并按格式名称引用。 例如,短语,例如“提取CF_TEXT格式...”通常用于讨论数据对象的 ANSI 文本数据项。

当放置目标收到指向数据对象的指针时,放置目标会枚举可用的格式,以确定可用的数据类型。 然后,它请求一个或多个可用格式并提取数据。 目标从数据对象中提取 Shell 数据的具体方式因格式而异;在 目标处理数据对象的方式中详细介绍了这一点。

使用简单的剪贴板数据传输,数据将放置在全局内存对象中。 该对象的地址放置在剪贴板上,以及其格式。 剪贴板格式告知目标在关联地址中找到的数据类型。 虽然简单的剪贴板传输易于实现:

  • 数据对象提供了更灵活的传输数据方式。
  • 数据对象更适合传输大量数据。
  • 数据对象必须用于通过拖放操作传输数据。

出于这些原因,所有 Shell 数据传输都使用数据对象。 使用数据对象时,不直接使用剪贴板格式。 而是使用剪贴板格式的通用化( FORMATETC 结构)标识数据项。

FORMATETC 结构

FORMATETC 结构是剪贴板格式的扩展版本。 与 Shell 数据传输一起使用, FORMATETC 结构具有以下特征:

  • 数据项仍由其剪贴板格式标识, 位于 cfFormat 成员中。

  • 数据传输不限于全局内存对象。 tymed 成员用于指示关联 STGMEDIUM 结构中包含的数据传输机制。 它设置为 TYMED_XXX 值之一。

  • Shell 使用 lIndex 成员及其 CFSTR_FILECONTENTS 格式,允许数据对象包含每个格式的多个数据项。 有关如何使用此格式的讨论,请参阅“使用CFSTR_FILECONTENTS格式从处理 Shell 数据传输方案的文件”部分提取数据。

  • dwAspect 成员通常设置为DVASPECT_CONTENT。 但是,Shlobj.h 中定义了三个值,可用于 Shell 数据传输。

    说明
    DVASPECT_COPY 用于指示格式表示数据的副本。
    DVASPECT_LINK 用于指示格式表示数据的快捷方式。
    DVASPECT_SHORTNAME 与CF_HDROP格式一起使用,请求名称缩短为 8.3 格式的文件路径。

     

  • ptd 成员不用于 Shell 数据传输,通常设置为 NULL

STGMEDIUM 结构

STGMEDIUM 结构提供对所传输数据的访问。 Shell 数据支持三种数据传输机制:

STGMEDIUM 结构的 tymed 成员是标识数据传输机制的TYMED_XXX值。 第二个成员是目标用来提取数据的指针。 指针可以是多种类型之一,具体取决于 tymed 值。 下表汇总了用于 Shell 数据传输的三个 tymed 值及其相应的 STGMEDIUM 成员名称。

tymed 值 成员名称 描述
TYMED_HGLOBAL hGlobal 指向全局内存对象的指针。 此指针类型通常用于传输少量数据。 例如,Shell 使用全局内存对象传输短文本字符串,例如文件名或 URL。
TYMED_ISTREAM pstm 指向 IStream 接口的指针。 对于大多数 Shell 数据传输,首选此指针类型,因为它需要相对较少的内存,而TYMED_HGLOBAL。 此外,TYMED_ISTREAM数据传输机制不需要源以任何特定方式存储其数据。
TYMED_ISTORAGE pstg 指向 IStorage 接口的指针。 目标调用接口方法来提取数据。 与TYMED_ISTREAM一样,此指针类型需要相对较少的内存。 但是,由于TYMED_ISTORAGE的灵活程度低于TYMED_ISTREAM,因此它不像常用的那样。

 

源如何创建数据对象

当用户启动 Shell 数据传输时,源负责创建数据对象并使用数据加载数据。 以下过程总结了该过程:

  1. 调用 RegisterClipboardFormat 以获取数据对象中包含的每个 Shell 格式的有效剪贴板格式值。 请记住, CF_HDROP 已是有效的剪贴板格式,不需要注册。
  2. 对于要传输的每个格式,请将关联的数据放入全局内存对象,或创建一个对象,该对象通过 IStreamIStorage 接口提供对该数据的访问权限。 IStreamIStorage 接口是使用标准 COM 技术创建的。 有关如何处理全局内存对象的讨论,请参阅 如何将全局内存对象添加到数据对象
  3. 为每个格式创建 FORMATETCSTGMEDIUM 结构。
  4. 实例化数据对象。
  5. 通过为每个受支持的格式调用 IDataObject::SetData 方法并传入格式的 FORMATETCSTGMEDIUM 结构,将数据加载到数据对象中。
  6. 使用剪贴板数据传输时,调用 OleSetClipboard ,将指针置于剪贴板上的数据对象的 IDataObject 接口。 对于拖放传输,请通过调用 DoDragDrop 启动拖动循环。 删除数据时, IDataObject 指针将传递到放置目标,结束拖动循环。

数据对象现已准备好传输到目标。 对于剪贴板数据传输,只需将对象保留到目标通过调用 OleGetClipboard 请求它为止。 对于拖放数据传输,数据对象负责创建图标来表示数据,并在用户移动光标时移动数据。 当对象处于拖动循环中时,源会通过其 IDropSource 接口接收状态信息。 有关进一步的讨论,请参阅 实现 IDropSource

如果数据对象由目标从剪贴板检索,则源不会收到任何通知。 当通过拖放操作将对象拖放到目标上时,调用用于启动拖动循环的 DoDragDrop 函数将返回。

如何将全局内存对象添加到数据对象

许多 Shell 数据格式采用全局内存对象的形式。 使用以下过程创建包含全局内存对象的格式并将其加载到数据对象中:

  1. 创建 FORMATETC 结构。 将 cfFormat 成员设置为适当的剪贴板格式值,并将 tymed 成员设置为TYMED_HGLOBAL。
  2. 创建 STGMEDIUM 结构。 将 tymed 成员设置为TYMED_HGLOBAL。
  3. 通过调用 GlobalAlloc 来分配适当大小的内存块来创建全局内存对象。
  4. 分配要传输到 GlobalAlloc 返回的地址的数据块。
  5. 将全局内存对象的地址分配给 STGMEDIUM 结构的 hGlobal 成员。
  6. 通过调用 IDataObject::SetData 并传入在前面步骤中创建的 FORMATETCSTGMEDIUM 结构,将格式加载到数据对象中。

以下示例函数创建包含 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 支持多种方法。 本部分讨论如何为 Shell 数据对象、 SetDataEnumFormatEtcGetData 实现三种最重要的方法。 有关其他方法的讨论,请参阅 IDataObject 参考。

SetData 方法

IDataObject::SetData 方法的主要函数是允许源将数据加载到数据对象中。 对于要包含的每个格式,源将创建 FORMATETC 结构来标识格式和 STGMEDIUM 结构,用于保存指向数据的指针。 然后,源调用对象的 IDataObject::SetData 方法,并传入格式的 FORMATETCSTGMEDIUM 结构。 该方法必须存储此信息,以便在目标调用 IDataObject::GetData 从对象中提取数据时可用。

但是,传输文件时,Shell 通常会将每个文件的信息放入单独的 CFSTR_FILECONTENTS 格式。 为了区分不同的文件,每个文件的 FORMATETC 结构的 lIndex 成员设置为标识特定文件的索引值。 IDataObject::SetData 实现必须能够存储仅与其 lIndex 成员不同的多个CFSTR_FILECONTENTS格式。

光标位于目标窗口上方时,目标可以使用 拖放帮助程序对象 来指定拖动图像。 拖放帮助程序对象调用 IDataObject::SetData ,以将专用格式加载到用于跨进程支持的数据对象中。 若要支持拖放帮助程序对象, IDataObject::SetData 实现必须能够接受和存储任意专用格式。

删除数据后,某些类型的 Shell 数据传输要求目标调用 IDataObject::SetData ,以便向数据对象提供有关删除操作结果的信息。 例如,使用优化的移动操作移动文件时,目标通常会删除原始文件,但不需要这样做。 目标通过调用具有CFSTR_LOGICALPERFORMEDDROPEFFECT格式的 IDataObject::SetData 来通知数据对象是否删除了文件。 目标还使用其他几个 Shell 剪贴板格式 将信息传递给数据对象。 IDataObject::SetData 实现必须能够识别这些格式并相应地做出响应。 有关进一步讨论,请参阅 处理 Shell 数据传输方案

EnumFormatEtc 方法

当目标收到数据对象时,它通常调用 FORMATETC 来确定对象包含的格式。 该方法创建 OLE 枚举对象,并返回指向对象的 IEnumFORMATETC 接口的指针。 然后,目标使用接口枚举可用格式。

枚举对象应始终按质量顺序枚举可用格式,从最佳开始。 格式的相对质量由放置源定义。 一般情况下,质量最高的格式包含最丰富和最完整的数据。 例如,24 位颜色图像通常被视为质量高于该图像的灰度版本。 按质量顺序枚举格式的原因是,目标通常会枚举,直到它们达到支持的格式,然后使用该格式提取数据。 为了使此过程能够生成目标可以支持的最佳可用格式,必须按其质量顺序枚举格式。

Shell 数据的枚举对象与其他类型的数据传输方式大致相同,但有一个值得注意的例外。 由于数据对象通常每个格式只包含一个数据项,因此它们通常枚举传递给 IDataObject::SetData 的每个格式。 但是,如 SetData 方法 部分所述,Shell 数据对象可以包含多个 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请求三种 Shell 数据传输机制中的任何一种。

注意

由于可以有多个CFSTR_FILECONTENTS格式,FORMATETC 结构的 cfFormattymed 成员不足以指示应返回哪个 STGMEDIUM 结构 IDataObject::GetData。 对于CFSTR_FILECONTENTS格式, IDataObject::GetData 还必须检查 FORMATETC 结构的 lIndex 成员才能返回正确的 STGMEDIUM 结构。

 

CFSTR_INDRAGLOOP格式放置在数据对象中,以允许目标检查拖放循环的状态,同时避免对对象的数据进行内存密集型呈现。 如果数据对象位于拖动循环中,则格式的数据是 一个 DWORD 值,该值设置为非零值。 如果数据已删除,则格式的数据值设置为零。 如果目标请求此格式且源尚未加载, 则 IDataObject::GetData 应响应,就像源加载了值为零的格式一样。

光标位于目标窗口上方时,目标可以使用 拖放帮助程序对象 来指定拖动图像。 拖放帮助程序对象调用 IDataObject::SetData ,以将专用格式加载到用于跨进程支持的数据对象中。 稍后会调用 IDataObject::GetData 来检索它们。 若要支持拖放帮助程序对象,Shell 数据对象实现必须在请求时返回任意专用格式。

实现 IDropSource

源必须创建一个公开 IDropSource 接口的对象。 此接口允许源更新 拖动图像 ,该图像指示光标的当前位置,并为系统提供有关如何终止拖放操作的反馈。 IDropSource 有两种方法: GiveFeedbackQueryContinueDrag

GiveFeedback 方法

在拖动循环中,放置源负责跟踪光标位置并显示适当的拖动图像。 但是,在某些情况下,你可能希望在拖放目标窗口上方更改拖动图像的外观。

当光标进入或离开目标窗口并在目标窗口上移动时,系统会定期调用目标的 IDropTarget 接口。 目标使用通过 GiveFeedback 方法转发到源的 DROPEFFECT 值进行响应。 如果合适,源可以根据 DROPEFFECT 值修改游标的外观。 有关更多详细信息,请参阅 GiveFeedbackDoDragDrop 引用。

QueryContinueDrag 方法

如果在数据对象处于拖动循环中时鼠标按钮或键盘状态发生更改,则调用此方法。 它会通知源是否按下 ESC 键,并提供键盘修饰键的当前状态,如 Ctrl 或 SHIFT。 QueryContinueDrag 方法的返回值指定以下三个操作之一:

  • S_OK。 继续拖动操作
  • DRAGDROP_S_DROP。 删除数据。 然后,系统调用目标的 IDropTarget::D rop 方法。
  • DRAGDROP_S_CANCEL。 在不删除数据的情况下终止拖动循环。 如果按下 ESCAPE 键,则通常会返回此值。

有关进一步的讨论,请参阅 QueryContinueDragDoDragDrop 引用。

目标如何处理数据对象

当目标从剪贴板中检索数据对象或用户将其删除到目标窗口中时,该对象将接收数据对象。 然后,目标可以从数据对象中提取数据。 如有必要,目标还可以通知数据对象操作的结果。 在 Shell 数据传输之前,删除目标必须自行准备操作:

  1. 目标必须调用 RegisterClipboardFormat 才能获取数据对象中可能包括的所有 Shell 格式的有效剪贴板格式值(CF_HDROP)。 CF_HDROP已是有效的剪贴板格式,无需注册。
  2. 若要支持拖放操作,目标必须实现 IDropTarget 接口并注册目标窗口。 若要注册目标窗口,目标调用 RegisterDragDrop ,并在窗口的句柄和 IDropTarget 接口指针中传递。

对于剪贴板传输,目标不会收到任何通知,即数据对象已放置在剪贴板上。 通常,应用程序会收到用户操作(如单击应用程序工具栏上的“粘贴”按钮)在剪贴板上的通知。 然后,目标通过调用 OleGetClipboard 从剪贴板检索数据对象的 IDataObject 指针。 对于拖放数据传输,系统使用目标的 IDropTarget 接口向目标提供有关数据传输进度的信息:

有关如何实现这些方法的讨论,请参阅 IDropTarget

删除数据后, IDropTarget::D rop 向目标提供指向数据对象的 IDataObject 接口的指针。 然后,目标使用此接口从数据对象中提取数据。

从数据对象中提取 Shell 数据

从剪贴板中删除或检索数据对象后,目标可以提取所需的数据。 提取过程的第一步通常是枚举数据对象包含的格式:

  • 调用 IDataObject::EnumFormatEtc。 数据对象创建标准 OLE 枚举对象,并返回指向其 IEnumFORMATETC 接口的指针。
  • 使用 IEnumFORMATETC 方法枚举数据对象所包含的格式。 此操作通常检索对象包含的每个格式的一个 FORMATETC 结构。 但是,无论数据对象包含多少种此类格式,枚举对象通常只返回CFSTR_FILECONTENTS格式的单个 FORMATETC 结构。
  • 选择要提取的一个或多个格式,并存储其 FORMATETC 结构。

若要检索特定格式,请将关联的 FORMATETC 结构传递给 IDataObject::GetData。 此方法返回一个 STGMEDIUM 结构,该结构提供对数据的访问。 若要指定特定的数据传输机制,请将 FORMATETC 结构的 tymed 值设置为相应的 TYMED_XXX 值。 若要要求数据对象选择数据传输机制,目标将为目标可以处理的每个数据传输机制设置 TYMED_XXX 值。 数据对象选择其中一种数据传输机制,并返回相应的 STGMEDIUM 结构。

对于大多数格式,目标可以通过传递在枚举可用格式时收到的 FORMATETC 结构来检索数据。 此规则的一个例外是 CFSTR_FILECONTENTS。 由于数据对象可以包含此格式的多个实例,因此枚举器返回的 FORMATETC 结构可能与要提取的特定格式不对应。 除了指定 cfFormattymed 成员外,还必须将 lIndex 成员设置为文件的索引值。 有关进一步讨论,请参阅使用CFSTR_FILECONTENTS格式从处理 Shell 数据传输方案的文件部分提取数据

数据提取过程取决于返回的 STGMEDIUM 结构所包含的指针的类型。 如果结构包含指向 IStreamIStorage 接口的指针,请使用接口方法提取数据。 下一部分将讨论从全局内存对象提取数据的过程。

从数据对象中提取全局内存对象

许多 Shell 数据格式采用全局内存对象的形式。 使用以下过程从数据对象中提取包含全局内存对象的格式,并将其数据分配给本地变量:

  1. 创建 FORMATETC 结构。 将 cfFormat 成员设置为适当的剪贴板格式值,并将 tymed 成员设置为TYMED_HGLOBAL。

  2. 创建空 STGMEDIUM 结构。

  3. 调用 IDataObject::GetData,并传入指向 FORMATETCSTGMEDIUM 结构的指针。

    当 IDataObject::GetData 返回时,STGMEDIUM 结构将包含指向包含数据的全局内存对象的指针。

  4. 通过调用 GlobalLock 并传入 STGMEDIUM 结构的 hGlobal 成员,将数据分配给局部变量。

  5. 调用 GlobalUnlock 以释放全局内存对象的锁。

  6. 调用 ReleaseStgMedium 释放全局内存对象。

注意

必须使用 ReleaseStgMedium 释放全局内存对象,而不是 GlobalFree

 

以下示例演示如何从数据对象中提取存储为全局内存对象的 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 rop 参数的 pdwEffect 参数设置为适当的 DROPEFFECT 值,通知数据对象操作结果。 使用 Shell 数据对象时,目标可能还需要调用 IDataObject::SetData。 有关目标应如何响应不同数据传输方案的讨论,请参阅 处理 Shell 数据传输方案

以下部分简要介绍了如何实现 IDropTarget::D ragEnterIDropTarget::D ragOverIDropTarget::D rop 方法。 有关更多详细信息,请参阅参考文档。

DragEnter 方法

当光标进入目标窗口时,系统会调用 IDropTarget::D ragEnter 方法。 其参数为目标提供光标的位置、键盘修饰键(如 Ctrl 键)的状态,以及指向数据对象的 IDataObject 接口的指针。 目标负责使用该接口来确定它是否可以接受数据对象包含的任何格式。 如果可以,它通常保留 pdwEffect 的值不变。 如果它不能接受数据对象中的任何数据,它将 pdwEffect 参数设置为DROPEFFECT_NONE。 系统将此参数的值传递给数据对象的 IDropSource 接口,以允许它显示适当的拖动图像。

目标不应使用 IDataObject::GetData 方法在删除 Shell 数据之前呈现它。 完全呈现每个此类事件的对象数据可能会导致拖动光标停止。 为了避免此问题,某些 Shell 对象包含 CFSTR_INDRAGLOOP 格式。 通过提取此格式,目标可以检查拖动循环的状态,同时避免对对象的数据进行内存密集型呈现。 格式的数据值是 一个 DWORD ,如果数据对象位于拖动循环中,则设置为非零值。 如果数据已删除,则格式的数据值设置为零。

如果目标可以接受数据对象中的数据,则应检查 grfKeyState ,以确定是否已按下任何修改键来修改正常删除行为。 例如,默认操作通常是一个移动,但抑制 CTRL 键通常表示复制操作。

虽然光标位于目标窗口上方,但目标可以使用 拖放帮助程序对象 将数据对象的拖动图像替换为自己的拖动图像。 如果是这样, 则 IDropTarget::D ragEnter 应调用 IDropTargetHelper::D ragEnter ,以便将 DragEnter 参数中包含的信息传递给拖放帮助程序对象。

DragOver 方法

当游标在目标窗口中移动时,系统会定期调用 IDropTarget::D ragOver 方法。 其参数为目标提供光标的位置以及键盘修饰键(如 Ctrl 键)的状态。 IDropTarget::D ragOverIDropTarget::D ragEnter 具有相同的责任,实现通常非常相似。

如果目标使用的是拖放帮助程序对象, 则 IDropTarget::D ragOver 应调用 IDropTargetHelper::D ragOver ,将 DragOver 参数中包含的信息转发到拖放帮助程序对象。

Drop 方法

系统调用 IDropTarget::D rop 方法,以通知目标用户已删除数据,通常是通过释放鼠标按钮。 IDropTarget::D rop 的参数与 IDropTarget::D ragEnter 相同。 目标通常通过从数据对象中提取一个或多个格式来响应。 完成后,目标应将 pdwEffect 参数设置为指示操作结果的 DROPEFFECT 值。 对于某些类型的 Shell 数据传输,目标还必须调用 IDataObject::SetData ,以将操作结果的其他信息传递给数据对象。 有关详细讨论,请参阅 处理 Shell 数据传输方案

如果目标使用的是拖放帮助程序对象, 则 IDropTarget::D rop 应调用 IDropTargetHelper::D rop ,将 IDropTargetHelper::D ragOver 参数中包含的信息转发到拖放帮助程序对象。

使用拖放帮助程序对象

拖放帮助程序对象 (CLSID_DragDropHelper) 由 Shell 导出,以允许目标在目标窗口上方时指定拖动图像。 若要使用拖放帮助程序对象,请使用CLSID_DragDropHelper的类标识符 (CLSID) 调用 CoCreateInstance 来创建进程内服务器对象。 拖放帮助程序对象公开以下两个接口:

  • IDragSourceHelper 接口允许放置目标指定表示数据对象的图标。
  • IDropTargetHelper 接口允许放置目标通知光标位置的拖放帮助程序对象,并显示或隐藏数据图标。

使用 IDragSourceHelper 接口

IDragSourceHelper 接口由拖放帮助程序对象公开,以允许放置目标提供在光标位于目标窗口上方时显示的图像。 IDragSourceHelper 提供了两种替代方法,用于指定要用作拖动图像的位图:

使用 IDropTargetHelper 接口

此接口允许放置目标在光标进入或离开目标时通知拖放帮助程序对象。 虽然光标位于目标窗口上方, 但 IDropTargetHelper 允许目标向拖放帮助程序对象提供目标通过其 IDropTarget 接口接收的信息。

IDropTargetHelper 方法(IDropTargetHelper::D ragEnterIDropTargetHelper::D ragLeaveIDropTargetHelper::D ragOverIDropTargetHelper::D rop)与同名的 IDropTarget 方法相关联。 若要使用拖放帮助程序对象,每个 IDropTarget 方法应调用相应的 IDropTargetHelper 方法,将信息转发到拖放帮助程序对象。 第五个 IDropTargetHelper 方法 IDropTargetHelper::Show,通知拖放帮助程序对象以显示或隐藏拖动图像。 在低颜色深度视频模式下拖动目标窗口时,使用此方法。 它允许目标在绘制窗口时隐藏拖动图像。