Shell数据对象(三):目标如何处理数据对象

来源:互联网 发布:不一样的天空知乎 编辑:程序博客网 时间:2024/05/24 01:30

MSDN 2005 -> Win32 和 COM 开发 -> User Interface -> Windows User Experience -> Windows Shell -> Windows Shell -> Shell Programmer's Guide -> Shell Basics -> The Shell Data Object 

3. 目标如何处理数据对象

目标可以从剪贴板接收数据对象,或者当数据对象在目标窗口上放下时接收它。然后目标就可以从数据对象中取出数据。如果必要的话,目标还会通知数据对象操作的结果。在开始Shell数据传输前,放置目标必须做下列准备操作:

  1. 目标必须调用RegisterClipboardFormat为除CF_HDROP外的所有可能包含在数据对象中的Shell格式获取有效的剪贴板格式值。CF_HDROP已经是有效的剪贴板格式,不需要注册。
  2. 为支持拖放操作,目标必须实现IDropTarget接口并且注册一个目标窗口。目标调用RegisterDragDrop,传入窗口句柄和IDropTarget接口指针来注册目标窗口。

对于剪贴板传输,目标不会在数据对象被放置到剪贴板中时收到任何通知。通常由一个用户操作,比如说,点击程序工具栏上的粘贴按钮,来通知程序剪贴板中有数据对象可用。然后目标调用OleGetClipboard从剪贴板中获取数据对象的IDataObject指针。而对于拖放数据传输,系统使用目标的IDropTarget接口为目标提供数据传输进度的相关信息:

  • 光标进入目标窗口时系统调用IDropTarget::DragEnter
  • 光标滑过目标窗口时系统不时地调用IDropTarget::DragOver告知目标当前光标位置。
  • 光标离开目标窗口时系统调用IDropTarget::DragLeave
  • 用户在目标窗口上放下数据对象时系统调用IDropTarget::Drop

关于如何实现这些方法的更深入讨论,见本文的Implementing IDropTarget节。

数据被放下时,IDropTarget::Drop为目标提供数据对象的IDataObject接口指针,目标使用这个指针从数据对象中取出数据。 

3.1 从数据对象中取出Shell数据

数据对象被放下,或者从剪贴板接收到数据对象后,目标会取出所需的数据。取出数据操作的第一步通常是枚举对象所包含的格式:

  • 调用IDataObject::EnumFormatEtc。数据对象会创建一个标准的OLE枚举对象并返回其IEnumFORMATETC接口指针。
  • 使用IEnumFORMATETC接口的方法来枚举数据对象所包含的格式。通常对于所包含的每种格式,都会收到一个FORMATETC结构体。但是,对于CFSTR_FILECONTENTS格式,枚举对象通常只会返回一个FORMATETC结构体,而不论数据对象包含了多少个这种格式的数据项。
  • 选择一个或者多个要取出其数据的格式,存储相关的FORMATETC结构体。

要获取特定格式的数据,调用IDataObject::GetData,传入相关的FORMATETC结构体。这个方法会返回用以访问数据的STGMEDIUM结构体。要指定特定的数据传输机制,把FORMATETC结构体的tymed成员设置为相应的TYMED_XXX值。要让数据对象选择数据传输机制,把FORMATETC结构体的tymed成员设置为能够处理的所有数据传输机制的相关TYMED_XXX值。这时数据对象会从这些数据传输机制中选择一种,返回合适的STGMEDIUM结构体。

对于大多数格式,传入枚举可用格式时取得的FORMATETC结构体就可以取得相应的数据了,但CFSTR_FILECONTENTS格式是个例外。因为数据对象可以包含多个这种格式的数据项,枚举格式时返回的FORMATETC结构体可能并不对应想要取出的特定数据项。除了cfFormattymed成员外,还必须设置lIndex成员为文件索引值。更深入的讨论,见Using the CFSTR_FILECONTENTS Format to Extract Data from a File

数据取出过程决定于返回的STGMEDIUM结构体所包含的指针。如果结构体包含IStream或者IStorage接口指针,则使用相关的接口方法取出数据。从全局存储对象中取出数据的过程在下一节讨论。 

3.2 从数据对象中取出全局存储对象

很多Shell数据格式以全局存储对象的形式存在。从数据对象中的全局存储对象里取出数据,赋值给局部变量的过程如下:

  1. 创建FORMATETC结构体。设置cfFormat成员为合适的剪贴板格式值,tymed成员为TYMED_HGLOBAL
  2. 创建一个空的STGMEDIUM结构体。
  3. 调用IDataObject::GetData,传入FORMATETCSTGMEDIUM结构体指针。方法返回时,STGMEDIUM结构体会包含全局存储对象的指针。
  4. 调用GlobalLock,传入STGMEDIUM结构体的hGlobal成员,就可以把数据赋值给局部变量了。
  5. 调用GlobalUnlock释放对全局存储对象的锁定。
  6. 调用ReleaseStgMedium释放全局存储对象。

注意:必须用ReleaseStgMedium释放全局存储对象,而不是GlobalFree。

下面的示例展示了如何从数据对象中的全局存储对象里取出DWORD值。pdtobj是数据对象的IDataObject接口指针,cf是标识所需数据的剪贴板格式值,pdwOut用以返回数据值。 

<…… 省略示例代码 ……> 

3.3 实现IDropTarget

光标位于目标窗口上时,系统使用IDropTarget接口与目标进行通信。目标的响应通过拖放源的IDropSource接口转发给拖放源。根据响应,拖放源可以修改代表数据的图标。如果放置目标需要指定数据图标,则可以通过创建拖放辅助对象实现。

