分类: C/C++
2008-08-01 16:54:35
图二 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 #includePersistFile的Load方法将文件名存储在m_szFile成员变量中,这个方法在初始化过程期间被资源管理器隐含调用。IqueryInfo只包含两个函数,其中GetInfoFlags目前还不被支持并且必须返回E_NOTIMPL。实际上,一旦您实现了IQueryInfoImpl.h 和IPersistFileImpl.h提供的最小限度的派生类,写一个信息条提示扩展是很容易的,先建立一个新的ATL进程内对象并实现IQueryInfo::GetInfoTip方法: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_
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文件的头。
HKCR这个键的缺省值必须是实现COM对象的CLISID。这个地方的CLSID是IqueryInfo接口的IID,而不是BMP文件的文件类。这就是为什么信息条提示扩展以非标准方式进行注册的原因。另一方面,这个注册方案适用于任何一个处理BMP文件的注册应用程序。实际上,处理某个文件类的应用将自身注册为缺省处理程序的话,它也能改变类名,也能使所有的注册扩展无效。如果MS Paint是缺省处理程序的话,用于BMP文件的类名就是PaintPicture。如果使用另一个程序编辑浏览bitmaps,用于BMP文件的类名可能就不是PaintPicture。
\.bmp \shellex \{00021500-0000-0000-C000-000000000046}
HKLM \SOFTWARE \Microsoft \Windows \CurrentVersion \Shell Extensions \Approved在这个键下,用外壳扩展的CLSID名字创建一个新串值并给出一个描述文本。
图四 DLL 和 EXE 文件的 Infotip
Visual Studio 6.0 中有一个很好的工具: Dependency Walker,这个工具也有与信息条提示一样的功能。不同是这个工具是一个独立的可执行程序,你必须得从上下文菜单来运行。
如果您只想知道某个可执行文件依赖的主动态链接库列表,使用本文的外壳扩展就足够了。为了获得静态链接库的列表,本文的代码中使用了ImageHLP
API,关于这个API,我将在另文中说明。此外,代码还使用了BindImageEx来请求每个输入函数的有效地址。为此,BindImageEx邦定可执行文件图像并为每一个输入库和函数调用回调函数。您要做的全部工作是写一个回调函数将模块名简单地连结为一个串。
从属列表很有用,但它显示的可能不是每次当您的鼠标移到某个DLL或EXE文件上时您想要的信息。如果能用一个逻辑变量来控制信息条提示特性的使能和取消那不是更好吗?为了实现这个功能,既可以利用注册表,也可以用INI文件来存储这个控制变量的值,但有一个问题是:如何改变存储在注册表或INI文件中的这个值?使用注册表编辑器或者写字板当然可以,但那未免土了点。如果外壳扩展是应用的一部分,那么为应用程序做一个参数选项对话框应该是最好的解决办法。
下载本文示例代码