使用拖放和剪贴板传输 Shell 对象

许多应用程序允许用户使用鼠标或剪贴板拖放数据,将数据传输到另一个应用程序。 可以传输的多种数据类型包括 Shell 对象,例如文件或文件夹。 Shell 数据传输可以在两个应用程序之间进行,但用户也可以从桌面或 Windows 资源管理器传输 Shell 数据。

尽管文件是最常传输的 Shell 对象,但 Shell 数据传输可能涉及 Shell 命名空间中找到的任何各种对象。 例如,应用程序可能需要将文件传输到虚拟文件夹(如回收站),或接受来自非 Microsoft 命名空间扩展的对象。 如果要实现命名空间扩展,它必须能够正确作为放置源和目标。

本文档讨论应用程序如何通过 Shell 对象实现拖放和剪贴板数据传输。

拖放与 Shell 对象的工作原理

应用程序通常需要为用户提供一种传输 Shell 数据的方法。 下面是一些示例:

  • 从 Windows 资源管理器或桌面拖动文件并将其放在应用程序上。
  • 将文件复制到 Windows 资源管理器中的剪贴板,并将其粘贴到应用程序中。
  • 将文件从应用程序拖到回收站。

有关如何处理这些方案和其他方案的详细讨论,请参阅 处理 Shell 数据传输方案。 本文档重点介绍 Shell 数据传输背后的一般原则。

Windows 为应用程序提供了两种传输 Shell 数据的标准方法:

  • 用户将 Shell 数据(如一个或多个文件)剪切或复制到剪贴板。 另一个应用程序从剪贴板检索数据。
  • 用户拖动一个图标,该图标表示源应用程序中的数据,并将该图标拖放到目标拥有的窗口中。

在这两种情况下,传输的数据都包含在 数据对象中。 数据对象是组件对象模型 (COM) 公开 IDataObject 接口的对象。 从图义上讲,所有 Shell 数据传输都必须遵循三个基本步骤:

  1. 源创建一个数据对象,该对象表示要传输的数据。
  2. 目标接收指向数据对象的 IDataObject 接口的指针。
  3. 目标调用 IDataObject 接口以从中提取数据。

剪贴板和拖放数据传输之间的区别主要在于 如何将 IDataObject 指针从源传输到目标。

剪贴板数据传输

剪贴板是传输 Shell 数据的最简单方法。 基本过程类似于标准剪贴板数据传输。 但是,由于要传输指向数据对象的指针,而不是数据本身,因此必须使用 OLE 剪贴板 API 而不是标准剪贴板 API。 以下过程概述了如何使用 OLE 剪贴板 API 通过剪贴板传输 Shell 数据:

  1. 数据源创建包含数据的数据对象。
  2. 数据源调用 OleSetClipboard,它将指向剪贴板上数据对象的 IDataObject 接口的指针。
  3. 目标调用 OleGetClipboard 来检索指向数据对象的 IDataObject 接口的指针。
  4. 目标通过调用 IDataObject::GetData 方法提取数据。
  5. 对于某些 Shell 数据传输,目标可能还需要调用数据对象的 IDataObject::SetData 方法,以便向数据对象提供有关数据传输结果的反馈。 有关此类操作的示例,请参阅 处理优化的移动操作

拖放数据传输

虽然实现数据拖放比较复杂,但与剪贴板不同,拖放数据传输具有一些显著优势:

  • 拖放传输可以通过简单的鼠标移动来完成,使操作比剪贴板更灵活、更直观。
  • 拖放为用户提供操作的可视表示形式。 用户可以跟随图标从源移动到目标。
  • 拖放功能在数据可用时通知目标。

拖放操作还使用数据对象来传输数据。 但是,放置源必须提供剪贴板传输所需的功能:

  • 放置源还必须创建公开 IDropSource 接口的对象。 在操作正在进行时,系统使用 IDropSource 与源通信。
  • 拖放数据对象负责跟踪光标移动并显示表示数据对象的图标。

删除目标还必须提供比处理剪贴板传输所需的更多功能:

  • 放置目标必须公开 IDropTarget 接口。 当光标位于目标窗口上方时,系统会使用 IDropTarget 向目标提供光标位置等信息,并在删除数据时通知它。
  • 放置目标必须通过调用 RegisterDragDrop 向系统注册自身。 此函数为系统提供目标窗口的句柄和指向目标应用程序的 IDropTarget 接口的指针。

注意

对于拖放操作,应用程序必须使用 OleInitialize 初始化 COM,而不是 CoInitialize

 

以下过程概述了通常用于通过拖放传输 Shell 数据的基本步骤:

  1. 目标调用 RegisterDragDrop ,为系统提供指向其 IDropTarget 接口的指针,并将窗口注册为放置目标。
  2. 当用户启动拖放操作时,源会创建一个数据对象,并通过调用 DoDragDrop 启动拖动循环
  3. 当光标位于目标窗口上方时,系统会通过调用目标的 IDropTarget 方法之一来通知目标。 当光标进入目标窗口时,系统会调用 IDropTarget::D ragEnter ,当光标通过目标窗口时,系统会调用 IDropTarget::D ragOver 。 这两种方法都为放置目标提供当前光标位置和键盘修饰键(如 CTRL 或 Alt)的状态。 当光标离开目标窗口时,系统会通过调用 IDropTarget::D ragLeave 来通知目标。 当这些方法中的任何一个返回时,系统会调用 IDropSource 接口,将返回值传递给源。
  4. 当用户释放鼠标按钮以删除数据时,系统会调用目标的 IDropTarget::D rop 方法。 方法的参数中有一个指向数据对象的 IDataObject 接口的指针。
  5. 目标调用数据对象的 IDataObject::GetData 方法来提取数据。
  6. 对于某些 Shell 数据传输,目标可能还需要调用数据对象的 IDataObject::SetData 方法,以便向源提供有关数据传输结果的反馈。
  7. 当目标使用完数据对象时,它将从 IDropTarget::D rop 返回。 系统返回源的 DoDragDrop 调用,通知源数据传输已完成。
  8. 根据特定的 数据传输方案,源可能需要根据 DoDragDrop 返回的值以及目标传递给数据对象的值执行其他操作。 例如,移动文件时,源必须检查这些值,以确定它是否必须删除原始文件。
  9. 源释放数据对象。

虽然上述过程为 Shell 数据传输提供了良好的常规模型,但 Shell 数据对象中可以包含许多不同类型的数据。 此外,应用程序可能需要处理许多不同的数据传输方案。 每种数据类型和方案都需要对过程中的三个关键步骤采用略有不同的方法:

  • 源如何构造数据对象以包含 Shell 数据。
  • 目标如何从数据对象中提取 Shell 数据。
  • 源如何完成数据传输操作。

Shell 数据对象提供了有关源如何构造 Shell 数据对象的常规讨论,以及目标如何处理该数据对象。 处理 Shell 数据传输方案 详细介绍了如何处理许多常见的 Shell 数据传输方案。