Chinaunix首页 | 论坛 | 博客
  • 博客访问: 607198
  • 博文数量: 945
  • 博客积分: 52360
  • 博客等级: 大将
  • 技术积分: 13070
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-08 11:12
文章分类

全部博文(945)

文章存档

2011年(1)

2008年(944)

我的朋友

分类: C/C++

2008-08-08 11:31:39

下载本文示例代码
例如,给定某一类文件,外壳扩展允许您添加新的项目到上下文菜单或插入一个附加的属性页到标准属性对话框。虽然可以用任何支持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文件中的这个值?使用注册表编辑器或者写字板当然可以,但那未免土了点。如果外壳扩展是应用的一部分,那么为应用程序做一个参数选项对话框应该是最好的解决办法。 下载本文示例代码

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