COM原理讲述Word文档格式.docx
《COM原理讲述Word文档格式.docx》由会员分享,可在线阅读,更多相关《COM原理讲述Word文档格式.docx(44页珍藏版)》请在冰点文库上搜索。
每个标准的COM组件都需要一个接口定义文件,文件的扩展名为IDL。
让我们看IUnknow接口的定义文件是怎样的。
[
local,
object,
uuid(00000000-0000-0000-C000-000000000046),
pointer_default(unique)
]
interfaceIUnknown
{
typedef[unique]IUnknown*LPUNKNOWN;
cpp_quote("
//////////////////////////////////////////////////////////////////"
)
//IID_IUnknownandallothersystemIIDsareprovidedinUUID.LIB"
//Linkthatlibraryinwithyourproxies,clientsandservers"
HRESULTQueryInterface(
[in]REFIIDriid,
[out,iid_is(riid)]void**ppvObject);
ULONGAddRef();
ULONGRelease();
}
[local]属性禁止产生网络代码。
[object]属性是表明定义的是一个COM接口,而不是DEC风格的接口。
[uuid]属性给接口一个GUID。
[unique]属性表明null(空)指针为一个合法的参数值。
[pointer_defaul]属性所有的内嵌指针指定一个默认指针属性
typedef[unique]IUnknown*LPUNKNOWN;
这是一个类型定义
cpp_quote这个比较有趣,这是一个在idl文件写注解的方法。
这些注解将保存到***.h和***_i.c文件中
[in]表示这个参数是入参
[out]表示这个参数是出参
[iid_is(riid)]表示这个参数需要前一个的riid参数。
Iunknown:
COM要求(最基本的要求)所有的接口都需要从IUnknown接口直接或间接继承。
IUnkown接口定义了三个方法。
HRESULTQueryInterface([in]REFIIDriid,[out]void**ppv);
ULONGAddRef();
ULONGRelease();
其中AddReft()和Release()负责对象引用计数用的,而QueryInterface()方法是用于查询所实现接口用的。
每当COM组件被引用一次就应调用一次AddRef()方法。
而当客户端在释放COM组件的某个接口时就需要调用Release()方法。
这里所讲的请在下面的例子仔细体会。
COM简单的例子
此例子共有四个文件组成:
文件名
说明
Interface.h
接口类定义文件
Math.h和Math.cpp
实现类文件
Simple.cpp主函数文件
这里用来当作COM的客户端
interface.h文件:
#ifndefINTERFACE_H
#defineINTERFACE_H
#include<
unknwn.h>
//{7C8027EA-A4ED-467c-B17E-1B51CE74AF57}
staticconstGUIDIID_ISimpleMath=
{0x7c8027ea,0xa4ed,0x467c,{0xb1,0x7e,0x1b,0x51,0xce,0x74,0xaf,0x57}};
//{CA3B37EA-E44A-49b8-9729-6E9222CAE84F}
staticconstGUIDIID_IAdvancedMath=
{0xca3b37ea,0xe44a,0x49b8,{0x97,0x29,0x6e,0x92,0x22,0xca,0xe8,0x4f}};
interfaceISimpleMath:
publicIUnknown
public:
virtualintAdd(intnOp1,intnOp2)=0;
virtualintSubtract(intnOp1,intnOp2)=0;
virtualintMultiply(intnOp1,intnOp2)=0;
virtualintDivide(intnOp1,intnOp2)=0;
};
interfaceIAdvancedMath:
virtualintFactorial(intnOp1)=0;
virtualintFabonacci(intnOp1)=0;
#endif
此文件首先#include<
将IUnknown接口定义文件包括进来。
接下来定义了两个接口,GUID(GloballyUniqueIdentifier全局唯一标识符)它能保证时间及空间上的唯一。
ISmipleMath接口里定义了四个方法,而IAdvancedMath接口里定义了二个方法。
这些方法都是虚函数,而整个ISmipleMath与IAdvancedMath抽象类就作为二进制的接口。
math.h文件:
#include"
interface.h"
classCMath:
publicISimpleMath,publicIAdvancedMath
private:
ULONGm_cRef;
intcalcFactorial(intnOp);
intcalcFabonacci(intnOp);
//IUnknownMethod
STDMETHOD(QueryInterface)(REFIIDriid,void**ppv);
STDMETHOD_(ULONG,AddRef)();
STDMETHOD_(ULONG,Release)();
//ISimpleMathMethod
intAdd(intnOp1,intnOp2);
intSubtract(intnOp1,intnOp2);
intMultiply(intnOp1,intnOp2);
intDivide(intnOp1,intnOp2);
//IAdvancedMathMethod
intFactorial(intnOp);
intFabonacci(intnOp);
此类为实现类,他实现了ISmipleMath和IAdvancedMath两个接口类(当然也可以只实现一个接口类)。
请注意:
m_cRef是用来对象计数用的。
当m_cRef为0组件对象应该自动删除。
math.cpp文件:
math.h"
STDMETHODIMPCMath:
:
QueryInterface(REFIIDriid,void**ppv)
{//这里这是实现dynamic_cast的功能,但由于dynamic_cast与编译器相关。
if(riid==IID_ISimpleMath)
*ppv=static_cast(this);
elseif(riid==IID_IAdvancedMath)
elseif(riid==IID_IUnknown)
else{
*ppv=0;
returnE_NOINTERFACE;
}
reinterpret_cast(*ppv)->
AddRef();
//这里要这样是因为引用计数是针对组件的
returnS_OK;
STDMETHODIMP_(ULONG)CMath:
AddRef()
return++m_cRef;
Release()
ULONGres=--m_cRef;
//使用临时变量把修改后的引用计数值缓存起来
if(res==0)//因为在对象已经销毁后再引用这个对象的数据将是非法的
deletethis;
returnres;
intCMath:
Add(intnOp1,intnOp2)
returnnOp1+nOp2;
Subtract(intnOp1,intnOp2)
returnnOp1-nOp2;
Multiply(intnOp1,intnOp2)
returnnOp1*nOp2;
Divide(intnOp1,intnOp2)
returnnOp1/nOp2;
calcFactorial(intnOp)
if(nOp<
=1)
return1;
returnnOp*calcFactorial(nOp-1);
Factorial(intnOp)
returncalcFactorial(nOp);
calcFabonacci(intnOp)
returncalcFabonacci(nOp-1)+calcFabonacci(nOp-2);
Fabonacci(intnOp)
returncalcFabonacci(nOp);
CMath:
CMath()
m_cRef=0;
}
此文件是CMath类定义文件。
simple.cpp:
iostream>
usingnamespacestd;
intmain(intargc,char*argv[])
ISimpleMath*pSimpleMath=NULL;
//声明接口指针
IAdvancedMath*pAdvMath=NULL;
//创建对象实例,我们暂时这样创建对象实例,COM有创建对象实例的机制
CMath*pMath=newCMath;
//查询对象实现的接口ISimpleMath
pMath->
QueryInterface(IID_ISimpleMath,(void**)&
pSimpleMath);
if(pSimpleMath)
cout<
<
"
10+4="
<
pSimpleMath->
Add(10,4)<
endl;
//查询对象实现的接口IAdvancedMath
QueryInterface(IID_IAdvancedMath,(void**)&
pAdvMath);
if(pAdvMath)
10Fabonacciis"
pAdvMath->
Fabonacci(10)<
Release();
return0;
此文件相当于客户端的代码,首先创建一个CMath对象,再根据此对象去查询所需要的接口,如果正确得到所需接口指针,再调用接口的方法,最后再将接口的释放掉。
Math组件的二进制结构图:
小结:
例子从严格意义上来并不是真正的COM组件(他不是dll),但他已符合COM的最小要求(实现IUnknown接口)。
接下来我们来做一COMdll。
创建一个COM组件
创建过程示意图如下:
在这一过程中我们将完成三个步骤:
创建dll的入口函数,定义接口文件,实现注册功能
1.一个类型为win32dll工程:
创建一个名为MathCOM的win32dll工程。
在向导的第二步选择"
Asmipledllproject"
选项。
当然如果你选择一个空的工程,那你自己完成DllMain定义吧。
2.接口文件:
生成一个名为MathCOM.idl的接口文件。
并将此文件加入到刚才创建的那个工程里。
//MathCOM.idl文件
//MathCOM.idl:
IDLsourceforMathCOM.dll
//
//ThisfilewillbeprocessedbytheMIDLtoolto
//producethetypelibrary(MathCOM.tlb)andmarshallingcode.
import"
oaidl.idl"
;
ocidl.idl"
[
uuid(FAEAE6B7-67BE-42a4-A318-3256781E945A),
helpstring("
ISimpleMathInterface"
),
object,
pointer_default(unique)
]
interfaceISimpleMath:
IUnknown
{
HRESULTAdd([in]intnOp1,[in]intnOp2,[out,retval]int*pret);
HRESULTSubtract([in]intnOp1,[in]intnOp2,[out,retval]int*pret);
HRESULTMultiply([in]intnOp1,[in]intnOp2,[out,retval]int*pret);
HRESULTDivide([in]intnOp1,[in]intnOp2,[out,retval]int*pret);
};
uuid(01147C39-9DA0-4f7f-B525-D129745AAD1E),
IAdvancedMathInterface"
interfaceIAdvancedMath:
HRESULTFactorial([in]intnOp1,[out,retval]int*pret);
HRESULTFabonacci([in]intnOp1,[out,retval]int*pret);
uuid(CA3B37EA-E44A-49b8-9729-6E9222CAE844),
version(1.0),
helpstring("
MATHCOM1.0TypeLibrary"
libraryMATHCOMLib
importlib("
stdole32.tlb"
);
stdole2.tlb"
uuid(3BCFE27E-C88D-453C-8C94-F5F7B97E7841),
MATHCOMClass"
coclassMATHCOM
[default]interfaceISimpleMath;
interfaceIAdvancedMath;
在编译此工程之前请检查Project/Setting/MIDL中的设置。
正确设置如下图:
在正确设置后,如编译无错误,那么将在工程的目录下产生四个
作用
MathCOM.h
接口的头文件,如果想声明或定义接口时使用此文件
MathCOM_i.c
定义了接口和类对象以及库,只有在要使用到有关与GUID有关的东西时才引入此文件,此文件在整个工程中只能引入一次,否则会有重复定义的错误
MathCOM_p.c
用于存根与代理
dlldata.c
不明
3.增加注册功能:
作为COM必须要注册与注销的功能。
增加一个MathCOM.def文件,DEF文件是模块定义文件(ModuleDefinitionFile)。
它允许引出符号被化名为不同的引入符号。
//MathCOM.def文件
MathCOM.def:
Declaresthemoduleparameters.
LIBRARY"
MathCOM.DLL"
EXPORTS
DllCanUnloadNow@1PRIVATE
DllGetClassObject@2PRIVATE
DllRegisterServer@3PRIVATE
DllUnregisterServer@4PRIVATE
DllUnregisterServer 这是函数名称@4<――这是函数序号PRIVATE
接下来大致介绍一下DllRegisterServer()和DllUnregisterServer()。
(其他两个函数的作用将在后面介绍)
DllRegisterServer()和DllUnregisterServer()
DllRegisterServer()函数的作用是将COM服务器注册到本机上。
DllUnregisterServer()函数的作用是将COM服务器从本机注销。
MathCOM.cpp文件:
现在请将MathCOM.cpp文件修改成如下:
//MATHCOM.cpp:
DefinestheentrypointfortheDLLapplication.
stdafx.h"
objbase.h>
initguid.h>
MathCOM.h"
//standardself-registrationtable
constchar*g_RegTable[][3]={
{"
CLSID\\{3BCFE27E-C88D-453C-8C94-F5F7B97E7841}"
0,"
MathCOM"
},
CLSID\\{3BCFE27E-C88D-453C-8C94-F5F7B97E7841}\\InprocServer32"
0,
(constchar*)-1/*表示文件名的值*/},
CLSID\\{3BCFE27E-C88D-453C-8C94-F5F7B97E7841}\\ProgID"
tulip.MathCOM.1"
tulip.MathCOM.1\\CLSID"
{3BCFE27E-C88D-453C-8C94-F5F7B97E7841}"
HINSTANCEg_hinstDll;
BOOLAPIENTRYDllMain(HANDLEhModule,
DWORDul_reason_for_call,
LPVOIDlpReserved
)
g_hinstDll=(HINSTANCE)hModule;
returnTRUE;
/*********************************************************************
*FunctionDeclare:
DllUnregisterServer
*Explain:
self-unregistrationroutine
*Parameters:
*void--
*Return:
*STDAPI--
*Author:
tulip
*Time:
2003-10-2919:
07:
42
*****************