处理 Shell 数据传输方案

Shell 数据对象文档讨论了用于使用拖放或剪贴板传输 Shell 数据的一般方法。 但是,若要在应用程序中实现 Shell 数据传输,还必须了解如何将这些一般原则和技术应用于可以传输 Shell 数据的各种方法。 本文档介绍了常见的 Shell 数据传输方案,并讨论了如何在应用程序中实现每个方案。

注意

尽管每个方案都讨论了特定的数据传输操作,但其中许多方案适用于各种相关方案。 例如,大多数剪贴板和拖放传输之间的主要区别在于数据对象到达目标的方式。 一旦目标具有指向数据对象的 IDataObject 接口的指针,提取信息的过程在两种类型的数据传输中基本上相同。 但是,某些方案仅限于特定类型的操作。 有关详细信息,请参阅各个方案。

 

通用准则

以下每个部分都讨论了一个相当具体的数据传输方案。 但是,数据传输通常更为复杂,并且可能涉及多个方案的各个方面。 通常,你事先不知道实际需要处理哪种方案。 以下是一些要记住的一般准则。

对于数据源:

  • Shell 剪贴板格式(CF_HDROP除外)未预定义。 必须通过调用 RegisterClipboardFormat 来注册要使用的每个格式。
  • 数据对象中的格式按源的首选项顺序提供。 枚举数据对象,然后选择可以使用的第一个对象。
  • 包括尽可能多的格式,你可以支持。 通常不知道数据对象将被删除的位置。 这种做法改进了数据对象将包含放置目标可以接受的格式的几率。
  • 应以 CF_HDROP 格式提供现有文件。
  • 提供CFSTR_FILECONTENTS CFSTR_FILEDESCRIPTOR/格式的类似文件的数据。 此方法允许目标从数据对象创建文件,而无需知道有关基础数据存储的任何信息。 通常应将数据呈现为 IStream 接口。 此数据传输机制比全局内存对象更灵活,并且使用的内存要少得多。
  • 拖动源应在拖动 Shell 项时提供 CFSTR_SHELLIDLIST 格式。 可以通过 IShellFolder::GetUIObjectOfIShellItem::BindToHandler 方法获取项的数据对象。 数据源可以使用 SHCreateDataObject 创建支持CFSTR_SHELLIDLIST格式的标准数据对象实现。
  • 删除想要使用 shell 项编程模型拖动项的原因的目标可以使用 SHCreateShellItemArrayFromDataObject 将 IDataObject 转换为 IShellItemArray
  • 使用标准反馈游标。
  • 支持左右拖动。
  • 使用嵌入对象中的数据对象本身。 此方法允许应用程序检索数据对象必须提供的任何额外格式,并避免创建额外的包含层。 例如,服务器 A 中的嵌入对象从服务器/容器 B 中拖动并丢弃在容器 C 上。C 应创建服务器 A 的嵌入对象,而不是服务器 A 包含嵌入式对象的服务器 B 的嵌入对象。
  • 请记住,在移动文件时,Shell 可能会使用 优化的移动 或删除 粘贴 操作。 应用程序应能够识别这些操作并相应地做出响应。

