Chinaunix首页 | 论坛 | 博客
  • 博客访问: 588911
  • 博文数量: 752
  • 博客积分: 40000
  • 博客等级: 大将
  • 技术积分: 5005
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-13 14:47
文章分类

全部博文(752)

文章存档

2011年(1)

2008年(751)

我的朋友

分类:

2008-10-13 16:42:56

Windows 2000 UI 新特点之一:信息条提示(Infotip)

原文参见:


编译/





本文假设您熟悉windows的外壳编程及ATL。


摘要:
Windows 2000引入了许多很有用的用户界面新特点,我们能在自己的应用程序中定制和实现这些用户界面。本文是由一系列文章组成,将探讨包括信息提示(Infotip)、增强文件夹特性、搜索管理、图标覆盖和快速启动工具条在内的几个新的UI编程,使用它们来丰富系统外壳的功能。
阅读本篇系列文章之后,您将学会Windows 2000用户界面编程的几个新特点:
  1. 如何为文件提供“infotips”(信息条提示)特性;
  2. 如何创建一个定制的栏目管理器扩展(column handler extension),它使我们能通过Windows的资源管理器在“查看”菜单的“详细资料”栏的视图中看到新创建的文件属性栏目。
  3. 为了进一步扩展外壳,我们还将另外实现几个界面特点:搜索管理、清除管理、使用属性页的文件夹定制、图标覆盖、以及上下文菜单外壳扩展。
随着 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现存的设置,否则将不会安装桌面更新。

编写信息条提示(Infotip)外壳扩展
    一个典型的外壳扩展应用是帮助用户对文档进行分类。以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

// CBmpTip
class 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()

// IQueryInfo
public:
    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文件的头。

注册信息条提示扩展
    信息提示扩展以非标准方式进行注册。通常基于文件的扩展在标识这个文件的注册键下创建它们自己的子树。例如:.ext文件的类名是在HKEY_CLASSES_ROOT下.ext注册键的缺省值。但是,BMP文件的信息提示必须创建下列入口:
        HKCR
   \.bmp        \shellex            \{00021500-0000-0000-C000-000000000046}
     这个键的缺省值必须是实现COM对象的CLISID。这个地方的CLSID是IqueryInfo接口的IID,而不是BMP文件的文件类。这就是为什么信息条提示扩展以非标准方式进行注册的原因。另一方面,这个注册方案适用于任何一个处理BMP文件的注册应用程序。实际上,处理某个文件类的应用将自身注册为缺省处理程序的话,它也能改变类名,也能使所有的注册扩展无效。如果MS Paint是缺省处理程序的话,用于BMP文件的类名就是PaintPicture。如果使用另一个程序编辑浏览bitmaps,用于BMP文件的类名可能就不是PaintPicture。
    为了能在NT和Windows 2000中正常工作,外壳扩展必须被注册并且要得到系统管理员的批准。许多开发人员都低估了这一点,因为他们多数以管理员的身份登陆到NT系统。在这种情况下可以忽略系统管理员的审批。以下是负责扩展审批的注册入口:
        HKLM
          \SOFTWARE
             \Microsoft
                \Windows
                   \CurrentVersion
                      \Shell Extensions
                         \Approved
        
在这个键下,用外壳扩展的CLSID名字创建一个新串值并给出一个描述文本。

如何存储信息条提示设置

    在很多情形下都可以应用信息条提示外壳扩展,包括定制的文档和象DLL及EXE这样的本地文件类。图四显示了一个信息条提示应用的例子。它列出了静态链接到给定的EXE或DLL模块的动态链接库的列表。注意在Windows资源管理器的状态条也显示了相同的内容。

图四  DLL 和 EXE 文件的 Infotip

    Visual Studio 6.0 中有一个很好的工具: Dependency Walker,这个工具也有与信息条提示一样的功能。不同是这个工具是一个独立的可执行程序,你必须得从上下文菜单来运行。
    如果您只想知道某个可执行文件依赖的主动态链接库列表,使用本文的外壳扩展就足够了。为了获得静态链接库的列表,本文的代码中使用了ImageHLP API,关于这个API,我将在另文中说明。此外,代码还使用了BindImageEx来请求每个输入函数的有效地址。为此,BindImageEx邦定可执行文件图像并为每一个输入库和函数调用回调函数。您要做的全部工作是写一个回调函数将模块名简单地连结为一个串。
    从属列表很有用,但它显示的可能不是每次当您的鼠标移到某个DLL或EXE文件上时您想要的信息。如果能用一个逻辑变量来控制信息条提示特性的使能和取消那不是更好吗?为了实现这个功能,既可以利用注册表,也可以用INI文件来存储这个控制变量的值,但有一个问题是:如何改变存储在注册表或INI文件中的这个值?使用注册表编辑器或者写字板当然可以,但那未免土了点。如果外壳扩展是应用的一部分,那么为应用程序做一个参数选项对话框应该是最好的解决办法。
--------------------next---------------------

linux牛牛网
阅读(277) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~