按照惯例,目标设置IDropTarget::DroppdwEffect参数为合适的DROPEFFECT值,向数据对象通知操作的结果。对于Shell数据对象,目标也可能需要调用IDataObject::SetData。关于目标应该如何处理不同的数据传输情形,见Handling Shell Data Transfer Scenarios

下面的几节将简要讨论如何实现IDropTarget::DragEnterIDropTarget::DragOverIDropTarget::Drop方法。更多的细节见相关参考文档。 

3.3.1. DragEnter 方法

光标进入目标窗口时系统调用IDropTarget::DragEnter方法。方法参数为目标提供光标位置、CTRL等键盘修饰键状态和数据对象的IDataObject接口指针。目标应该使用IDataObject接口确定是否可以接受数据对象中某种格式的数据。如果可以,通常保持pdwEffect值不变。如果不能接受数据对象中的任何数据,则设置pdwEffect参数为DROPEFFECT_NONE。系统会把这个值传递给数据对象的IDropSource接口,使对象可以显示合适的拖动图像。

在放下数据前,目标不应该使用IDataObject::GetData方法获取Shell数据并进行绘制,这可能会使拖动光标停滞。为避免这个问题,有些Shell对象包含CFSTR_INDRAGLOOP格式的数据。通过取出这种格式的数据,目标可以检查拖动循环的状态,而避免密集地绘制存储器中的对象数据。这种格式的数据是一个DWORD值,非零表示对象处于拖动循环中;数据被放下时,这个值被设置为零。

如果目标可以接受数据对象中的数据,则应该检查grfKeyState来确定是否按下了任何改变放置行为的修饰键。比如说,通常默认的操作是移动,但是按下CTRL键则表示复制。

光标位于目标窗口上时,目标可以使用拖动辅助对象来替换数据对象的拖动图像。如果需要替换的话,IDropTarget::DragEnter应该调用IDropTargetHelper::DragEnter,传入IDropTarget::DragEnter参数所包含的信息到拖放辅助对象中。 

3.3.2. DragOver方法

光标位于目标窗口上时,系统会不时地调用IDropTarget::DragOver方法。方法参数会为目标提供光标位置和CTRL等修饰键状态。IDropTarget::DragOver的职责跟IDropTarget::DragEnter几乎相同,因而通常它们的实现也非常类似。

如果目标使用了拖放辅助对象,IDropTarget::DragOver应该调用IDropTargetHelper::DragOver方法,把IDropTarget::DragOver参数包含的信息转发给拖放辅助对象。 

3.3.3. Drop方法

用户放下数据时,系统会调用IDropTarget::Drop方法通知目标。放下操作通常是通过释放鼠标键进行的。IDropTarget::Drop的参数跟IDropTarget::DragEnter相同。目标对方法的响应通常是从数据对象中取出一种或者多种格式的数据。取出数据操作完成后,目标应该设置pdwEffect参数为某个DROPEFFECT值来指示操作的结果。对于某些类型的Shell数据传输,目标还应该调用IDataObject::SetData,把带有操作结果相关信息的数据传递给数据对象。更详尽的讨论,见Handling Shell Data Transfer Scenarios

如果目标使用了拖放辅助对象,IDropTarget::Drop应该调用IDropTargetHelper::Drop,把IDropTargetHelper::DragOver参数所包含的信息转发给拖放辅助对象。 

3.4 使用拖放辅助对象

Shell导出了拖放辅助对象(CLSID_DragDropHelper),使得拖放图像位于目标窗口上时,目标可以指定拖放图像。要创建拖放辅助对象,调用CoCreateInstance,传入CLSID_DragDropHelper以创建一个进程内服务器对象。拖放辅助对象暴露两个接口,它们的使用方法如下:

  • IDragSourceHelper让拖放目标可以指定代表数据对象的图标。
  • IDropTargetHelper让拖放目标可以通知拖放辅助对象光标的位置,以及显示或者隐藏数据图标。 

3.4.1.使用IDragSourceHelper接口

拖放辅助对象暴露的IDropSourceHelper接口让拖放目标可以提供光标位于目标窗口上时要显示的图像。 IDragSourceHelper提供了两种可供选择的,用以指定用作拖放图像的位图的方法:

  • 有窗口的目标可以用IDragSourceHelper::InitializeFromWindow初始化拖放辅助对象,从而为自己注册DI_GETDRAGIMAGE窗口消息。目标收到DI_GETDRAGIMAGE消息时,把用作拖放图像的位图信息放到作为消息lParam参数传入的SHDRAGIMAGE结构体中。
  • 没有窗口的目标用IDragSourceHelper::InitializeFromBitmap初始化拖放辅助对象,指定用作拖放图像的位图。 

3.4.2. 使用IDropTargetHelper接口

这个接口让放置目标可以在光标进入或者离开目标窗口时通知拖放辅助对象。光标位于目标窗口上时,目标用IDropTargetHelper以为拖放辅助对象提供自己从IDropTarget接口接收到的信息。

IDropTargetHelper的四个方法:IDropTargetHelper::DragEnterIDropTargetHelper::DragLeaveIDropTargetHelper::DragOverIDropTargetHelper::DropIDropTarget的同名方法相关联。要使用拖放辅助对象,IDropTarget方法应该调用相应的IDropTargetHelper方法转发信息给拖放辅助对象。IDropTargetHelper的第五个方法,IDropTargetHelper::Show,用以通知拖放辅助对象显示或者隐藏拖放图像。这个方法在滑过处于低色深视频模式的目标窗口时使用。它让目标在绘制窗口时可以隐藏拖动图像。


菊子曰 这就是菊子曰啦!
原创粉丝点击