处理 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::GetDataSTGMEDIUM 结构的指针。 释放结构后,在步骤 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. 源调用将 dwEffect 参数设置为 DROPEFFECT_MOVE 的 DoDragDrop,以指示可以移动源对象。

  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 站点)上的存储数据。 其他对象(如“打印机”文件夹)包含的对象根本不表示存储的数据。 虽然某些虚拟文件夹是系统的一部分,但开发人员还可以通过实现命名空间扩展来创建和安装自定义虚拟文件夹。

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

因此,有两组开发人员需要关注与虚拟文件夹的数据传输:

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

接受虚拟文件夹中的数据

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

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

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

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

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

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

向/从 NameSpace 扩展传输数据

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

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

在回收站上删除文件

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

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

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

注意

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

 

创建和导入报废文件

场景: 用户从 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. 调用 SetAsyncMode ,将 fDoOpAsync 设置为 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 通知数据对象提取已完成。