找了一本《MFC技术内幕》的书,英文名MFC Black Book,老美Al Williams写的,很早以前写的啦。不管怎样,让我来学习吧。
从最后一章,哦,是附录A学起吧。
附录A有关外壳图标处理过程
Windows的外壳设计者让我们能够通过注册表、COM对象来扩展外壳,知道这一点,我们才能决定是否通过一个外壳来实现我们的某种需求。
下表列出外壳扩展的种类:(我喜欢表格)
名称 | 描述 | 接口 |
Icon | 为文件类型提供动态图标 | IExtractIcon,IPersistFile |
Context Menu | 添加上下文菜单或拖放菜单 | IContextMenu,IShellExtInit |
Property Sheet | 添加定制的属性页 | IShellPropSheetExt,IShellExtInit |
Copy Hook | 管理文件操作 | ICopyHook |
Drop Target | 当对象被拖放至其它对象时交互 | IDropTarget,IPersistFile |
Data Object | 为拖放或拷贝粘贴提供数据对象 | IDataObject,IPersistFile |
Quick View | 为Quick Vierwer 解析文件 | IFileViewer,IPersistFile |
Briefcase Reconcile | 把某文件的多个版本合并为一个文件 | IReconcilableObject, IPersistFile |
说起来这好像不是MFC的范畴的东西。以前曾经用ATL做过文件管理器的一个扩展,实在是很麻烦。不过学习要坚持下去啦~~~
接下来是关于COM,MFC对COM支持的一些小节,其中MFC用来实现COM接口的方式值得关注一下。为了支持多个接口,MFC使用嵌套(Nested)类的方式,缺省的名称开头为X,让我们到MFC源代码中去找一个例子:
class COleDataSource : public CCmdTarget { // Interface Maps 这里删减掉了其它构造、属性、实现等部分 public: BEGIN_INTERFACE_PART(DataObject, IDataObject) INIT_INTERFACE_PART(COleDataSource, DataObject) STDMETHOD(GetData)(LPFORMATETC, LPSTGMEDIUM); // 此处删减掉 IDataObject 其它多个接口函数的定义部分 END_INTERFACE_PART(DataObject) DECLARE_INTERFACE_MAP() }; |
代码中绿色的几个宏就是用嵌套类的方式来实现IDataObject接口,具体宏的定义如下:
#define BEGIN_INTERFACE_PART(localClass, baseClass) / class X##localClass : public baseClass / { / public: / STDMETHOD_(ULONG, AddRef)(); / STDMETHOD_(ULONG, Release)(); / STDMETHOD(QueryInterface)(REFIID iid, LPVOID* ppvObj); /
#define END_INTERFACE_PART(localClass) / } m_x##localClass; / friend class X##localClass; / |
也就是说在COleDataSource类中定义展开的样子大致如下:
class XDataObject : public IDataObject { public: STDMETHOD_(ULONG, AddRef)(); // 删减了其它接口定义 . . . } m_xDataObject; // 同时产生一个实例 friend class XDataObject; // 定义为友类,好让嵌套类访问父类的私有、保护成员 |
如果有多个接口实现,就会定义多个这样的嵌套类和实例。下面再简单看看实现的代码:
STDMETHODIMP_(ULONG) COleDataSource::XDataObject::AddRef() { // 这个宏产生 COleDataSource* pThis 的值,使用 pThis 可以访问嵌套父类 METHOD_PROLOGUE_EX_(COleDataSource, DataObject) return pThis->ExternalAddRef(); // AddRef, Release 等被委托给父类(CCmdTarget)实现 } |
至此,MFC怎样实现COM应该大致理解了。
书上说用AppWizard可以创建一个MFC COM对象,不过在VC7(.NET)中好像不太好使了。
下面总算详细介绍图标处理过程了。从表里面可以看到我们的COM对象需要实现两个接口IPersistFile 和IExtractIcon。
提示:如果仅仅使用一个静态图标,则只需要更改注册表DefaultIcon键就可以了,例如到HKCR下面随便找到几个:
.col/DefaultIcon = C:/WINDOWS/hh.exe,0 .nsc/DefaultIcon = dxmasf.dll,-502 |
对于基于一些文件属性或其他动态条件而更改图标,才需要用到这种复杂的方法,例如:
l 显示不同类型的账目文件。
l 网络文件图标。当网络连通正常时显示为绿色,当网络断开时显示为红色。
l 等等。。。
下表给出IPersistFile接口在此应用中需要实现的接口函数及其意义:
函数 | 描述 |
GetClassID | 不要求,返回E_NOTIMPL。 |
IsDirty | 不要求,返回E_NOTIMPL。 |
Load | 外壳给该函数传递一个文件名;把UNICODE文件名转换为ANSI并保存起来。 |
Save | 不要求,返回E_NOTIMPL。 |
SaveCompleted | 不要求,返回E_NOTIMPL。 |
GetCurFile | 不要求,返回E_NOTIMPL。 |
IExtractIcon接口:
函数 | 描述 |
GetIconLocation | 获取包含图标的文件名和图标在该文件中的索引。 |
Extract | 返回图标句柄。 |
注:这两个函数实现一个就可以。
书中给出了一个例子,表明怎么用CIconHandler来实现为 .VIZ文件实现自定义Icon, 也就是要实现上面所说的IPersistFile, IExtractIcon接口,我找了半天,也没看到GetIconLocation, Extract两个函数的实现,作者忘掉了吗??
最后最最关键的是要知道仅仅写了代码编译了是没有用的,还要:
1、将你的COM组件注册到系统中。不注册外壳程序找不到的哦。
2、在注册表添加如下键值:
[HKCR/.VIZ] = "VIZFile" [HKCR/VIZFile] = "Vxxx Dxxx File Description" [HKCR/VIZFile/Shellex/IconHandler] = "{The handler’s CLSID GUID}" [HKCR/VIZFile/DefaultIcon] = "%1" |
注:到MSDN的User Interface Design And Development/Windows Shell/SDK/Windows Shell/Shell Programmer’s Guide可以找到更多的信息。
还有啊,现在要是实现的话,建议用ATL,不要用复杂的MFC,我笔记这些仅仅是为了学习哦。 :)