2008年(909)
分类:
2008-05-06 21:36:15
原文参见:MSDN
Magazine March 2000
编译/赵湘宁
第一部分
Windows 2000 UI 新特点之一:信息条提示(Infotip)
本文源代码
本文假设您熟悉windows的外壳编程及ATL。
摘要:
图五 详细资料视图中的栏目
通过选择“更多选项(More)”,还可以显示出整个栏目列表。有 、三个栏目是让人耳目一新的:即创建日期、作者和模块版本。创建日期显示的是文件或文件夹最初创建的日期。作者栏则显示的是按照复合文件的摘要信息(SummaryInformation)头内容指定的文档签名者的名字。尤其是在一个全是Office文档的文件夹中,一眼就能区分出某人写的文档。而且,一旦显示了一个新的栏目,同时也能按这个栏目来对文件排序。注意作者信息只在文档被存为嵌入了摘要信息头的复合文档时才能获得。除了微软的Office文档格式以外(Word、Execel
或PowerPoint),输出摘要信息块的文档类型不多,除了Office文档以外,还有FlashPix图像文件也输出摘要信息块。
在栏目中包含可执行文件版本号“Module Version” (模块版本栏目)是人们期待已久的事情了。图六中显示的就是一个在system32文件夹中使能的模块版本栏目。
图六 Module Version 栏目
通过栏目选择对话框,您可以设置一个缺省的栏目宽度并选择它显示的位置。栏目重新排序使每个文件夹的基本特性,在某个文件夹上通过对属性的调整,总是能使所有文件夹的有相同的外观,方法是在文件夹选项对话框的“视图(View)”标签中按“与当前文件夹一样(Like
Current Folder)”按钮。还有一个关于文件夹的设置是“记忆每个文件夹的设置(Remember each folder’s
setting)”,这个特性允许您控制是否将全程文件夹选项应用到每一个单个的文件夹。
上面所讲的文件夹是针对相应的文件系统目录而言的。其他类型的文件夹,如名字空间扩展(namespace
extensions),由其自己定义非栏目视图。但也有例外,那就是“我的文档”和“Favorites”。它们是名字空间扩展,但它们的内容映象到常规文件夹目录,提供标准的表格式视图并对应到选中的当前文件夹设置。
想要编写基于栏目的名字空间扩展应用,应该提供一种允许从说明栏上单击右键弹出的上下文菜里定制栏目。
如何定义定制栏目
Windows 2000 允许您定制栏目并将它加到列表中(见图五)。为了定义一个新栏目,必须要编写并注册一个新的外壳类型:栏目处理器(Column
Hander)。大多数情况下,一个栏目为一个文件类提供特殊信息----例如,BMP文件的尺寸和调色板大小。然而栏目处理器并不绑定到文件类,而是文件夹对象的一个特性。换句话说,您不用在位图文件类子树中注册一个栏目处理器,而要在文件夹子树下注册。用扩展的代码来区分文件,它知道如何有针对性地处理和返回文件的信息。
一个栏目处理器扩展需要实现单接口:IColumnProvider。这样就使得在初始化时与其它需要IPersistFile
或 IShellExtInit 接口的扩展有些轻微的差别(这是因为实际上栏目处理器不适用于文件类)。
栏目扩展实现代码 // IColumnProviderImpl.h #includeIColumnProvider提供了三个方法:Initialize, GetColumnInfo, 和GetItemData。Initialize在外壳设置这个扩展作用于某个文件夹时被调用。文件夹的名字是在作为方法参数的SHCOLUMNINIT结构中被传递的。大多数情况下,您不用着这么做。本文源代码提供的标准ATL类(IProviderColumnImpl.h)已经将文件夹名存储在一个成员变量中。除非调用文件夹系统告诉您需要采取别的特别动作。您也可以不用这个方法,而是使用GetColumnInfo函数,它是一个很重要的函数:#include class ATL_NO_VTABLE IColumnProviderImpl : public IColumnProvider { protected: TCHAR m_szFolder[MAX_PATH]; public: // IUnknown STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) = 0; _ATL_DEBUG_ADDREF_RELEASE_IMPL(IColumnProviderImpl) // IColumnProvider // IColumnProvider::Initialize STDMETHOD(Initialize)(LPCSHCOLUMNINIT psci){ USES_CONVERSION; _tcscpy(m_szFolder, OLE2T((WCHAR*)psci->wszFolder)); return S_OK; } // IColumnProvider::GetColumnInfo STDMETHOD(GetColumnInfo)(DWORD dwIndex, SHCOLUMNINFO *psci){ return S_FALSE; } // IColumnProvider::GetItemData STDMETHOD(GetItemData)(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData){ return S_FALSE; } }; // BmpColInfo.h : Declaration of the CBmpColInfo #ifndef __BMPCOLINFO_H_ #define __BMPCOLINFO_H_ #include "resource.h" // main symbols #include // GUID of IColumnProvider #include "IColumnProviderImpl.h" // IColumnProvider base impl const UINT BMPCH_DEFWIDTH = 16;// column default width in chars const UINT BMPCH_MAXSIZE = 80; // max text size const DWORD BMPCH_NUMOFCOLS = 1;// number of columns handled // here // CBmpColInfo class ATL_NO_VTABLE CBmpColInfo : public CComObjectRootEx , public CComCoClass , public IColumnProviderImpl, public IDispatchImpl { public: CBmpColInfo(){} DECLARE_REGISTRY_RESOURCEID(IDR_BMPCOLINFO) DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CBmpColInfo) COM_INTERFACE_ENTRY(IBmpColInfo) COM_INTERFACE_ENTRY_IID(IID_IColumnProvider, CBmpColInfo) COM_INTERFACE_ENTRY(IDispatch) END_COM_MAP() // IColumnProvider public: STDMETHOD(GetColumnInfo)(DWORD, SHCOLUMNINFO *); STDMETHOD(GetItemData)(LPCSHCOLUMNID, LPCSHCOLUMNDATA, VARIANT *); private: STDMETHOD(GetBitmapInfo)(LPCWSTR, LPTSTR); }; #endif //__BMPCOLINFO_H_ // BmpColInfo.cpp : Implementation of CBmpColInfo #include "stdafx.h" #include "BmpCol.h" #include "BmpColInfo.h" HRESULT CBmpColInfo::GetColumnInfo(DWORD dwIndex, SHCOLUMNINFO *psci){ // 因为此扩展可以提供多个栏目,用索引号列举。外壳用递增的 // 索引号重复调用此方法,直到返回S_FALSE。只有当列举了所有 // 的栏目后才返回S_FALSE。dwIndex 是基于 0 的索引。注意必须 // 对它进行检查,否则会进入无限循环! if (dwIndex >= BMPCH_NUMOFCOLS) return S_FALSE; // 先填写 SHCOLUMNINFO 结构,使外壳知道此栏目的常规特性 // 用一对 FMTID/PID 值确定栏目。必须为每一个添加的栏目定义 // 一对 FMTID/PID 值。 psci->scid.fmtid = *_Module.pguidVer; // use object''s CLSID psci->scid.pid = 1; // Sets type, alignment and default width psci->vt = VT_LPSTR; // data is LPSTR psci->fmt = LVCFMT_LEFT; // left alignment psci->cChars = BMPCH_DEFWIDTH; // default width in chars // Other flags psci->csFlags = SHCOLSTATE_TYPE_STR; // Caption and description wcsncpy(psci->wszTitle, L"Dimensions", MAX_COLUMN_NAME_LEN); wcsncpy(psci->wszDescription, L"Provides dimensions and colors for BMPs", MAX_COLUMN_DESC_LEN); return S_OK; } // IColumnProvider::GetItemData HRESULT CBmpColInfo::GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData){ USES_CONVERSION; // 一旦选中栏目,则外壳便为先是在该文件夹中的每一个 // 文件调用此方法。 // 如果你处理多个栏目,则 SHCOLUMNID 结构对栏目进行 // 具体确定。 // 此例中由于只处理一个栏目,所以可以忽略 SHCOLUMNID // 结构参数。 // 具体的文件的信息包含在 SHCOLUMNDATA 结构中。 // 在此只对 .BMP 感兴趣。 if (wcsicmp(pscd->pwszExt, L".bmp")) return S_FALSE; // Reads dimensions and palette size from the BMP file TCHAR szBuf[BMPCH_MAXSIZE]; GetBitmapInfo(pscd->wszFile, szBuf); // The return value (a string in this case) must be // packed as a Variant. CComVariant cv(szBuf); cv.Detach(pvarData); return S_OK; } HRESULT CBmpColInfo::GetBitmapInfo(LPCWSTR wszFile, LPTSTR p){ USES_CONVERSION; BITMAPFILEHEADER bf; BITMAPINFOHEADER bi; HFILE h; // Reads the file header h = _lopen(OLE2T(wszFile), OF_READ); if (h==HFILE_ERROR) return S_OK; _lread(h, (LPBITMAPFILEHEADER)&bf, sizeof(BITMAPFILEHEADER)); _lread(h, (LPBITMAPINFOHEADER)&bi, sizeof(BITMAPINFOHEADER)); _lclose(h); // Formats the string wsprintf(p, _T("%d x %d x %d"), bi.biWidth, bi.biHeight, bi.biBitCount); return S_OK; }
HRESULT GetColumnInfo(DWORD dwIndex, SHCOLUMNINFO *psci);GetColumnInfo要给外壳传递尽可能多的栏目信息,包括说明、描述、管理的数据类型,以及您想让用户如何与数据交互。这些信息都填写在SHCOLUMNINFO结构中。因为栏目管理器能管理一个以上的栏目,所以应该提供一对唯一的IDs来明确标识这个栏目。这对IDS一个称为格式ID(FMTID)和另外一个是属性ID(PID),一并形成SHCOLUMNINFO数据结构的内容。格式ID是标识属性集的GUID,而属性ID是在属性集中特定属性的索引。FMTID/PID是在COM结构化存储中实现持久稳固的属性集的更一般机制的一个实例。
psci->scid.fmtid = FMTID_SummaryInformation; psci->scid.pid = 2;这里 2 是标题属性的PID。现在可以在标准的标题栏下编写您的扩展。
psci->scid.fmtid = *_Module.pguidVer; psci->scid.pid = 1;可以使用任何数字来标识PID。这里,选择 1。
dwColIndex = 0; while (true) { SHCOLUMNINFO shci; hr = pColumnProvider->GetColumnInfo(dwColIndex , &shci); if (FAILED(hr)) break; // other shell-specific code }应该用一个栏目数常量与dwIndex进行比较,当指定的栏目数完成时便跳出循环。如果只提供一个栏目处理器,就使用下列代码:
if (dwIndex >= 1) return S_FALSE;否则,模块将陷入死循环。
switch(dwIndex) { case 0: InitColumn_Dimensions(pshci); case 1: InitColumn_Title(pshci); // other code }只要正确实现了GetColumnInfo,当您点击“More”上下文菜单项,新栏目就会出现在对话框的底部。使用资源管理器的“查看”=>“选择栏目”菜单项可以到达相同的对话框。
图八 BMP文件的尺寸栏目
图八是例子程序显示BMP文件尺寸栏,除了表示图像颜色深度的像素外,还包含位图文件的宽度和高度。
注册栏目处理器扩展HKCR { NoRemove Folder { NoRemove Shellex { NoRemove ColumnHandlers { ForceRemove不要忘了用实际的组件CLSID值替代= s ''description'' } } } }