分类:
2008-10-13 17:08:20
COM技术初探(三)-- 一个真正的COM
作者:
一、实现ISmipleMath,IAdvancedMath接口和DllGetClassObject()
1.1 实现ISmipleMath和IAdvancedMath接口
让我们将原来的CMath 类(CMath其实就是"COM技术初探(二)COM基础知识"里的那个CMath类)修改来实现ISmipleMath接口和IAdvancedMath接口。
修改的地方如下:
1) Math.h文件
/*@**#---2003-10-29 21:33:44 (tulip)---#**@ #include "interface.h"*/ #include "MathCOM.h"//新增加的,以替换上面的东东 class CMath : public ISimpleMath, public IAdvancedMath { private: ULONG m_cRef; private: int calcFactorial(int nOp); int calcFabonacci(int nOp); public: CMath(); //IUnknown Method STDMETHOD(QueryInterface)(REFIID riid, void **ppv); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)(); // ISimpleMath Method STDMETHOD (Add)(int nOp1, int nOp2,int * pret); STDMETHOD (Subtract)(int nOp1, int nOp2,int *pret); STDMETHOD (Multiply)(int nOp1, int nOp2,int *pret); STDMETHOD (Divide)(int nOp1, int nOp2,int * pret); // IAdvancedMath Method STDMETHOD (Factorial)(int nOp,int *pret); STDMETHOD (Fabonacci)(int nOp,int *pret); };2) Math.cpp文件
/*@**#---2003-10-29 21:32:35 (tulip)---#**@ #include "math.h" STDMETHODIMP CMath::QueryInterface(REFIID riid, void **ppv) {// 这里这是实现dynamic_cast的功能,但由于dynamic_cast与编译器相关。 if(riid == IID_ISimpleMath) *ppv = static_cast(this); else if(riid == IID_IAdvancedMath) *ppv = static_cast (this); else if(riid == IID_IUnknown) *ppv = static_cast (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; } STDMETHODIMP CMath::Add(int nOp1, int nOp2,int * pret) { *pret=nOp1+nOp2; return S_OK; } STDMETHODIMP CMath::Subtract(int nOp1, int nOp2,int * pret) { *pret= nOp1 - nOp2; return S_OK; } STDMETHODIMP CMath::Multiply(int nOp1, int nOp2,int * pret) { *pret=nOp1 * nOp2; return S_OK; } STDMETHODIMP CMath::Divide(int nOp1, int nOp2,int * pret) { *pret= nOp1 / nOp2; return S_OK; } int CMath::calcFactorial(int nOp) { if(nOp <= 1) return 1; return nOp * calcFactorial(nOp - 1); } STDMETHODIMP CMath::Factorial(int nOp,int * pret) { *pret=calcFactorial(nOp); return S_OK; } int CMath::calcFabonacci(int nOp) { if(nOp <= 1) return 1; return calcFabonacci(nOp - 1) + calcFabonacci(nOp - 2); } STDMETHODIMP CMath::Fabonacci(int nOp,int * pret) { *pret=calcFabonacci(nOp); return S_OK; } CMath::CMath() { m_cRef=0; }
#include "math.h" #include "MathCOM_i.c" 并将MathCOM.cpp里的DllGetClassObject()修改成如下: /********************************************************************* * Function Declare : DllGetClassObject * Explain : * Parameters : * REFCLSID rclsid -- * REFIID riid -- * void **ppv -- * Return : * STDAPI -- * Author : tulip * Time : 2003-10-29 22:03:53 *********************************************************************/ STDAPI DllGetClassObject(REFCLSID rclsid ,REFIID riid,void **ppv) { if(rclsid == CLSID_MathCom) { return pm_math->QueryInterface(riid,ppv); } return CLASS_E_CLASSNOTAVAILABLE; }1.4 客户端
//main.cpp文件 #include关于如何调试dll请参阅附录A#include "../MathCOM.h"//这里请注意路径 #include "../MathCOM_i.c"//这里请注意路径 #include using namespace std; void main(void) { //初始化COM库 HRESULT hr=::CoInitialize(0); ISimpleMath * pSimpleMath=NULL; IAdvancedMath * pAdvancedMath=NULL; int nReturnValue=0; hr=::CoGetClassObject(CLSID_MATHCOM, CLSCTX_INPROC, NULL, IID_ISimpleMath, (void **)&pSimpleMath); if(SUCCEEDED(hr)) { hr=pSimpleMath->Add(10,4,&nReturnValue); if(SUCCEEDED(hr)) cout << "10 + 4 = " < QueryInterface(IID_IAdvancedMath, (void **)&pAdvancedMath); if(SUCCEEDED(hr)) { hr=pAdvancedMath->Fabonacci(10,&nReturnValue); if(SUCCEEDED(hr)) cout << "10 Fabonacci is " << nReturnValue << endl; } pAdvancedMath->Release(); pSimpleMath->Release(); ::CoUninitialize(); ::system("pause"); return ; }
工程 |
文件 |
作用 |
MathCOM | Stdafx.h和stdafx.cpp | 预编译文件 |
MathCOM.cpp | Dll入口函数及其他重要函数定义的地方 | |
MathCOM.def | 模块定义文件 | |
MathCOM.idl | 接口定义文件(在1.2后如果编译的话应该还有四个文件) | |
math.h和math.cpp | ISmipleMath,IadvancedMath接口的实现类 | |
TestMathCOM | Main.cpp | MathCOM的客户端,用于测试MathCOM组件 |
2.2 增加IClassFactory的实现
此次我们将修改的文件如下
工程名 |
文件名 |
修改属性 |
MathCOM | MathCOM.cpp | 修改 |
MathFactory.h和MathFactory.cpp | 新增 | |
TestMathCOM | Main.cpp | 修改 |
2.2.1 MathCOM.cpp
#include "math.h" #include "MathCOM_i.c" #include "MathFactory.h" ////////////////////////////////////////////////////// // 服务器锁, 如果标志为S_OK,就可以卸载当前组件的服务器, // 关于引用计数及服务器跟COM对象的关系,后绪章节再讲解 LONG g_cObjectAndLocks=0; //standard self-registration table const char * g_RegTable[][3]={…………………. ………………………… STDAPI DllGetClassObject(REFCLSID rclsid ,REFIID riid,void **ppv) { if(rclsid==CLSID_MATHCOM) { CMathFactory *pFactory = new CMathFactory; // 创建类厂对象 if(pFactory == NULL) return E_OUTOFMEMORY; /////////////////////////////////////////// // 一般情况下COM希望用户只查询IClassFactory, // IClassFactory2及IUnknow HRESULT hr = pFactory->QueryInterface(iid, ppv); return hr; } return CLASS_E_CLASSNOTAVAILABLE; } STDAPI DllCanUnloadNow(void) { return (g_cObjectAndLocks==0)?S_OK:E_FAIL; }2.2.2 MathFactory.h
// MathFactory.h: interface for the CMathFactory class. // ////////////////////////////////////////////////////////////////////// #ifndef MATHFACTORY_H #define MATHFACTORY_H #include2.2.3 MathFactory.cppclass CMathFactory :public IClassFactory { public: CMathFactory():m_cRef(0){} //IUnknow Method STDMETHODIMP QueryInterface(REFIID,void**); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); //IClassFactory Method STDMETHODIMP CreateInstance(IUnknown * ,REFIID ,void **); STDMETHODIMP LockServer(BOOL fLock); protected: LONG m_cRef; }; #endif
// MathFactory.cpp: implementation of the CMathFactory class. // ////////////////////////////////////////////////////////////////////// #include "math.h" #include "MathFactory.h" extern LONG g_cObjectAndLocks; ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// STDMETHODIMP_(ULONG) CMathFactory::AddRef(void) { return InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) CMathFactory::Release(void) { return ::InterlockedDecrement(&m_cRef); } STDMETHODIMP CMathFactory::QueryInterface(REFIID riid,void ** ppv) { *ppv=NULL; if(riid==IID_IUnknown||riid==IID_IClassFactory) { *ppv=static_cast2.2.4 main.cpp(this); reinterpret_cast (*ppv)->AddRef(); return S_OK; } else return (*ppv=0),E_NOINTERFACE; } STDMETHODIMP CMathFactory::CreateInstance(IUnknown * pUnkOuter,REFIID riid,void ** ppv) { *ppv=NULL; // 现在不支持聚合 if(pUnkOuter!=NULL) return CLASS_E_NOAGGREGATION; CMath * pMath=new CMath; if(pMath==NULL) return E_OUTOFMEMORY; HRESULT hr=pMath->QueryInterface(riid,ppv); if(FAILED(hr)) delete pMath; return hr; } STDMETHODIMP CMathFactory::LockServer(BOOL fLock) { if(fLock) ::InterlockedIncrement(&g_cObjectAndLocks); else ::InterlockedDecrement(&g_cObjectAndLocks); return NOERROR; }
#include2.3 小结#include "../MathCOM.h" #include "../MathCOM_i.c" #include using namespace std; void main(void) { //初始化COM库 HRESULT hr=::CoInitialize(0); ISimpleMath * pSimpleMath=NULL; IAdvancedMath * pAdvancedMath=NULL; int nReturnValue=0; //如果还想用CoCreateInstance()得到接口指针,请如下使用先传一个IClassFactory接口 /*************** 这里请注意 *****************/ // 方法一 hr = ::CoCreateInstance(CLSID_MATHCOM, CLSCTX_INPROC, NULL, IID_ISimpleMath, (void*)&pSimpleMath); /////////////////////////////////////////////////////////////////////// // 这个方法的好处是不需要太多的代码, 让COM来处理真正的类厂创建对象的过程. // 同时这个函数的改进版CoCreateInstanceEx在分布式对象应用中可以一次取回多 // 个接口指针. 避免过多的在网络上浪费时间关于这个函数的用法我也会在以后的 // 章节中讲解. 您也可以参考MSDN或相关书藉 // 方法二 IClassFactory * pClassFactory=NULL; // 类厂接口指针 // 获取对象的类厂接口指针 hr=::CoGetClassObject(CLSID_MATHCOM, CLSCTX_INPROC, NULL, IID_IClassFactory, (void**)&pClassFactory); // 真正创建对象 hr = pClassFactory->CreateInstance(NULL,IID_ISimpleMath,(void**)&pSimpleMath); // 此方法的好处在于可以一次创建多个对象, 不需要 // 下面测试 IDispatch 接口 if(SUCCEEDED(hr)) { hr=pSimpleMath->Add(10,4,&nReturnValue); if(SUCCEEDED(hr)) cout << "10 + 4 = " < QueryInterface(IID_IAdvancedMath, (void **)&pAdvancedMath); if(SUCCEEDED(hr)) { hr=pAdvancedMath->Fabonacci(10,&nReturnValue); if(SUCCEEDED(hr)) cout << "10 Fabonacci is " << nReturnValue << endl; } pAdvancedMath->Release(); pSimpleMath->Release(); ::CoUninitialize(); return ; }
关于IDispatch接口请参阅MSND文档或<
3.2 支持派发接口的MathCOM组件
要让我们的MathCOM组件支持双接口,需要修改如下文件
工程名 |
文件名 |
MathCOM | MathCOM.idl |
Math.h和Math.cpp | |
TestMathCOM | Main.cpp |
3.2.1修改MathCOM.idl
MathCOM.idl文件需要修改三个地方。
1) 增加daul属性,以便表明我们的接口是双接口。
2) 将接口的基类由IUnknow接口变成IDispatch接口
3) 在接口的每个方法加上序号属性
修改后的MathCOM.idl如下(请自己找出修改的地方)
// MathCOM.idl : IDL source for MathCOM.dll // // This file will be processed by the MIDL tool to // produce the type library (MathCOM.tlb) and marshalling code. import "oaidl.idl"; import "ocidl.idl"; [ uuid(FAEAE6B7-67BE-42a4-A318-3256781E945A), helpstring("ISimpleMath Interface"), object, dual, // 这个标识我们前面没遇到过。 pointer_default(unique) ] interface ISimpleMath : IDispatch { [id(1)] HRESULT Add([in]int nOp1,[in]int nOp2,[out,retval]int * pret); [id(2)] HRESULT Subtract([in]int nOp1,[in]int nOp2,[out,retval]int * pret); [id(3)] HRESULT Multiply([in]int nOp1,[in]int nOp2,[out,retval] int * pret); [id(4)] HRESULT Divide([in]int nOp1,[in]int nOp2,[out,retval]int * pret); }; [ uuid(01147C39-9DA0-4f7f-B525-D129745AAD1E), helpstring("IAdvancedMath Interface"), object, dual, pointer_default(unique) ] interface IAdvancedMath : IDispatch { [id(1)] HRESULT Factorial([in]int nOp1,[out,retval]int * pret); [id(2)] HRESULT Fabonacci([in]int nOp1,[out,retval]int * pret); }; [ uuid(CA3B37EA-E44A-49b8-9729-6E9222CAE844), version(1.0), helpstring("MATHCOM 1.0 Type Library") ] library MATHCOMLib { importlib("stdole32.tlb"); importlib("stdole2.tlb"); [ uuid(3BCFE27E-C88D-453C-8C94-F5F7B97E7841), helpstring("MATHCOM Class") ] coclass MATHCOM { [default] interface ISimpleMath; interface IAdvancedMath; }; };3.2.2 修改math.h文件
//前面相同 CMath(); //IUnknown Method STDMETHOD(QueryInterface)(REFIID riid, void **ppv); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)(); //IDispatch Method STDMETHOD(GetTypeInfoCount)(UINT * pit){ return E_NOTIMPL;} STDMETHOD(GetTypeInfo)(UINT it,LCID lcid,ITypeInfo **ppti){ return E_NOTIMPL; } STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR ** pNames, UINT nNames, LCID lcid, DISPID * pdispids){ return E_NOTIMPL; } STDMETHOD(Invoke)(DISPID id, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pd, VARIANT * pVarResult, EXCEPINFO * pe, UINT *pu); // ISimpleMath Method STDMETHOD (Add)(int nOp1, int nOp2,int * pret); STDMETHOD (Subtract)(int nOp1, int nOp2,int *pret); STDMETHOD (Multiply)(int nOp1, int nOp2,int *pret); STDMETHOD (Divide)(int nOp1, int nOp2,int * pret); //后面相同3.2.3 修改math.cpp文件
//前面相同 CMath::CMath() { m_cRef=0; } /********************************************************************* * Function Declare : CMath::Invoke * Explain : IDispatch接口的Invoke方法 * Parameters : * DISPID id -- 方法的调度ID(请与idl中序号相比较) * REFIID riid -- 接口IID * LCID lcid -- 语言ID * WORD wFlags -- * DISPPARAMS * pd -- 传入参数结构(具体结构请参阅MSDN或本质论p294) * VARIANT * pVarResult -- 出参(VARIANT请参阅MSDN文档或<<深入解析ATL>>p56 * EXCEPINFO * pe -- 异常(一般为NULL) * UINT * pu -- * Return : * STDMETHODIMP -- * Author : tulip * Time : 2003-10-30 15:56:37 *********************************************************************/ STDMETHODIMP CMath::Invoke(DISPID id, REFIID riid, LCID lcid,WORD wFlags, DISPPARAMS * pd, VARIANT * pVarResult, EXCEPINFO * pe, UINT * pu) { if(riid==IID_ISimpleMath) { if(1==id) return Add(pd->rgvarg[0].intVal,pd->rgvarg[1].intVal,&pVarResult->intVal); else if (2==id) return Subtract(pd->rgvarg[0].intVal,pd->rgvarg[1].intVal,&pVarResult->intVal); else if (3==id) return Multiply(pd->rgvarg[0].intVal,pd->rgvarg[1].intVal,&pVarResult->intVal); else if (4==id) return Divide(pd->rgvarg[0].intVal,pd->rgvarg[1].intVal,&pVarResult->intVal); else return E_FAIL; } else if (riid==IID_IAdvancedMath) { if(1 == id) return Factorial(pd->rgvarg[0].intVal,&pVarResult->intVal); else if (2 == id) return Fabonacci(pd->rgvarg[0].intVal,&pVarResult->intVal); else return E_FAIL; } else return E_NOINTERFACE; }
#include#include "../MathCOM.h" #include "../MathCOM_i.c" #include using namespace std; #include void main(void) { ///////////////////////////////////////////////// // 初始化COM库,这里的真正含义是把COM加入适当的套间, // 关于套间的概念及其它相关概念会在后绪文章中讲解。 HRESULT hr = ::CoInitialize(NULL); ISimpleMath * pSimpleMath=NULL; IAdvancedMath * pAdvancedMath=NULL; IDispatch * pDispatch=NULL; // 派发接口指针 int nReturnValue=0; hr = ::CoCreateInstance(CLSID_MATHCOM, CLSCTX_INPROC, NULL, IID_ISimpleMath, (void*)&pSimpleMath); ///////////////////////// // 下面测试IDispatch接口 // pSimpleMath->QueryInterface(IID_IDispatch,(void**)&pDispatch); if(SUCCEEDED(hr)) { cout<<"下面进行IDispatch接口"< Invoke(0x1, IID_ISimpleMath, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL); if(SUCCEEDED(hr)) cout << "10 + 4 = " < Invoke(0x2, IID_IAdvancedMath, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult2, NULL, NULL); if(SUCCEEDED(hr)) cout << "10 Fabonacci is " < Add(10,4,&nReturnValue); if(SUCCEEDED(hr)) cout << "10 + 4 = " < QueryInterface(IID_IAdvancedMath, (void **)&pAdvancedMath); if(SUCCEEDED(hr)) { hr=pAdvancedMath->Fabonacci(10,&nReturnValue); if(SUCCEEDED(hr)) cout << "10 Fabonacci is " << nReturnValue << endl; } pAdvancedMath->Release(); pSimpleMath->Release(); pDispatch->Release(); ::CoUninitialize(); return ; }
五 参考资料
COM方面 (在COM方面除潘爱明的作著可以看看外,国内其他作者的东西都是垃圾。)
<
<
<
ATL方面
<<深入解析ATL>> (相当于COM界里的<
MSDN
MSJ 杂志的<
MSDN Dr.GUI online 具体位置在MSND/Welcome to the msnd library/
msnd resource/selected online columns/Dr.GUI online/
MSDN SDK 文档 具体位置 MSDN/Platform SDK Documentation/Component Servies
MSDN 的技术文章 位置 MSDN/Technical Articles/Component Object Model
MSDN BackGrounders 有一些相当于白皮书之类的东西。