对于数据目标:

  • Shell 剪贴板格式(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 接口,以将数据从其存储中呈现为文件。
  • 请记住,在移动文件时,Shell 可能会使用 优化的移动 或删除 粘贴 操作。 应用程序应能够识别这些操作并相应地做出响应。

将文件名从剪贴板复制到应用程序

场景: 用户在 Windows 资源管理器中选择一个或多个文件,并将其复制到剪贴板。 应用程序提取文件名并将其粘贴到文档中。

例如,此方案可用于允许用户通过剪切文件并将其粘贴到应用程序来创建 HTML 链接。 然后,应用程序可以从数据对象中提取文件名,并对其进行处理以创建定位标记。

当用户在 Windows 资源管理器中选择文件并将其复制到剪贴板时,Shell 将创建一个数据对象。 然后,它调用 OleSetClipboard ,将指针置于剪贴板上的数据对象的 IDataObject 接口。

当用户从应用程序的菜单或工具栏中选择 “粘贴” 命令时:

  1. 调用 OleGetClipboard 以检索数据对象的 IDataObject 接口。
  2. 调用 IDataObject::EnumFormatEtc 以请求枚举器对象。
  3. 使用枚举器对象的 IEnumFORMATETC 接口枚举数据对象包含的格式。

注意

此过程的最后两个步骤用于完成。 对于简单的文件传输,通常不需要它们。 用于此类数据传输的所有数据对象都应包含 CF_HDROP 格式,该格式可用于确定对象包含的文件的名称。 但是,对于更常规的数据传输,应枚举格式并选择应用程序可以处理的最佳格式。

 

从数据对象中提取文件名

下一步是从数据对象中提取一个或多个文件名并将其粘贴到应用程序中。 请注意,从数据对象中提取文件名的过程同样适用于拖放传输。

从数据对象 检索文件名的最简单方法是CF_HDROP 格式:

  1. 调用 IDataObject::GetData。 将 FORMATETC 结构的 cfFormat 成员设置为CF_HDROP,将 tymed 成员设置为TYMED_HGLOBALdwAspect 成员通常设置为DVASPECT_CONTENT。 但是,如果需要采用短 (8.3) 格式的文件路径,请将 dwAspect 设置为DVASPECT_SHORT。

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

  2. 创建 HDROP 变量并将其设置为 STGMEDIUM 结构的 hGlobal 成员。 HDROP 变量现在是 DROPFILES 结构的句柄,后跟包含复制文件的完全限定文件路径的双 null 终止字符串。

  3. 通过使用 iFile 参数设置为0xFFFFFFFF调用 DragQueryFile 来确定列表中的文件路径数。 该函数返回列表中的文件路径数。 此列表中的文件路径从零开始的索引用于下一步来标识特定路径。

  4. 通过为每个文件调用 DragQueryFile 一次,将 iFile 设置为文件的索引,从全局内存对象中提取文件路径。

  5. 根据需要处理文件路径,并将其粘贴到应用程序中。

  6. 调用 ReleaseStgMedium,并传入指向在步骤 1 中传递给 IDataObject::GetData 的 STGMEDIUM 结构的指针。 释放结构后,在步骤 2 中创建的 HDROP 值不再有效,不应使用。

将已删除文件的内容复制到应用程序中

场景: 用户从 Windows 资源管理器拖动一个或多个文件,并将其拖放到应用程序的窗口中。 应用程序提取文件的内容并将其粘贴到应用程序中。

此方案使用拖放将文件从 Windows 资源管理器传输到应用程序。 在操作之前,应用程序必须:

  1. 调用 RegisterClipboardFormat 以注册任何所需的 Shell 剪贴板格式。
  2. 调用 RegisterDragDrop 以注册目标窗口和应用程序的 IDropTarget 接口。

用户通过选择一个或多个文件并开始拖动它们来启动操作后:

  1. Windows 资源管理器创建一个数据对象,并将支持的格式加载到其中。
  2. Windows 资源管理器调用 DoDragDrop 来启动拖动循环。
  3. 当拖动图像到达目标窗口时,系统会通过调用 IDropTarget::D ragEnter 通知你。
  4. 若要确定数据对象包含的内容,请调用数据对象的 IDataObject::EnumFormatEtc 方法。 使用方法返回的枚举器对象来枚举数据对象所包含的格式。 如果应用程序不想接受这些格式中的任何一种,请返回DROPEFFECT_NONE。 出于此方案的目的,应用程序应忽略不包含用于传输文件的格式的任何数据对象,例如 CF_HDROP
  5. 当用户删除数据时,系统会调用 IDropTarget::D rop
  6. 使用 IDataObject 接口提取文件的内容。

可通过多种不同的方法从数据对象中提取 Shell 对象的内容。 一般情况下,请使用以下顺序:

如果数据提取过程较长,可能需要在后台线程上异步执行该操作。 然后,主线程可以继续,而不会造成不必要的延迟。 有关如何处理异步数据提取的讨论,请参阅 异步拖放 Shell 对象。

使用CFSTR_FILECONTENTS格式从文件中提取数据

CFSTR_FILECONTENTS格式提供了一种非常灵活且强大的方法来传输文件的内容。 甚至不需要将数据存储为单个文件。 此格式所需的所有内容就是数据对象将数据呈现给目标,就好像是文件一样。 例如,实际数据可能是文本文档的一部分或从数据库中提取的数据块。 目标可以将数据视为文件,不需要知道有关基础存储机制的任何信息。

命名空间扩展通常使用 CFSTR_FILECONTENTS 来传输数据,因为此格式不假定任何特定的存储机制。 命名空间扩展可以使用任何方便的存储机制,并使用此格式将其对象呈现给应用程序,就像它们是文件一样。

CFSTR_FILECONTENTS数据传输机制通常TYMED_ISTREAM。 传输 IStream 接口指针所需的内存比将数据加载到全局内存对象要少得多,IStream 是表示数据比 IStorage 更简单的方法。

CFSTR_FILECONTENTS格式始终附带CFSTR_FILEDESCRIPTOR格式。 必须首先检查此格式的内容。 如果要传输多个文件,则数据对象实际上将包含多个 CFSTR_FILECONTENTS 格式,每个文件对应一个。 CFSTR_FILEDESCRIPTOR格式包含每个文件的名称和属性,并为提取特定文件的CFSTR_FILECONTENTS格式所需的每个文件提供索引值。

若要提取 CFSTR_FILECONTENTS 格式,请执行以下操作:

  1. CFSTR_FILEDESCRIPTOR 格式提取为 TYMED_HGLOBAL 值。
  2. 返回的 STGMEDIUM 结构的 hGlobal 成员指向全局内存对象。 通过将 hGlobal传递给 GlobalLock锁定该对象。
  3. 将 GlobalLock 返回的指针强制转换为 FILEGROUPDESCRIPTOR 指针。 它将指向 FILEGROUPDESCRIPTOR 结构,后跟一个或多个 FILEDESCRIPTOR 结构。 每个 FILEDESCRIPTOR 结构都包含由随附 CFSTR_FILECONTENTS 格式之一包含的文件的说明。
  4. 检查 FILEDESCRIPTOR 结构以确定要提取的文件对应的结构。 FILEDESCRIPTOR 结构的从零开始的索引用于标识文件的CFSTR_FILECONTENTS格式。 由于全局内存块的大小不是字节精确的,因此请使用结构的 nFileSizeLownFileSizeHigh 成员来确定全局内存对象中表示文件的字节数。
  5. 调用 IDataObject::GetData,其 FORMATETC 结构的 cfFormat 成员设置为 CFSTR_FILECONTENTS 值,lIndex 成员设置为在上一步中确定的索引。 tymed 成员通常设置为TYMED_HGLOBAL |TYMED_ISTREAM |TYMED_ISTORAGE。 然后,数据对象可以选择其首选数据传输机制。
  6. IDataObject::GetData 返回的 STGMEDIUM 结构将包含指向文件数据的指针。 检查结构的 tymed 成员以确定数据传输机制。
  7. 如果 tymed 设置为 TYMED_ISTREAM 或TYMED_ISTORAGE,请使用接口提取数据。 如果 tymed 设置为TYMED_HGLOBAL,则数据包含在全局内存对象中。 有关如何从全局内存对象中提取数据的讨论,请参阅 Shell 数据对象的“从数据对象中提取全局内存对象”部分
  8. 调用 GlobalLock 以解锁在步骤 2 中锁定的全局内存对象。

处理优化的移动操作

场景: 使用优化的移动将文件从文件系统移动到命名空间扩展。

在传统的移动操作中,目标创建数据的副本,源会删除原始数据。 此过程可能效率低下,因为它需要两个数据副本。 对于大型对象(如数据库),传统的移动操作甚至可能并不实用。

通过优化的移动,目标使用对数据的存储方式的理解来处理整个移动操作。 数据永远不会有第二个副本,并且不需要源删除原始数据。 Shell 数据非常适合优化移动,因为目标可以使用 Shell API 处理整个操作。 典型的示例是移动文件。 目标具有要移动的文件的路径后,可以使用 SHFileOperation 移动它。 无需源删除原始文件。

注意

Shell 通常使用优化的移动来移动文件。 若要正确处理 Shell 数据传输,应用程序必须能够检测和处理优化的移动。

 

优化移动的处理方式如下:

  1. 源调用 DoDragDrop并将 dwEffect 参数设置为DROPEFFECT_MOVE以指示可以移动源对象。

  2. 目标通过其 IDropTarget 方法之一接收DROPEFFECT_MOVE值,指示允许移动。

  3. 目标将复制对象(未优化移动)或移动对象(优化移动)。

  4. 然后,目标会告知源是否需要删除原始数据。

    优化移动是默认操作,目标删除了数据。 若要通知源已执行优化移动,请执行:

    如果目标执行了未优化移动,则必须由源删除数据。 若要通知源,已执行未优化移动:

  5. 源检查目标可返回的两个值。 如果两者都设置为DROPEFFECT_MOVE,则通过删除原始数据来完成未优化移动。 否则,目标执行优化移动并删除了原始数据。

处理删除粘贴操作

方案: 从 Windows 资源管理器中的文件夹剪切一个或多个文件,并粘贴到命名空间扩展中。 Windows 资源管理器会突出显示文件,直到收到有关粘贴操作结果的反馈。

传统上,当用户剪切数据时,它会立即从视图中消失。 这可能无法有效,如果用户担心数据发生的情况,可能会导致可用性问题。 另一种方法是使用删除粘贴操作。

使用删除粘贴操作,所选数据不会立即从视图中删除。 相反,源应用程序会通过更改字体或背景色将其标记为选中。 在目标应用程序粘贴数据后,它会通知源操作的结果。 如果目标执行 优化移动,则源只需更新其显示即可。 如果目标执行了正常移动,则源还必须删除其数据副本。 如果粘贴失败,源应用程序会将所选数据还原为其原始外观。

注意

当剪切/粘贴操作用于移动文件时,Shell 通常会使用删除粘贴。 使用 Shell 对象的删除粘贴操作通常使用 经过优化的移动 来移动文件。 若要正确处理 Shell 数据传输,应用程序必须能够检测和处理粘贴时删除操作。

 

删除粘贴的基本要求是,目标必须将操作的结果报告给源。 但是,标准剪贴板技术不能用于实现粘贴时删除,因为它们不提供目标与源通信的方法。 相反,目标应用程序使用数据对象的 IDataObject::SetData 方法向数据对象报告结果。 然后,数据对象可以通过专用接口与源通信。

删除粘贴操作的基本过程如下所示:

  1. 源标记所选数据的屏幕显示。
  2. 源创建数据对象。 它通过添加 数据值为 DROPEFFECT_MOVE 的CFSTR_PREFERREDDROPEFFECT 格式来指示剪切操作。
  3. 源使用 OleSetClipboard 在剪贴板上放置数据对象。
  4. 目标使用 OleGetClipboard 从剪贴板检索数据对象。
  5. 目标提取 CFSTR_PREFERREDDROPEFFECT 数据。 如果它设置为仅DROPEFFECT_MOVE,则目标可以执行优化的移动或只是复制数据。
  6. 如果目标未执行优化的移动,它将调用 IDataObject::SetData 方法,并将CFSTR_PERFORMEDDROPEFFECT格式设置为DROPEFFECT_MOVE。
  7. 粘贴完成后,目标将调用 IDataObject::SetData 方法,并将CFSTR_PASTESUCCEEDED格式设置为DROPEFFECT_MOVE。
  8. 当源的 IDataObject::SetData 方法被调用 时,CFSTR_PASTESUCCEEDED 格式设置为DROPEFFECT_MOVE,它必须检查它是否还收到 设置为DROPEFFECT_MOVE的CFSTR_PERFORMEDDROPEFFECT 格式。 如果这两种格式都由目标发送,则源必须删除数据。 如果仅 收到CFSTR_PASTESUCCEEDED 格式,则源只需从其显示中删除数据。 如果传输失败,源会将显示更新为其原始外观。

向/从虚拟文件夹传输数据

场景: 用户从虚拟文件夹中拖动对象或将其拖放到虚拟文件夹中。

虚拟文件夹包含通常不属于文件系统的对象。 某些虚拟文件夹(如回收站)可以表示存储在硬盘上的数据,而不是普通文件系统对象。 有些可以表示远程系统上的存储数据,例如手持电脑或 FTP 站点。 其他对象(如 Printer 文件夹)包含的对象根本不表示存储的数据。 虽然某些虚拟文件夹是系统的一部分,但开发人员还可以通过实现命名空间扩展来创建和安装自定义虚拟文件夹。

无论数据类型或存储方式如何,Shell 包含的文件夹和文件对象都由 Shell 呈现,就像它们是普通文件和文件夹一样。 虚拟文件夹负责获取它包含的任何数据,并将其适当地呈现给 Shell。 此要求意味着虚拟文件夹通常支持拖放和剪贴板数据传输。

因此,需要关注传入和传出虚拟文件夹的两组开发人员:

  • 应用程序需要接受从虚拟文件夹传输的数据的开发人员。
  • 命名空间扩展需要正确支持数据传输的开发人员。

接受虚拟文件夹中的数据

虚拟文件夹几乎可以表示任何类型的数据,并且可以以任何方式存储该数据。 某些虚拟文件夹实际上可能包含正常的文件系统文件和文件夹。 例如,其他人可能将其所有对象打包到单个文档或数据库中。

将文件系统对象传输到应用程序时,数据对象通常包含具有 该对象的完全限定路径的 CF_HDROP格式。 应用程序可以提取此字符串,并使用普通文件系统函数打开文件并提取其数据。 但是,由于虚拟文件夹通常不包含普通文件系统对象,因此它们通常不使用 CF_HDROP

通常使用CFSTR_FILEDESCRIPTOR CFSTR_FILECONTENTS格式从虚拟文件夹中传输数据,而不是CF_HDROP/ CFSTR_FILECONTENTS格式与CF_HDROP有两个优势

  • 不采用任何特定的数据存储方法。
  • 格式更灵活。 它支持三种数据传输机制:全局内存对象、IStream 接口或 IStorage 接口。

全局内存对象很少用于将数据传输到虚拟对象或从虚拟对象传输数据,因为数据必须全部复制到内存中。 传输接口指针几乎不需要内存,而且效率更高。 对于非常大的文件,接口指针可能是唯一可行的数据传输机制。 通常,数据由 IStream 指针表示,因为该接口比 IStorage 更灵活。 目标从数据对象中提取指针,并使用接口方法提取数据。

有关如何处理CFSTR_FILEDESCRIPTOR/CFSTR_FILECONTENTS格式的进一步讨论,请参阅使用CFSTR_FILECONTENTS格式从文件中提取数据。

向/从 NameSpace 扩展传输数据

实现命名空间扩展时,通常需要支持拖放功能。 按照一般准则讨论的删除源和目标的建议进行操作。 具体而言,命名空间扩展必须:

  • 能够处理CFSTR_FILEDESCRIPTOR/CFSTR_FILECONTENTS格式。 这两种格式通常用于向命名空间扩展和传出对象。
  • 能够处理 优化的移动。 Shell 预期 Shell 对象将随优化移动一起移动。
  • 能够处理 删除粘贴 操作。 当使用剪切/粘贴操作从 Shell 移动对象时,Shell 使用删除/粘贴操作。
  • 能够通过 IStream IStorage 接口处理数据传输。 通常通过传输这两个接口指针之一(通常是 IStream 指针)来处理虚拟文件夹的数据传输。 然后,目标调用接口方法来提取数据:
      • 作为删除源,命名空间扩展必须从存储中提取数据,并将其通过此接口传递给目标。
      • 作为删除目标,命名空间扩展必须通过此接口接受来自源的数据并正确存储数据。

删除回收站上的文件

场景:用户删除回收站上的文件。 应用程序或命名空间扩展将删除原始文件。

回收站是一个虚拟文件夹,用作不再需要的文件的存储库。 只要回收站尚未清空,用户就可以稍后恢复该文件并将其返回到文件系统。

在大多数情况下,将 Shell 对象传输到回收站的工作方式非常类似于任何其他文件夹。 但是,当用户在回收站删除文件时,源需要删除原始文件,即使文件夹中的反馈指示复制操作。 通常,删除源无法知道其数据对象已删除的文件夹。 但是,对于 Windows 2000 及更高版本系统,在回收站上删除数据对象时,Shell 将调用数据对象的 IDataObject::SetData 方法,CFSTR_TARGETCLSID格式设置为回收站的类标识符(CLSID)(CLSID_RecycleBin)。 若要正确处理回收站大小写,数据对象应能够识别此格式并通过专用接口将信息传达给源。

注意

当使用设置为CLSID_RecycleBin的CFSTR_TARGETCLSID格式调用 IDataObject::SetData,数据源应在从方法返回之前关闭要传输的对象的任何打开句柄。 否则,可能会创建共享冲突。

 

创建和导入废旧文件

场景: 用户从 OLE 应用程序的数据文件中拖动某些数据,并将其拖放到桌面或 Windows 资源管理器中。

Windows 允许用户从 OLE 应用程序的数据文件中拖动对象,并将其拖放到桌面或文件系统文件夹中。 此操作将创建一个 报废文件,其中包含数据或指向数据的链接。 文件名取自为对象的 CLSID 注册的短名称以及 CF_TEXT 数据。 若要使 Shell 创建包含数据的废旧文件,应用程序的 IDataObject 接口必须支持剪贴板格式CF_EMBEDSOURCE。 若要创建包含链接的文件, IDataObject 必须支持CF_LINKSOURCE格式。

应用程序还可以实现三个可选功能来支持废旧文件:

  • 往返支持
  • 缓存的数据格式
  • 延迟呈现

往返支持

往返涉及将数据对象传输到另一个容器,然后传回原始文档。 例如,用户可以将一组单元格从电子表格传输到桌面,从而创建包含数据的废旧文件。 如果用户随后将废品传输回电子表格,则需要将数据集成到文档中,就像原始传输之前一样。

当 Shell 创建报废文件时,它将数据表示为嵌入对象。 当废品转移到另一个容器时,它作为嵌入对象传输,即使它被返回到原始文档也是如此。 应用程序负责确定报废中包含的数据格式,并在必要时将数据放回其本机格式。

若要建立嵌入对象的格式,请通过检索对象的CF_OBJECTDESCRIPTOR格式来确定其 CLSID。 如果 CLSID 指示属于应用程序的数据格式,则应传输本机数据,而不是调用 OleCreateFromData

缓存的数据格式

当 Shell 创建报废文件时,它会检查注册表中是否有可用格式的列表。 默认情况下,有两种格式可用:CF_EMBEDSOURCE和CF_LINKSOURCE。 但是,在某些情况下,应用程序可能需要采用不同格式的废旧文件:

  • 允许将碎片传输到无法接受嵌入式对象格式的非 OLE 容器。
  • 允许应用程序套件与专用格式通信。
  • 使往返更容易处理。

应用程序可以通过在注册表中缓存格式来向废品添加格式。 有两种类型的缓存格式:

  • 优先级缓存格式。 对于这些格式,数据将全部复制到数据对象的废品中。
  • 延迟呈现的格式。 对于这些格式,数据对象不会复制到废品。 相反,在目标请求数据之前,呈现会延迟。 下一部分将更详细地讨论延迟呈现。

若要添加优先级缓存或延迟呈现的格式,请在作为数据源的应用程序的 CLSID 键下创建 DataFormat 子项。 在该子项下,创建 PriorityCacheFormatsDelayRenderFormats 子项。 对于每个优先级缓存或延迟呈现的格式,请创建以零开头的编号子项。 将此键的值设置为格式的已注册名称的字符串或 #X 值,其中 X 表示标准剪贴板格式的格式号。

以下示例显示了两个应用程序的缓存格式。 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 接口将为目标提供延迟的呈现格式以及本机数据和缓存的数据。 如果目标请求延迟呈现格式,Shell 将运行应用程序,并从活动对象向目标提供数据。

注意

由于延迟呈现有点危险,因此应谨慎使用。 如果服务器不可用或未启用 OLE 的应用程序,则它不起作用。

 

异步拖放 Shell 对象

场景: 用户将一大块数据从源传输到目标。 为了避免在相当长时间内阻止这两个应用程序,目标异步提取数据。

通常,拖放是同步操作。 简单地说:

  1. 删除源调用 DoDragDrop 并阻止其主线程,直到函数返回。 阻止主线程通常会阻止 UI 处理。
  2. 调用目标的 IDropTarget::D rop 方法后,目标将从其主线程上的数据对象中提取数据。 此过程通常会在提取过程期间阻止目标的 UI 处理。
  3. 提取数据后,目标将返回 IDropTarget::D rop 调用,系统将返回 DoDragDrop,并且两个线程都可以继续。

简言之,同步数据传输可能会长时间阻止这两个应用程序的主线程。 具体而言,当目标提取数据时,两个线程必须等待。 对于少量数据,提取数据所需的时间很小,同步数据传输效果很好。 但是,同步提取大量数据可能会导致长时间的延迟,并干扰目标和源的 UI。

IAsyncOperation/IDataObjectAsyncCapability 接口是可由数据对象实现的可选接口。 它使放置目标能够在后台线程上异步提取数据对象中的数据。 将数据提取移交到后台线程后,这两个应用程序的主线程均可继续。

使用 IASyncOperation/IDataObjectAsyncCapability

注意

接口最初名为 IAsyncOperation,但后来更改为 IDataObjectAsyncCapability 否则,这两个接口是相同的。

 

IAsyncOperation/IDataObjectAsyncCapability 的目的是允许放置源和删除目标协商是否可以异步提取数据。 以下过程概述了放置源如何使用接口:

  1. 创建公开 IAsyncOperation/IDataObjectAsyncCapability 的数据对象。
  2. 调用设置为 fDoOpAsync 的 SetAsyncMode VARIANT_TRUE以指示支持异步操作。
  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 通知数据对象提取已完成。