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

2024-01-14 11:38

本文主要是介绍编写可复用性更好的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

    由于MFC树形对象模型而带来的负担,并不意味COMToys也必须有这个负担。COMToys使用多继承进行COM编程。为了清楚的说明它的工作原理,让我们看看建立在COMToys中CBandObj的新版本。对于应用层的程序像MyBands来说——CBandObj与其前身没什么两样。不同的只是现在CBandObj派生于多个父类CWnd 和IDeskBand。
 // 在BandObj.h文件中
class CBandObj : public CWnd,public IDeskBand
{
DECLARE_IUnknown();
// IDeskBand
HRESULT GetBandInfo(...);
……
};
    因为Band对象是个窗口,所以CBandObj要从CWnd派生。同时要实现IDeskBand,所以还要从IDeskBand派生。DECLARE_IUnknown是一个COMToys宏,在其代码块中声明了AddRef,Release和QueryInterface。这个宏降低了错误输入的可能性并且形式可读性更强。如果声明了接口,当然就得实现它,为此,COMToys还提供了另一个宏。
 // 在BandObj.cpp文件中
IMPLEMENT_IUnknownCT(CBandObj);
    就这么简单,一行代码搞掂。CT的意思是CCmdTarget,而不是COMToys;应该将这一行看成是:"为CCmdTarget 的IUnknown实现"(记住,CBandObj从CWnd继承了CCmdTarget),IMPLEMENT_IUnknownCT为调用CCmdTarget的AddRef,,Release和QueryInterface产生库存实现。
 ULONG CBandObj::AddRef()
{
CMDTARGENTRYTR("CBandObj(%p)::AddRef,
count=%d/n", this, m_dwRef+1);
return ExternalAddRef();
}
    ExternalAddRef,ExternalRelease和ExternalQueryInterface是MFC用于实现IUnknown的标准。它们再调用内部版本——除非使用聚合,此时它们调用外部IUnknown。不要纠缠在内部/外部这些东西上;它只是MFC实现的细节。重点是COMToys使用CCmdTarget实现IUnknown。想一想——如果这是一条能行得通的路,为什么不用它呢?CMDTARGENTRYTR是个COMToys宏,它为CCmdTarget的派生类产生标准的入口或条目。它等价于METHOD_PROLOGUE。它初始化MFC的状态并产生TRACE诊断。
AFX_MANAGE_STATE(m_pModuleState);
CTTRACEFN(...);
    还有一个宏是CMDTARGENTRY,它是一个没有TRACE诊断的版本,用于非CCmdTarget入口(象外来的DllGetClassObject),COMToys有MFCENTRY 和 MFCENTRYTR,它们用AfxGetStaticModuleState初始化状态,而不是用m_pModuleState,下面的代码列出了用于IMPLEMENT_IUnknownCT的所有宏扩展:
 // 用于 IMPLEMENT_IUnknownCT(CMyComClass)的宏扩展

STDMETHODIMP_(ULONG) CMyComClass::AddRef()
{
CMDTARGENTRYTR(_T("CMyComClass(%p)::AddRef "),this);
DWORD dwRef = ExternalAddRef();
CTTRACE(_T("> returns count=%d/n"),dwRef);
return dwRef;
}

STDMETHODIMP_(ULONG) CMyComClass::Release()
{
CMDTARGENTRYTR(_T("CMyComClass(%p)::Release "),this);
DWORD dwRef = ExternalRelease();
CTTRACE(_T("> returns count=%d/n"),dwRef);
return dwRef;
}

STDMETHODIMP CMyComClass::QueryInterface(REFIID iid, LPVOID* ppvRet)
{
CTCHECKARG(ppvRet);
CMDTARGENTRYTR(_T("CMyComClass(%p)::QueryInterface(%s) "), this, _TR(iid));
HRESULT hr = ExternalQueryInterface(&iid, ppvRet);
CTTRACE(_T("> returns %s, *ppv=%p, count=%d/n"),
_TR(hr), *ppvRet, CCmdTarget::m_dwRef);
return hr;
}

// 扩展 CMDTARGENTRYTR.
// 注: 当创建 CCmdTarget 对象时, MFC 初始化 m_pModuleState = AfxGetModuleState();
//
AFX_MANAGE_STATE(m_pModuleState);
TRACE(...);
    目前还要对CBandObj的IUnknown实现进行编码,但这里存在一个问题,容器什么时候调用QueryInterface来请求IID_IDeskBand,CBandObj将请求传递给CCmdTarget——但MFC如何知道返回什么接口指针(vtbl)?我在前面讨论过,MFC查找包含在嵌套类偏移量中的接口映射。CBandObj不具备接口映射——COMToys也不进行接口映射。那么当有人用指定的IID进行调用时,CCmdTarget如何找到这个接口呢? 解开这个谜团的是CCmdTarget的虚函数:GetInterfaceHook。由于MFC设计者的先见之明,这个问题得到了解决。在进行接口映射之前,CCmdTarget 会调用 GetInterfaceHook。
 // 伪码
HRESULT
CCmdTarget::InternalQueryInterface
(REFIID iid, void** ppv)
{
*ppv = GetInterfaceHook(iid);
if (*ppv)
return S_OK;
// 搜索接口映射

}
另外,为了旁路掉整个接口映射,只要在CBandObj中实现GetInterfaceHook就可以了。
 LPUNKNOWN CBandObj::GetInterfaceHook(void* piid)
{
REFIID iid = *((IID*)piid);
if (iid==IID_IUnknown)
return (IDeskBand*)this;
if (iid==IID_IDeskBand)
return (IDeskBand*)this;
return NULL;
}
    无论请求什么接口 ,CBandObj以标准的多继承方式返回相应的强制转换类型。但对于IUnknown不能进行IUnknown*强制转换,原因是在类型还不明确的情况下是不能转换的;这时不知道所指的是哪一个IUnknown,IDeskBand中的那一个,仰或是其它接口中的某一个?还没有明确告知呢。所以只能强制转换某些继承了IUnknown的单继承类。其实哪一个并不重要,因为它们全都指向相同的函数(参见图十七)。 我原本想在多继承的基础上添加自己的接口映射版本来代替旧式风格——但我看不到那样做有什么好处。在每个接口上多加两行代码有多难呢?有时候旧式风格既简单又不易产生混乱。 综上所述,可以总结出用COMToys实现一个基本的COM对象的方法:从CCmdTarget直接派生,或从CCmdTarget的派生类——如CWnd——间接派生,用DECLARE_IUnknown宏声明IUnknown,并用IMPLEMENT_IUnknownCT宏实现它,并编写GetInterfaceHook返回正确的接口指针。当然还得实现方法,对于Band对象,必须实现IDeskBand::GetBandInfo。
 HRESULT CBandObj::GetBandInfo(...)
{
// do it
}
......
(待续)

Google
<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>

这篇关于编写可复用性更好的C++代码——Band对象和COMToys(六)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/605034

相关文章

Windows下C++使用SQLitede的操作过程

《Windows下C++使用SQLitede的操作过程》本文介绍了Windows下C++使用SQLite的安装配置、CppSQLite库封装优势、核心功能(如数据库连接、事务管理)、跨平台支持及性能优... 目录Windows下C++使用SQLite1、安装2、代码示例CppSQLite:C++轻松操作SQ

C++中RAII资源获取即初始化

《C++中RAII资源获取即初始化》RAII通过构造/析构自动管理资源生命周期,确保安全释放,本文就来介绍一下C++中的RAII技术及其应用,具有一定的参考价值,感兴趣的可以了解一下... 目录一、核心原理与机制二、标准库中的RAII实现三、自定义RAII类设计原则四、常见应用场景1. 内存管理2. 文件操

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

Python实例题之pygame开发打飞机游戏实例代码

《Python实例题之pygame开发打飞机游戏实例代码》对于python的学习者,能够写出一个飞机大战的程序代码,是不是感觉到非常的开心,:本文主要介绍Python实例题之pygame开发打飞机... 目录题目pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本代码解释初始化部

C++ 函数 strftime 和时间格式示例详解

《C++函数strftime和时间格式示例详解》strftime是C/C++标准库中用于格式化日期和时间的函数,定义在ctime头文件中,它将tm结构体中的时间信息转换为指定格式的字符串,是处理... 目录C++ 函数 strftipythonme 详解一、函数原型二、功能描述三、格式字符串说明四、返回值五

Java中Map.Entry()含义及方法使用代码

《Java中Map.Entry()含义及方法使用代码》:本文主要介绍Java中Map.Entry()含义及方法使用的相关资料,Map.Entry是Java中Map的静态内部接口,用于表示键值对,其... 目录前言 Map.Entry作用核心方法常见使用场景1. 遍历 Map 的所有键值对2. 直接修改 Ma

MySQL JSON 查询中的对象与数组技巧及查询示例

《MySQLJSON查询中的对象与数组技巧及查询示例》MySQL中JSON对象和JSON数组查询的详细介绍及带有WHERE条件的查询示例,本文给大家介绍的非常详细,mysqljson查询示例相关知... 目录jsON 对象查询1. JSON_CONTAINS2. JSON_EXTRACT3. JSON_TA

C++作用域和标识符查找规则详解

《C++作用域和标识符查找规则详解》在C++中,作用域(Scope)和标识符查找(IdentifierLookup)是理解代码行为的重要概念,本文将详细介绍这些规则,并通过实例来说明它们的工作原理,需... 目录作用域标识符查找规则1. 普通查找(Ordinary Lookup)2. 限定查找(Qualif

C#之List集合去重复对象的实现方法

《C#之List集合去重复对象的实现方法》:本文主要介绍C#之List集合去重复对象的实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C# List集合去重复对象方法1、测试数据2、测试数据3、知识点补充总结C# List集合去重复对象方法1、测试数据