分享
 
 
 

编写可复用性更好的C++代码——Band对象和COMToys(八)

王朝vc·作者佚名  2006-01-17
窄屏简体版  字體: |||超大  

编写可复用性更好的C++代码

——Band对象和COMToys(八)

编译/赵湘宁

原著:Paul Dilascia

MSJ November 1999 & December 1999

关键字:Bands 对象,Desk Bands,Info/Comm Bands,Explorer Bar,Tool

Bands。

本文假设你熟悉C++,COM,IE。

下载本文源代码: MyBands.zip

(128KB)

TestEditSrch.zip

(75KB)

第一部分:Band 对象介绍

第二部分:BandObj的类层次和MyBands服务程序的注册

第三部分:深入Band内部,揭开Band的面纱

第四部分:Band对象使用中遇到的一些问题

第五部分:建立自己的COM编程平台ComToys

第六部分:设计和构造COMToys

第七部分:类的实现

第八部分 类厂和注册以及美中不足

类厂和注册

到现在为止,我们的讨论没有涉及到一个重要的细节,那就是对象的创建。因为相对与设计和实现来说,它比较简单。前四个部分所讨论的类工厂和注册相当广义。其中描述了BandObj如何利用资源脚本文件中所列的DLL的条目以及使用ATL注册器的类厂来注册COM对象。到了COMToys,我仅仅是将代码从BandObj移到了COMToys。

COMToys使用CTModule,CTFactory,和一个特殊的文件DllEntry.cpp来处理对象的创建,注册和DLL条目。CTModule是一个典型的“模块类”,就像MFC的CWinApp或者ATL的CComModule。其中的OnGetClassObject,OnDllRegisterServer等虚函数是可以重载的。不过必须包含(#include)实现DLL入口的DllEntry.cpp。

extern "C"

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid,

LPVOID* ppv)

{

MFCENTRY;

return CTModule::GetModule()->

OnGetClassObject(rclsid, riid, ppv);

}

如法炮制DllRegisterServer,DllCanUnloadNow及其它函数。CTModule::GetModule类似AfxGetApp。它返回一个且是仅有的一个全程CTModule对象。所以DllEntry.cpp的全部工作是将外部实现的硬性函数转换成较容易重载的虚函数。

至于MFC应用方面,COMToys 有一个用于COM的MFC 应用类 CTMfcModule,它从CTModule 和

COleControlModule 实现多继承。在这个CTMfcModule类中实现了OnGetClassObject和其它操作,这些操作中又调用相应的MFC函数来完成工作。将这些东西分离的理由是尽量将处理放到不依赖MFC的代码中。图十八显示了MFC和非MFC类之间的关系:

图十八 MFC和非MFC类之间的关系

关于类工厂的实现,COMToys 依赖于MFC模仿类:CTFactory。CTModule::OnRegisterServer调用一个静态函数,CTFactory::OnRegisterAll,它具备针对每一个类工厂调用的CTFactory::OnRegister。当创建CTFactory的时候。必须指定的一个参数之一是资源ID。缺省情况下,CTFactory::OnRegister查找有相同ID的脚本文件资源并调用ATL注册器运行它。但首先它要调用另一个虚函数:OnInitRegistryVariables,以便初始化通用的注册器变量,如%CLSID% 和%ClassName%.,下面是ComToys在

CTComObjFactory::OnInitRegistryVariables 定义的变量清单:

%CLSID% = class ID (GUID) (COleObjectFactory::m_clsid)

%MODULE% = full pathname of DLL

%Title% = title (resource substring 0)

%ClassName% = human-readable COM class name (resource substring 1)

%ProgID% = ProgID (resource substring 2)

%ThreadingModel% = "", "Apartment", "Free", or "Both" (m_nThreadingModel)

可以重载 OnInitRegistryVariables

添加自己的注册器变量。这些都与前几部分的做法一样,只是现在的代码被从Band对象中抽象出来进入了COMToys。最终,在进行注册与注销的处理时,所要做的全部工作是编写一个注册脚本并将它作为资源放入资源文件,让它的ID与COM类相同。

IDR_MYCOMCLASS REGISTRY DISCARDABLE "MyComClass.rgs"

应该指出,CTFactory有一个地方很伤脑筋:它本应该实现IClassFactory,但是它没有。那是因为我还没来得及顾上它。因为到目前为止我所建立的所有COM类都与MFC有关,我一直都用COleObjectFactory,它很好用。因此,我从COleObjectFactory 和

