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 格式,以允许数据对象包含每个格式的多个数据项。 有关如何使用此格式的讨论,请参阅处理 Shell 数据传输方案的使用CFSTR_FILECONTENTS格式从文件中提取数据部分。

  • 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 接口。 目标使用 DROPEFFECT 值做出响应,该值通过 GiveFeedback 方法转发到源。 如果合适,源可以根据 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 成员设置为文件的索引值。 有关进一步的讨论,请参阅处理 Shell 数据传输方案中的使用CFSTR_FILECONTENTS格式从文件中提取数据部分

数据提取过程取决于返回的 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 roppdwEffect 参数设置为相应的 DROPEFFECT 值,将数据对象告知操作的结果。 对于 Shell 数据对象,目标可能还需要调用 IDataObject::SetData。 有关目标应如何响应不同数据传输方案的讨论,请参阅 处理 Shell 数据传输方案

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

DragEnter 方法

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

在删除 Shell 数据之前,目标不应使用 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 ropIDropTargetHelper::D ragOver 参数中包含的信息转发到拖放帮助程序对象。

使用拖放帮助程序对象

拖放帮助程序对象 (CLSID_DragDropHelper) 由 Shell 导出,以允许目标在位于目标窗口上方时指定拖动图像。 若要使用拖放帮助程序对象,请通过使用类标识符 (CLSID) CLSID_DragDropHelper 调用 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 通知拖放帮助程序对象显示或隐藏拖动图像。 在低颜色深度视频模式下拖动目标窗口时,使用此方法。 它允许目标在绘制窗口时隐藏拖动图像。