分类: WINDOWS
2008-05-06 12:05:01
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)---#**@2) Math.cpp文件
#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);
};
/*@**#---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"1.4 客户端
#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;
}
//main.cpp文件关于如何调试dll请参阅附录A
#include
#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 = " <nReturnValue=0;
}
// 查询对象实现的接口IAdvancedMath
hr=pSimpleMath->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"2.2.2 MathFactory.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;
}
// MathFactory.h: interface for the CMathFactory class.2.2.3 MathFactory.cpp
//
//////////////////////////////////////////////////////////////////////
#ifndef MATHFACTORY_H
#define MATHFACTORY_H
#include
class 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.2.2.4 main.cpp
//
//////////////////////////////////////////////////////////////////////
#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_cast(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 = " <nReturnValue=0;
}
//查询对象实现的接口IAdvancedMath
hr=pSimpleMath->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.dll3.2.2 修改math.h文件
//
// 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.3 修改math.cpp文件
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);
//后面相同
//前面相同
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接口"<CComVariant varResult;
CComVariant varResult2;
CComVariant *pvars=new CComVariant[2];
CComVariant *pvars2=new CComVariant[1];
varResult.Clear();
pvars[1]=4;
pvars[0]=10;
DISPPARAMS disp = { pvars, NULL, 2, 0 };
hr=pDispatch->Invoke(0x1,
IID_ISimpleMath,
LOCALE_USER_DEFAULT,
DISPATCH_METHOD,
&disp,
&varResult,
NULL,
NULL);
if(SUCCEEDED(hr))
cout << "10 + 4 = " <varResult2.Clear();
pvars2[0]=10;
DISPPARAMS disp2={pvars2,NULL,1,0};
hr=pDispatch->Invoke(0x2,
IID_IAdvancedMath,
LOCALE_USER_DEFAULT,
DISPATCH_METHOD,
&disp,
&varResult2,
NULL,
NULL);
if(SUCCEEDED(hr))
cout << "10 Fabonacci is " <cout<<"IDispatch接口测试完毕"< ::system("pause");
}
/*
*IDispatch接口测试完毕
*/
if(SUCCEEDED(hr))
{
hr=pSimpleMath->Add(10,4,&nReturnValue);
if(SUCCEEDED(hr))
cout << "10 + 4 = " <nReturnValue=0;
}
// 查询对象实现的接口IAdvancedMath
hr=pSimpleMath->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 有一些相当于白皮书之类的东西。