CTFactory 派生 CBandObjFactory。如果需要非MFC类工厂,可以为

CTFactory 实现一个IClassFactory。不过现在有CTFactory,COleObjectFactory相伴左右,CTFactory提供了所有注册工作所需要的东西,很容易使用。而COleObjectFactory提供了IClassFactory接口和与DllGetClassObject的联接。

当Windows资源管理器(Explorer)之类的进程想要创建自己的COM对象实例时——假设是创建MyBands.dll中的Band对象——它调用CoCreateInstance函数,这个函数读取注册表确定加载哪一个DLL并加载它,然后调用DllGetClassObject(在DllEntry.cpp中)函数,它再接着调用CTModule::OnGetClassObject。因为BandObj是一个基于MFC的对象,它使用CTMfcModule,其OnGetClassObject函数调用MFC的AfxDllGetClassObject函数。MFC搜索类工厂清单及其它处理。到这里也许有人会问:类工厂是在哪儿创建的呢?通常在MFC中创建类工厂用的是DECLARE_OLECREATE 和 IMPLEMENT_OLECREATE。这些宏声明并实例化一个静态COleObjectFactory对象:它的形式为CMyComClass::类工厂。但前面四个部分中的BandObj和COMToys都没有使用DECLARE/IMPLEMENT_OLECREATE,因此,为了创建自己的类工厂,这就要调用new进行动态创建:

// 在InitInstance中

new CTBandObjFactory(MYGUID,

RUNTIME_CLASS(CMyComClass),

IDR_MAINFRAME);

当创建了新的CTFactory,COMToys将它添加到主清单列表中;MFC对COleObjectFactory也做同样的事情。换句话说,正像我在面几个部分中解释的那样,不必专门做些什么来让MFC或COMToys知道你的类工厂。只要创建就行了。也不必删除类工厂,CTModule会在ExitInstance中自动完成。如果宁愿用老式方法将类工厂创建成一个静态全局类,那也没问题——但必须在自己的类工厂构造函数中设置m_bAutoDel = FALSE。避开DECLARE/IMPLEMENT_OLECREATE的主要理由是这样做了以后,我就能按理想的方式派生自己的类工厂。在BandObj例子中,自己当然必须创建一个类工厂;如面几个部分所做的那样,依然要调用AddBandClass。

总之,为了处理对象的创建,所有要做的工作是从 CTFactory

和 COleObjectFactory 派生自己的类工厂并记住#include文件DllEntry.cpp。剩下的事情由COMToys来做。为了让COM对象自注册,自己得写一个用于注册的资源脚本(RGS文件),并将它作为与类工厂有相同ID的REGISTRY资源添加到.RC文件。RGS文件的具体细节请下载源代码。

美中不足

我刚开始实现COMToys的时候,碰到一个小障碍。如果两个COM接口的方法有相同的名字和签名怎么办?虽然这种情况很少发生,但它确实发生了。例如,IPersistStream 和IPersistFile都有IsDirty。如果像下面这样写的话:

DECLARE_IPersistFile();

DECLARE_IPersistStream();

最后会声明IsDirty两次。尽管可以用手工方式进行更正,但这个问题总是让人有些忐忑不安。用手工来做,你必须重新敲入很多IMPLEMENT_IPersistStream代码,这太令人讨厌。为了避免这种事情,我引入了一个函数级的宏。

// IPersistFile - 整个接口

IMPLEMENT_IPersistFile(CMyClass,CTPersistFile);

// IPersistStream - 每个单独的函数

// IMPLEMENT_IPersistStream_IsDirty();

IMPLEMENT_IPersistStream_Load(CMyClass,CTPersistStream);

IMPLEMENT_IPersistStream_Save(CMyClass,CTPersistStream);

IMPLEMENT_IPersistStream_GetSizeMax(CMyClass,CTPersistStream);

这里再次印证了多继承(Magic MI Ruler)的优点,只有一个IsDirty,它应用到两个接口:IPersistFile 和 IPersistStream。这不就是希望的结果吗?如果你确实要让两个接口有相同的函数,又有不同的实现,你完全可以用普通的C++语法来充分限定方法名字:CMyComClass::IPersistStream::IsDirty 和 CMyComClass::IPersistFile::IsDirty。但用这个函数级的宏,至少不用重新敲入——甚至是不用知道它的实现。函数级的宏还提供了为一个或多个接口方法重载标准实现的途径——尽管如此,我觉得最好还是通过派生自己的实现类——CTMyPersistStream或别的类来完成,(待续)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有