许多应用程序允许用户使用鼠标拖放数据或使用剪贴板将数据传输到另一个应用程序。 可以传输的许多类型的数据包括 Shell 对象,例如文件或文件夹。 Shell 数据可以在两个应用程序之间传输,但用户也可以将 Shell 数据传输到桌面或 Windows 资源管理器,或者从桌面或 Windows 资源管理器传输到 Shell。
尽管文件是最常传输的 Shell 对象,但 Shell 数据传输可能涉及在 Shell 命名空间中找到的各种对象中的任何一种。 例如,应用程序可能需要将文件传输到虚拟文件夹(如回收站),或者接受来自非Microsoft命名空间扩展的对象。 如果要实现命名空间扩展,它必须能够作为拖放源和目标正常运行。
本文档讨论应用程序如何使用 Shell 对象实现拖放和剪贴板数据传输。
拖放如何与 Shell 对象配合使用
应用程序通常需要为用户提供传输 Shell 数据的方法。 下面是一些示例:
- 从Windows资源管理器或桌面拖动文件并将其拖放到应用程序中。
- 在Windows资源管理器中将文件复制到剪贴板,并将其粘贴到应用程序中。
- 将文件从应用程序拖动到回收站。
有关如何处理这些方案和其他方案的详细讨论,请参阅 处理 Shell 数据传输方案。 本文档重点介绍 Shell 数据传输背后的一般原则。
Windows为应用程序提供两种传输 Shell 数据的标准方法:
- 用户将 Shell 数据(例如一个或多个文件)剪切或复制到剪贴板。 另一个应用程序从剪贴板检索数据。
- 用户将代表源应用程序中数据的图标从源应用程序中拖出,并将其拖放到目标应用程序所属的窗口上。
在这两种情况下,传输的数据都包含在 数据对象中。 数据对象是提供 IDataObject 接口的组件对象模型(COM)对象。 从原理上讲,所有 Shell 数据传输都必须遵循三个基本步骤:
- 源会创建一个数据对象,该对象表示要传输的数据。
- 目标接收指向数据对象的 IDataObject 接口的指针。
- 目标调用 IDataObject 接口以从中提取数据。
剪贴板和拖放数据传输之间的区别主要在于 IDataObject 指针从源传输到目标的方式。
剪贴板数据传输
剪贴板是传输 Shell 数据的最简单方法。 基本过程类似于标准剪贴板数据传输。 但是,由于你要将指针传输到数据对象,而不是数据本身,因此必须使用 OLE 剪贴板 API 而不是标准剪贴板 API。 以下过程概述了如何使用 OLE 剪贴板 API 通过剪贴板传输 Shell 数据:
- 数据源创建一个数据对象以包含数据。
- 数据源调用 OleSetClipboard,它将指向数据对象的 IDataObject 接口的指针放到剪贴板上。
- 目标调用 OleGetClipboard 来检索指向数据对象的 IDataObject 接口的指针。
- 目标通过调用 IDataObject::GetData 方法提取数据。
- 对于某些 Shell 数据传输,目标可能还需要调用数据对象的 IDataObject::SetData 方法,以向数据对象提供有关数据传输结果的反馈。 有关此类操作的示例,请参阅“处理优化的移动操作”。
拖放数据传输
尽管实现更为复杂,但拖放数据传输相比剪贴板具有一些显著优势:
- 拖放传输可以通过简单的鼠标移动来完成,使操作比剪贴板更灵活、更直观。
- 拖放为用户提供操作的可视表示形式。 用户可以跟踪图标,当它从源移动到目标时。
- 当数据可用时,拖放操作会通知目标对象。
拖放操作还使用数据对象来传输数据。 但是,拖放源必须提供剪贴板传输所需之外的功能:
- 拖放源还必须创建一个对象,该对象公开 IDropSource 接口。 在操作正在进行时,系统使用 IDropSource 与源通信。
- 拖放数据对象负责跟踪光标移动并显示用于表示数据对象的图标。
放置目标还必须提供比处理剪贴板传输所需的更多功能:
- 放置目标必须公开 IDropTarget 接口。 当光标悬停在目标窗口上时,系统会使用 IDropTarget 向目标窗口提供光标位置等信息,并在数据被拖放时通知该窗口。
- 放置目标必须通过调用 RegisterDragDrop 向系统注册自身。 此函数为系统提供目标窗口的句柄和指向目标应用程序的 IDropTarget 接口的指针。
注释
对于拖放操作,应用程序必须使用 OleInitialize 初始化 COM,而不是 CoInitialize。
以下过程概述了通常用于使用拖放传输 Shell 数据的基本步骤:
- 目标调用 RegisterDragDrop ,为系统提供指向其 IDropTarget 接口的指针,并将窗口注册为放置目标。
- 当用户启动拖放操作时,源将创建一个数据对象,并通过调用 DoDragDrop 启动拖动循环。
- 当光标位于目标窗口上方时,系统会通过调用目标的 IDropTarget 方法之一来通知目标。 当游标进入目标窗口时,系统会调用 IDropTarget::DragEnter,当游标在目标窗口上方移动时,系统会调用 IDropTarget::DragOver。 这两种方法都为放置目标提供当前光标位置和键盘修饰符键(如 CTRL 或 ALT)的状态。 当光标离开目标窗口时,系统会通过调用 IDropTarget::DragLeave 通知目标。 当其中任一方法返回时,系统将调用 IDropSource 接口,以将返回值传递给拖放源对象。
- 当用户松开鼠标按钮以释放数据时,系统会调用目标对象的 IDropTarget::Drop 方法。 方法的参数中有一个指向数据对象的 IDataObject 接口的指针。
- 目标调用数据对象的 IDataObject::GetData 方法来提取数据。
- 在某些 Shell 数据传输中,目标方可能还需要调用数据对象的 IDataObject::SetData 方法,向源方反馈数据传输的结果。
- 当目标使用完数据对象后,它将从 IDropTarget::Drop返回。 系统会返回源对象的 DoDragDrop 调用,以通知源对象数据传输已完成。
- 根据特定的 数据传输方案,源可能需要根据 DoDragDrop 返回的值以及目标传递给数据对象的值执行其他操作。 例如,移动文件时,源必须检查这些值,以确定它是否必须删除原始文件。
- 源释放数据对象。
虽然上述过程为 Shell 数据传输提供了良好的常规模型,但 Shell 数据对象中有许多不同类型的数据。 应用程序可能需要处理许多不同的数据传输方案。 每种数据类型和方案都需要对过程中的三个关键步骤采用一种略有不同的方法:
- 源如何构造数据对象以包含 Shell 数据。
- 目标如何从数据对象中提取 Shell 数据。
- 源如何完成数据传输操作。
Shell 数据对象对源如何构建一个 Shell 数据对象,以及目标如何处理该数据对象进行了概述。 处理 Shell 数据传输方案 详细介绍了如何处理许多常见的 Shell 数据传输方案。