Windows 2000 UI 新特点之一:信息条提示(Infotip)
Windows 2000 UI 新特点之一:信息条提示(Infotip)
编译/赵湘宁
本文假设您熟悉windows的外壳编程及ATL。

Windows 2000引入了许多很有用的用户界面新特点,我们能在自己的应用程序中定制和实现这些用户界面。本文是由一系列文章组成,将探讨包括信息提示(Infotip)、增强文件夹特性、搜索管理、图标覆盖和快速启动工具条在内的几个新的UI编程,使用它们来丰富系统外壳的功能。
阅读本篇系列文章之后,您将学会Windows 2000用户界面编程的几个新特点:
如何为文件提供“infotips”(信息条提示)特性;
如何创建一个定制的栏目管理器扩展(column handler extension),它使我们能通过Windows的资源管理器在“查看”菜单的“详细资料”栏的视图中看到新创建的文件属性栏目。
为了进一步扩展外壳,我们还将另外实现几个界面特点:搜索管理、清除管理、使用属性页的文件夹定制、图标覆盖、以及上下文菜单外壳扩展。
随着 Windows 操作系统的不断更新,其用户界面也在随之推陈出新,甚至有一些人已经在抱怨微软公司花太多的精力来开发用户界面,而在操作系统的其它功能上创新不够。这个说法是否正确,有待广大Windows用户来评判。
探究 Windows 2000 的几个UI新特点。可以发现Windows用户界面的几个发展趋势:首先是脚本,尤其是WSH将取代命令行;其次是新的用户界面为将来定制自己的应用程序提供了更好的(基于COM的)图形用户界面接口;第三是应用程序将与系统外壳(SHELL)结合得更紧密。

外壳扩展是一个COM进程内服务器,它由资源管理器 (Explorer) 来调用以响应系统外壳内发生的事件。只有少数几个任务是靠资源管理器与用户定义的应用来协作完成。在开始这些任务之前,资源管理器查找这些注册的模块并加载它们。从概念上讲,这些模块相当于回调函数。而回调函数是由Windows3.1引入并影响了整整一代程序员的著名编程特点。
外壳扩展需要实现一对COM接口:一个提供特定行为,另一个用于初始化目的。另外,外壳扩展必须要有精确的注册方案,它们必须在适当的地方创建正确的注册入口以便资源管理器在需要时能找到并加载它们。
下表列出了目前可获得的所有外壳扩展类型,需要的最小版本号,有关的接口和简单描述:
(表一)
类型
类型说明
适用于
版本
有关的接口
描述
Context Menu
上下文菜单
文件类和外壳对象
Windows 9x
IContextMenu、IContextMenu2、IContextMenu3
允许在外壳对象的上下文菜单中增加新的才单项
Right drag and drop
右拖拽
文件类和外壳对象
Windows 9x
IContextMenu、IContextMenu2、IContextMenu3
允许在右拖拽后出现的上下文菜单中增加新的才单项
Drawing shell Icons
绘制外壳图标
文件类和外壳对象
Windows 9x
IExtractIcon
对于一个文件类来说,可以选择文件在运行时应该显示那个图标
Property Sheet
属性页
文件类和外壳对象
Windows 9x
IShellPropSheetExt
向文件类属性对话框中加入另外的属性表页。也适用于控制面板应用
Left drag and drop
左拖拽
文件类和外壳对象
Windows 9x
IDropTarget
决定在外壳内用鼠标左键拖拽一个对象到另一个对象上时做什么
Clipboard
剪贴板
文件类和外壳对象
Windows 9x
IDataObject
定义如何将对象拷贝到剪贴板以及如何从剪贴板吸取对象
File Hook
文件钩
Windows 9x
ICopyHook
控制整个外壳内的任何文件操作。您可以允许或拒绝这些对文件的操作,但不会通知您成功或失败
Program Execution
外壳执行程序
资源管理器
桌面更新
IShellExecuteHook
拦截(钩)外壳内任何程序的执行
Infotip
信息条提示
文件类和外壳对象
桌面更新
IQueryInfo
当鼠标移到某个文件类型文档上时显示简短文本信息
Column
栏目
文件夹
Windows 2000
IColumnProvider
在资源管理器“查看”菜单的“详细资料”视图中增加新的栏目
Icon Overlay
图标覆盖
资源管理器
Windows 2000
IShellIconOverlay
用定制的图像覆盖图标
Search
搜索
资源管理器
Windows 2000
IContextMenu
在“开始”菜单的“搜索”菜单项中增加新的搜索入口
Cleanup
清除
清除管理器
Windows 2000
IEmptyVolumeCache2
向清除管理器中增加新的入口来恢复磁盘空间
例如,给定某一类文件,外壳扩展允许您添加新的项目到上下文菜单或插入一个附加的属性页到标准属性对话框。虽然可以用任何支持COM组件的工具编写外壳扩展代码,但大多数外壳扩展都是在ATL框架中用C++编写的。
如果您使用VB来做同样的事情会有一些困难,因为您找不到专门的向导或者可以直接使用的设计器来帮助您编写支持特定COM接口的组件。您不得不写一个类型库来描述接口,然后在工程中引用这个类型库并使用实现关键字来声明只对此接口的支持。在VB6.0的光盘中可以找到一个用VB编写的外壳扩展例子。
外壳扩展常常与文档类有关,也就是说外壳扩展的功能只适用于具有某些扩展名的文件。更详细的信息可以参考MSDN库和有关Windows外壳编程的书籍。
有许多用户界面特点已在桌面更新(Desktop Update)中引入----Windows 9x和Windows
NT4.0中有各自的外壳扩展。桌面更新是随IE4.0和Windows 98 一同而来。但应注意桌面更新不是IE5.0的一部分。所以如果您想在NT4.0上安装,需要先安装IE4.0,必须选中桌面更新选项,IE5.0将更新NT4.0和Windows
95现存的设置,否则将不会安装桌面更新。

