编写可复用性更好的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

相关文章

C++ move 的作用详解及陷阱最佳实践

《C++move的作用详解及陷阱最佳实践》文章详细介绍了C++中的`std::move`函数的作用,包括为什么需要它、它的本质、典型使用场景、以及一些常见陷阱和最佳实践,感兴趣的朋友跟随小编一起看... 目录C++ move 的作用详解一、一句话总结二、为什么需要 move?C++98/03 的痛点⚡C++

JAVA项目swing转javafx语法规则以及示例代码

《JAVA项目swing转javafx语法规则以及示例代码》:本文主要介绍JAVA项目swing转javafx语法规则以及示例代码的相关资料,文中详细讲解了主类继承、窗口创建、布局管理、控件替换、... 目录最常用的“一行换一行”速查表(直接全局替换)实际转换示例(JFramejs → JavaFX)迁移建

Go异常处理、泛型和文件操作实例代码

《Go异常处理、泛型和文件操作实例代码》Go语言的异常处理机制与传统的面向对象语言(如Java、C#)所使用的try-catch结构有所不同,它采用了自己独特的设计理念和方法,:本文主要介绍Go异... 目录一:异常处理常见的异常处理向上抛中断程序恢复程序二:泛型泛型函数泛型结构体泛型切片泛型 map三:文

详解C++ 存储二进制数据容器的几种方法

《详解C++存储二进制数据容器的几种方法》本文主要介绍了详解C++存储二进制数据容器,包括std::vector、std::array、std::string、std::bitset和std::ve... 目录1.std::vector<uint8_t>(最常用)特点:适用场景:示例:2.std::arra

C++构造函数中explicit详解

《C++构造函数中explicit详解》explicit关键字用于修饰单参数构造函数或可以看作单参数的构造函数,阻止编译器进行隐式类型转换或拷贝初始化,本文就来介绍explicit的使用,感兴趣的可以... 目录1. 什么是explicit2. 隐式转换的问题3.explicit的使用示例基本用法多参数构造

C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解

《C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解》:本文主要介绍C++,C#,Rust,Go,Java,Python,JavaScript性能对比全面... 目录编程语言性能对比、核心优势与最佳使用场景性能对比表格C++C#RustGoJavapythonjav

MyBatis中的两种参数传递类型详解(示例代码)

《MyBatis中的两种参数传递类型详解(示例代码)》文章介绍了MyBatis中传递多个参数的两种方式,使用Map和使用@Param注解或封装POJO,Map方式适用于动态、不固定的参数,但可读性和安... 目录✅ android方式一:使用Map<String, Object>✅ 方式二:使用@Param

SpringBoot实现图形验证码的示例代码

《SpringBoot实现图形验证码的示例代码》验证码的实现方式有很多,可以由前端实现,也可以由后端进行实现,也有很多的插件和工具包可以使用,在这里,我们使用Hutool提供的小工具实现,本文介绍Sp... 目录项目创建前端代码实现约定前后端交互接口需求分析接口定义Hutool工具实现服务器端代码引入依赖获

C++打印 vector的几种方法小结

《C++打印vector的几种方法小结》本文介绍了C++中遍历vector的几种方法,包括使用迭代器、auto关键字、typedef、计数器以及C++11引入的范围基础循环,具有一定的参考价值,感兴... 目录1. 使用迭代器2. 使用 auto (C++11) / typedef / type alias

利用Python在万圣节实现比心弹窗告白代码

《利用Python在万圣节实现比心弹窗告白代码》:本文主要介绍关于利用Python在万圣节实现比心弹窗告白代码的相关资料,每个弹窗会显示一条温馨提示,程序通过参数方程绘制爱心形状,并使用多线程技术... 目录前言效果预览要点1. 爱心曲线方程2. 显示温馨弹窗函数(详细拆解)2.1 函数定义和延迟机制2.2