MSDN 2005 -> Win32 和 COM 开发 -> User Interface -> Windows User Experience -> Windows Shell -> Windows Shell -> Shell Programmer's Guide -> Shell Basics -> Transferring Shell Objects with Drag-and-Drop and the Clipboard
很多应用程序可以让用户通过鼠标拖放或者剪贴板把数据传输到其他程序中。像文件和文件夹这样的Shell对象是可以传输的众多数据类型之一。Shell数据传输可以发生在两个应用程序间,当然也可以在应用程序与桌面或者资源管理器间传输数据。
虽然最经常传输的Shell对象是文件,但Shell名字空间中的任何对象都是可以传输的。比如说,程序可能需要传输文件到像回收站这样的虚拟文件夹中,或者从非微软名字空间扩展处接受对象。如果要实现名字空间扩展,必须使它具有作为拖放源和拖放目标的正确行为。
本文讨论如何实现用拖放和剪贴板传输Shell对象。
Shell对象是如何被拖放的
应用程序通常要为用户提供传输Shell数据的方法。比如说:
- 从桌面或者资源管理器中拖动文件放置到应用程序中
- 在资源管理器中复制文件到剪贴板,然后粘贴到应用程序中
- 从应用程序中拖动文件放置到回收站中
关于如何处理这些情况和其他情况的详细讨论,见Handling Shell Data Transfer Scenarios,它讨论了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方法为数据对象提供关于数据传输结果的反馈。关于这种操作的示例,见Handling Optimized Move Operations。
拖放数据传输
虽然比剪贴板数据传输实现起来复杂一些,但拖放数据传输具备下列优点:
- 拖放数据传输可以通过简单的鼠标移动完成,比剪贴板操作灵活直观
- 拖放为用户提供了操作的可视表示。用户可以跟踪把图标从源移动到目标的过程
- 拖放会在数据可用的时候通知拖放目标
拖放操作也用数据对象传输数据,然而放置源必须提供多于剪贴板传输所需的功能:
- 放置源必须创建暴露IDropSource接口的对象。操作进行过程中系统用IDropSource与拖放源进行通信。
- 拖放数据对象要负责跟踪光标移动并且显示代表数据对象的图标
放置目标也必须提供多于处理剪贴板传输所需的功能:
注意: 对于拖放操作,程序必须用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返回,然后系统从传输源中的DoDrogDrop方法返回,通知传输源传送完成。
-
对于某些数据传输的情况,传输源可能需要根据DoDragDrop方法的返回值,以及传输目标传递给数据对象的值,而进行额外的动作。比如说,如果进行的是文件移动操作,拖放源必须检查这两个值以确定是否要删除原文件。
-
传输源释放数据对象。
上述过程是Shell数据传输的通用模型,然而Shell数据对象可以包含各种不同类型的数据,应用程序可能需要处理各种不同类型数据传输的情况。每种数据类型和传输情况要求对上述过程中三个关键步骤进行一些不同的处理:
-
传输源如何构造包含Shell数据的数据对象
-
传输目标如何从数据对象中取出Shell数据
-
传输源如何完成数据传输操作
Shell Data Object 提供了对于传输源如何构造Shell数据对象的一般讨论,它还讨论了传输目标如何处理数据对象。Handling Shell Data Transfer Scenarios 详细讨论了如何处理各种通用Shell数据传输。