一个典型的外壳扩展应用是帮助用户对文档进行分类。以BMP文件为例,当从文件列表中选中BMP文件时,桌面更新(外壳版本4.72+)会以小图方式提供这个BMP文件的预览,但是它不提供BMP文件的大小及颜色信息。如果您对系统外壳稍加扩展就可以使您的应用程序对BMP文件作更细致的处理,这将大大方便用户的使用。
第一个想到的方案便是为BMP文件添加新的属性页。实际上,这不是最好的方法,因为用户要单击右键,选中“属性”菜单项,再选择正确的标签读取信息。为了达到同样的目的,最佳方法是使用新的信息条提示(Infotips)特性。
信息提示的作用是当鼠标移到某种类型的文件上时,让工具提示(tooltip)控件显示一个简短的文本,这个文本片段提供了关于特定文件的属性细节信息。对于Excel和Word文档,信息提示特性是自动使能的。它显示文档的标题、作者和主题。信息条提示特性是针对特殊类型文件的外壳扩展,其注册方案也很独特。信息条提示扩展接收对当前选中文件的引用,然后作处理以便抽取需要的信息。

图二 BMP文件的 Infotip
图二显示了一个BMP文件的信息提示扩展,为了建立这个扩展,您需要创建一个进程内的服务器,实现IQueryInfo和IPersistFile接口。IQueryInfo用来给外壳提供运行时的
文本。IPersistFile由资源管理器使用,让扩展知道当前鼠标指针指向的文件。本文的例子代码定义了一对从上述两个接口派生的最小限度的ATL基类(IQueryInfoImpl.h
和IPersistFileImpl.h),可以使用它们来建立更专业的类。在源代码中您还能看到嵌入到这个外壳的其它相关类的声明。下面是ATL基类定义:
实现对位图文件的信息提示条(BMP Infotip)
// IPersistFileImpl.h
#include class ATL_NO_VTABLE IPersistFileImpl : public IPersistFile{public: TCHAR m_szFile[MAX_PATH]; // IUnknown STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) = 0; _ATL_DEBUG_ADDREF_RELEASE_IMPL(IPersistFileImpl) // IPersistFile STDMETHOD(Load)(LPCOLESTR wszFile, DWORD dwMode){ USES_CONVERSION; _tcscpy(m_szFile, OLE2T((WCHAR*)wszFile)); return S_OK; }; STDMETHOD(GetClassID)(LPCLSID){ return E_NOTIMPL; } STDMETHOD(IsDirty)(VOID){ return E_NOTIMPL; } STDMETHOD(Save)(LPCOLESTR, BOOL){ return E_NOTIMPL; } STDMETHOD(SaveCompleted)(LPCOLESTR){ return E_NOTIMPL; } STDMETHOD(GetCurFile)(LPOLESTR FAR*){ return E_NOTIMPL; }};// IQueryInfoImpl.h#include #include class ATL_NO_VTABLE IQueryInfoImpl : public IQueryInfo{public: // IUnknown STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) = 0; _ATL_DEBUG_ADDREF_RELEASE_IMPL(IQueryInfoImpl) // IQueryInfo::GetInfoTip STDMETHOD(GetInfoTip)(DWORD dwFlags, LPWSTR *ppwszTip){ wcscpy(*ppwszTip, L"InfoTip"); return S_OK; } // IQueryInfo::GetInfoFlags STDMETHOD(GetInfoFlags)(LPDWORD pdwFlags){ *pdwFlags = 0; return E_NOTIMPL; }};// BmpTip.h : Declaration of the CBmpTip coclass#ifndef __BMPTIP_H_#define __BMPTIP_H_#include "resource.h" // main symbols#include "comdef.h" // GUIDs #include "IPersistFileImpl.h" // IPersistFile#include "IQueryInfoImpl.h" // IQueryInfo// CBmpTipclass ATL_NO_VTABLE CBmpTip : public CComObjectRootEx, public CComCoClass, public IQueryInfoImpl, public IPersistFileImpl, public IDispatchImpl{public: CBmpTip(){ HRESULT hr; hr = SHGetMalloc(&m_pAlloc); if (FAILED(hr)) m_pAlloc = NULL; } ~CBmpTip(){ m_pAlloc-Release(); }DECLARE_REGISTRY_RESOURCEID(IDR_BMPTIP)DECLARE_PROTECT_FINAL_CONSTRUCT()BEGIN_COM_MAP(CBmpTip) COM_INTERFACE_ENTRY(IBmpTip) COM_INTERFACE_ENTRY(IQueryInfo) COM_INTERFACE_ENTRY(IPersistFile) COM_INTERFACE_ENTRY(IDispatch)END_COM_MAP()// IQueryInfopublic: STDMETHOD(GetInfoTip)(DWORD, LPWSTR*);private: STDMETHOD(GetBitmapInfo)(CComBSTR*); LPMALLOC m_pAlloc;};#endif //__BMPTIP_H_ PersistFile的Load方法将文件名存储在m_szFile成员变量中,这个方法在初始化过程期间被资源管理器隐含调用。IqueryInfo只包含两个函数,其中GetInfoFlags目前还不被支持并且必须返回E_NOTIMPL。实际上,一旦您实现了IQueryInfoImpl.h 和IPersistFileImpl.h提供的最小限度的派生类,写一个信息条提示扩展是很容易的,先建立一个新的ATL进程内对象并实现IQueryInfo::GetInfoTip方法: HRESULT CBmpTip::GetInfoTip(DWORD dwFlags, LPWSTR* ppwszTip) 参数dwFlags目前不使用,ppwszTip是一个指向Unicode串缓冲的指针,用来返回运行时关于文件的文本。要注意ppwszTip缓冲必须使用标准的外壳内存分配器起来为它分配内存。这个缓冲由应用程序负责分配内存,由外壳来释放分配的内存。为了保证所发生的一切都是线程安全的,请使用SHGetMalloc来获得外壳内存分配器(一个IMalloc对象)的指针。然后,使用IMalloc的Alloc方法分配需要的内存并以Unicode方式存储信息条提示文本。 下面的代码段说明了外壳扩展如何获取和返回文本: CComBSTR bstrInfo;GetBitmapInfo((CComBSTR *)&bstrInfo);*ppwszTip = (WCHAR*) m_pAlloc-Alloc( (bstrInfo.Length() +1) * sizeof(WCHAR) );if (*ppwszTip) wcscpy(*ppwszTip, (WCHAR*)(BSTR)bstrInfo); GetBitmapInfo是一个内部成员,它只是准备一个串来存放图像的大小和颜色。为了得到这个信息,这个函数打开BMP文件并读取bitmap文件的头。


