2、一个比较简单的COM
此例子共有四个文件组成: 文件名 说明 Interface.h 接口类定义文件 Math.h和Math.cpp 实现类文件 Simple.cpp 主函数文件 这里用来当作COM的客户端
2.1 interface.h 文件
#ifndef INTERFACE_H #define INTERFACE_H #include <unknwn.h>
// static const GUID IID_ISimpleMath = };
// static const GUID IID_IAdvancedMath = };
interface ISimpleMath : public IUnknown { public: virtual int Add(int nOp1, int nOp2) = 0; virtual int Subtract(int nOp1, int nOp2) = 0; virtual int Multiply(int nOp1, int nOp2) = 0; virtual int Divide(int nOp1, int nOp2) = 0; };
interface IAdvancedMath : public IUnknown { public: virtual int Factorial(int nOp1) = 0; virtual int Fabonacci(int nOp1) = 0; }; #endif 此文件首先 #include <unknwn.h> 将 IUnknown 接口定义文件包括进来。 接下来定义了两个接口,GUID(Globally Unique Identifier全局唯一标识符)它能保证时间及空间上的唯一。 ISmipleMath接口里定义了四个方法,而IAdvancedMath接口里定义了二个方法。这些方法都是虚函数,而整个 ISmipleMath 与 IAdvancedMath 抽象类就作为二进制的接口。
2.2 math.h文件 #include "interface.h"
class CMath : public ISimpleMath, public IAdvancedMath { private: ULONG m_cRef;
private: int calcFactorial(int nOp); int calcFabonacci(int nOp);
public: //IUnknown Method STDMETHOD(QueryInterface)(REFIID riid, void **ppv); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)();
// ISimpleMath Method int Add(int nOp1, int nOp2); int Subtract(int nOp1, int nOp2); int Multiply(int nOp1, int nOp2); int Divide(int nOp1, int nOp2);
// IAdvancedMath Method int Factorial(int nOp); int Fabonacci(int nOp); }; 此类为实现类,他实现了ISmipleMath和IAdvancedMath两个接口类(当然也可以只实现一个接口类)。 请注意:m_cRef 是用来对象计数用的。当 m_cRef 为0组件对象应该自动删除。
2.3 math.cpp文件 #include "interface.h" #include "math.h"
STDMETHODIMP CMath::QueryInterface(REFIID riid, void **ppv) {// 这里这是实现dynamic_cast的功能,但由于dynamic_cast与编译器相关。 if(riid == IID_ISimpleMath) *ppv = static_cast<ISimpleMath*>((this); else if(riid == IID_IAdvancedMath) *ppv = static_cast<IAdvancedMath*>(this); else if(riid == IID_IUnknown) *ppv = static_cast<CMath*>(this); else { *ppv = 0; return E_NOINTERFACE; }
reinterpret_cast(*ppv)->AddRef(); //这里要这样是因为引用计数是针对组件的 return S_OK; }
STDMETHODIMP_(ULONG) CMath::AddRef() { return ++m_cRef; }
STDMETHODIMP_(ULONG) CMath::Release() { ULONG res = --m_cRef; // 使用临时变量把修改后的引用计数值缓存起来 if(res == 0) // 因为在对象已经销毁后再引用这个对象的数据将是非法的 delete this; return res; }
int CMath::Add(int nOp1, int nOp2) { return nOp1+nOp2; }
int CMath::Subtract(int nOp1, int nOp2) { return nOp1 - nOp2; }
int CMath::Multiply(int nOp1, int nOp2) { return nOp1 * nOp2; }
int CMath::Divide(int nOp1, int nOp2) { return nOp1 / nOp2; }
int CMath::calcFactorial(int nOp) { if(nOp <= 1) return 1;
return nOp * calcFactorial(nOp - 1); }
int CMath::Factorial(int nOp) { return calcFactorial(nOp); }
int CMath::calcFabonacci(int nOp) { if(nOp <= 1) return 1;
return calcFabonacci(nOp - 1) + calcFabonacci(nOp - 2); }
int CMath::Fabonacci(int nOp) { return calcFabonacci(nOp); } CMath::CMath() { m_cRef=0; } 此文件是CMath类定义文件。
2.4 simple.cpp文件 #include "math.h" #include <iostream>
using namespace std;
int main(int argc, char* argv[]) { ISimpleMath *pSimpleMath = NULL;//声明接口指针 IAdvancedMath *pAdvMath = NULL;
//创建对象实例,我们暂时这样创建对象实例,COM有创建对象实例的机制 CMath *pMath = new CMath;
//查询对象实现的接口ISimpleMath pMath->QueryInterface(IID_ISimpleMath, (void **)&pSimpleMath); if(pSimpleMath) cout << "10 + 4 = " << pSimpleMath->Add(10, 4) << endl;
//查询对象实现的接口IAdvancedMath pSimpleMath->QueryInterface(IID_IAdvancedMath, (void **)&pAdvMath); if(pAdvMath) cout << "10 Fabonacci is " << pAdvMath->Fabonacci(10) << endl;
pAdvMath->Release(); pSimpleMath->Release(); return 0; } 此文件相当于客户端的代码,首先创建一个CMath对象,再根据此对象去查询所需要的接口,如果正确得到所需接口指针,再调用接口的方法,最后再将接口的释放掉。
2.5 Math组件的二进制结构图
 图1.3 Math组件二进制结构图
2.6 小结
此例子从严格意义上来并不是真正的COM组件(他不是dll),但他已符合COM的最小要求(实现IUnknown接口)。
|