还剩27页未读,继续阅读
本资源只提供10页预览,全部文档请下载后查看!喜欢就下载吧,查找使用更方便
文本内容:
学习笔记一初识COM COM组件对象模型历来以难学著称,有人曾说过这样的话COM^ComponentObject ModeL世界上只有两个程序员真正理解他们都在微软工作这句话虽然有点过,但基本上说出COM,了确实有些难理解不过,不用紧张,本文并不探求多深多高的技术领域,而是带领大COM家浏览一下我们的就像本文的题目同样初识首先,我们先来理解一下有关COM,COM COM的概念组件对象模酚是微软企业的最高级的、包罗万象的二进COM ComponentObject Model,制通信规范,用于软件组件间跨越多种进程、机器、硬件和操作系统进行互操作下面我们来看看的某些特点COM在中,应用程序不是通过诸如的函数进行操纵程序是由对象构COM ShowWindowAPI成的,对象向外提供一种或多种接口接口是一组有关的函数,函数操作他们所属的对象不能直接访问对象中的数据,而只能通过对象的接口函数访问学过和数据构造的人应当对C++上述说法并不陌生在中,没有指向对象的指针这种东西,有的只是指向对象接口的指针实际上,是指向COM另一种指针的指针第二个指针指向一种指针表,表中的指针指向接口组员函数该指针表称为将指针指向对象后,就可以通过调用接口中的组员函数与该对象通信怎样将指针指向第一种对象呢?可以调用一种返回指向对象指针的函数如COMCoCreatelnstaceo对象都提供一种叫的接口,该接口包括措施、和COM IUnknownAddRef Release每个接口都是从接口派生出来的前两个措施操纵一种控制对Querylnterfaceo IUnknown象有效期限的内部引用计数当对象第一次被创立时,创立者必须调用该对象的将AddRef,计数加每当其他的顾客将一种指针指向该对象时,必须再次调用该对象的措施1AddRef当顾客不再使用对象时,它调用对象的措施,将引用计数减当最终一种顾客调Release1o用对象的措施后,计数值变为导致对象释放自己下面是和Release0,AddRef Release措施的简朴实现ULONG IUnknown::AddRefvoidm_RefCount++;return m_RefCount;}ULONG IUnknown::Releasevoidm_RefCount-;ifm_RefCount==0客户可使用lUnkown中的组员函数Queryinterface查询组件与否支持某个特定的接口,假如支持,将返回一种指向此接口的指针;假如不支持,则返回一种错误代码HRESULT_stdcall Queryinterfaceconst IID iid,void**ppv;其中,第一种参数iid标志客户所需的接口,第二个参数ppv寄存所祈求接口的指针地址
4.使用Queryinterface[cpp]view plaincopy
1.lUnkown*pl;
2.//define apointer for the interface
3.IX*pIX=NULL;
4.//ask forinterface IX
5.HRESULT hr=pI-QueryInterfaceIID.IX,void**pIX;
6.//check returnvalue
7.ifSUCCEEDEDhr
8.{
9.//use interface
10.pIX-fx;11・}在这段代码中,我们查询了pl与否支持由HD.IX所标识的接口若成功返回,那么我们就可以使用它返回的指针了
5.Queryinterface的实现Quei^ylnterface需要根据某个给定的HD返回指向对应接口的指针假如组件支持客户指定的接口,那么返回S_OK及对应的指针,否则,返回E_NOINTERFACE并将对应的指针返回值设为NULLo例如我们有类CA,及其接口的继承关系o lUnkownylUnkownVV接【vv接口lIX IY图类及其接口的继承关系2CA[cpp]view plaincopyHRESULT_stdcall CA::QueryInterfaceconst IIDiid,void**ppv{
1.
2.ifiid==IID.lUnkown
3.
4.//the client wants thelUnkown interface*ppv=static_castIX*this;
56.else ifiid==IDD.IX
7.
8.//the clientwants theIX interface9I*ppv=static_castIX*this;
10.
11.else ifiid==IDD.IY12・//the clientwants theIY interface
113.*ppv=static_castIY*this;
14.else
15.//we dontsupport theinterface theclientwants.
16.//be sureto setthe resultingpointer toNULL1I*ppv=NULL;7return E_NOINTERFACE;•
18.static_castIUnkown**ppv-AddRef;
19.return S_OK;
20.在这里,Queryinterface使用一种简朴的if-then-else语句实现当然用其
28.}
21.他任何一种可以将一种类型映射成此外一种类型的构造也是可以实现的如可用数组、
22.哈希表或树来实现
23.
6.Queryinterface的实现规则
24.1Queryinterface必须总是返回同一lUnkown指针
25.
26.可以通过查询两个接口的lUnkown接口,并比较其返回值来确定这两个接口与否指向同一组件2若客户曾经获取过某个接口,那么它将总能获取此接口3客户可以再次获取已经拥有的接口4客户可以返回到起始接口5若可以从某个接口获取某特定接口,那么可以从任意接口都将可以获取此接口COM学习笔记四IDispatch接口原理与应用目录接口原理与应用IDispatch目录序言接口的定义IDispatch创立支持接口的对象IDispatch COM原则方式调用接口的措施IDispatch采用智能指针类调用接口的措施ATL IDispatch使用类调用的措施COleDispatchDriver IDispatch序言:尽管接口是跨语言的,不过诸多语言在使用时更多地通过技术来和com comAutomation com对象通信接口是的关键技术IDispatch Automation尽管程序员并不喜欢甚至讨厌使用接口,由于调用它实在是非常的麻烦并且易C++IDispatch出错不过不可否认大量的现存组件是只基于接口技术而开发的,有时候你没有选IDispatch择,并且假如你想要写某些组件可以在上运行,你也离不开接口,由于web IDisptchVBScript这样的脚本语言不会聪颖到可以理解你的基于虚函数表的一般接口com与其规避它,不如征服它本文中,我将结合自己的经验和读者一起探讨接口的多IDispatch种应用并简介几种可以加紧我们使用接口的类IDispatch C++接口的定义IDispatch参照文献中的定义―oaidl.h・・・MIDL_INTERFACE0002040000000000C0046IDispatch:public IUnknown{public:virtual HRESULT STDMETHODCALLTYPE GetTypelnfoCount/*[out]*/UINT*pctinfo=0;virtual HRESULT STDMETHODCALLTYPE GetTypelnfo/*[in]VUINT iTInfo,/*[in]*/LCID Icid,/*[out]*/ITypelnfo**ppTlnfo=0;virtual HRESULT STDMETHODCALLTYPE GetlDsOfNames/*[in]7REFIID riid,/*[size_is][in]*/LPOLESTR*rgszNames,/*[in]7UINT cNames,/*[in]*/LCID Icid,/*[size_is][out]7DISPID*rgDispld=0;virtual/*[local]*/HRESULT STDMETHODCALLTYPElnvoke/*[in]7DISPID dispIdMember,/*[in]7REFIID riid,/*[in]*/LCIDIcid,/*[in]7WORD wFlags,/*[out][in]*/DISPPARAMS*pDispParams,/*[out]7VARIANT*pVarResult,/*[out]*/EXCEPINFO*pExceplnfo,/*[out]7UINT*puArgErr=0;};我们通过的到注册表中搜索,可以搜索到如下成果:注意在接口IDispatch GUIDIDispatch GUID下面尚有两个展开的项,他们分别是和接口这两个接口在自动化GUID ITypelnfoITypeLib应用中也是非常重要的此后我们会常常看到他们接口措施简介IDispatch1HRESULT STDMETHODCALLTYPEGetTypelnfoCount/*[out]*/UINT*pctinfo;判断实现了接口的对象与否支持类型信息,假如返回则支持,返回则不支持IDispatch102HRESULTSTDMETHODCALLTYPEGetTypelnfo/*[in]*/UINT iTInfo,/*[in]*/LCID Icid,/*[out]7ITypelnfo**ppTlnfo=0;获取对象的类型信息接口指针,该措施调用之前总应当先调用措施GetTypelnfoCount・..确认与否支持类型信息参数必须为否则该措施将返回表达失败iTInfo0,DISP_E_BADINDEX参数传递类型信息的地区标志接口的措施和属性在不一样的语言环境地区标Icid IDispatch志可以使用不一样的名称,因而不一样也许会导致返回的接口指针不一样假Icid ITypelnfo如我们创立的组件根据不一样的地区标志对属性和措施起不一样的名字,我们就要使用这个参数,否则可以忽视3HRESULTSTDMETHODCALLTYPEGetlDsOfNames/*[in]7REFIID riid,/*[size_is][in]*/LPOLESTR*rgszNames,/*[in]*/UINT cNames,/*[in]*/LCID Icid,/*[sizejs][out]*/DISPID*rgDispld忙接口的属性实质上是措施,措施也就是组员函数,接口把所有组员函数的IDispa hIDispatch入口地址放入到一种数组中,并且内部组织了一种将数组索引和措施名称一一映射°我们Map,常见的就是这些措施在数组中的索引°假如我们想调用某一种措施,我们就需要DISPID来让我们找到该措施的地址DISPID参数必须为riid NULL参数为字符串数组,第一种字符串为措施或者属性的名称,后续的字符串为参rgszNames数名称,接口的参数也可以有名字IDispatch参数指定数组中字符串的个数cNames rgszNames参数传递地区标志,同措施中的参数Icid GetTypelnfo参数输出一种数组,每个数组组员对应中的一种字符串名称rgDispId rgszNames有关的深入阐明DISPIDtypedef LONGDISPID;typedef DISPIDMEMBERID;不不小于等于的值都是有特殊意义的,如下面简介的--DISPID0/*DISPID reserved to indicatean unknown”name*//*only reserved for datamembers properties;reused asa methoddispid below*/〃假如函数找不到与名称相对应的返回该值GetIDsOfNames DISPID,#define DISPID_UNKNOWN-1/*DISPID reservedfor thevalue property*/〃假如调用时不指定措施或者属性,则使用该缺省值#define DISPID_VALUE0/*The followingDISPID is reservedtoindicate theparam*that isthe right-hand-side ornputn valueof aPropertyPut7〃表明属性设置函数中某一种参数将接受新属性值#define DISPID_PROPERTYPUT-3/*DISPID reservedfor thestandard NewEnum”method*/〃用于集合对象#define DISPID_NEWENUM-4脚本语言中可以用口调用该措施/*DISPID reservedforthestandard Evaluatemethod,*/#define DISPID_EVALUATE-5/*表达某措施具有和构造函数相似的功能*/#define DISPID_CONSTRUCTOR-6//表达某措施具有和析构函数相似的功能*/#define DISPID_DESTRUCTOR-7/*The Collectproperty.You usethis propertyif themethod youare callingthroughInvoke isan accessorfunction.*/#define DISPID_COLLECT-8/*The range-500through-999is reservedfor Controls*//*The range0x80010000through0x8001FFFF is reservedforControls*//*The range-5000through-5499is reservedfor ActiveXAccessability*//*The range-through-2499is reservedfor VB5*//*The range-3900through-3999isreservedfor Forms7/*The range-5500through-5550isreservedfor Forms*//*The remainderof thenegative DISPIDsare reservedfor futureuse*/4HRESULTSTDMETHODCALLTYPElnvoke/*[in]*/DISPID dispIdMember,/*[in]*/REFIID riid,/*[in]*/LCID Icid,/*[in]*/WORD wFlags,/*[out][in]*/DISPPARAMS*pDispParams,/*[out]*/VARIANT*pVarResult,/*[out]*/EXCEPINFO*pExceplnfo,/*[out]VUINT*puArgErr参数为措施或者属性的就是我们通过获得的dispIdMember DISPID,GetIDsOfNames参数必须为riid IID_NULL参数为地区标志,同前面两个措施Icid参数有下面若干值一-wFlagsValue Description表达将调用措施假如属性DISPATCH_METHOD名称和措施名称相似,则和标志一起设置DISPATCH_PROPERTYGET获得属性DISPATCH_PROPERTYGET设置属性DISPATCH_PROPERTYPUT通过引用设置属性DISPATCH_PROPERTYPUTREF参数为参数信息数组,元素类型为pDispParams DISPPARAMStypedefstruct tagDISPPARAMS〃参数数组/*[sizejs]*/VARIANTARG*rgvarg;〃参数中的数组/*[sizejs]*/DISPID gdispidNamedArgs;DISPID数组中的参数个数UINT cArgs;//〃命名参数的个数UINT cNamedArgs;}DISPPARAMS;注意假如是属性设置,数组中只有一种参数,假如是措施调用,可以包括到多种参数;rgvarg0数组中的参数的域为时,该参数可写,否则为只读;rgvarg VARIANTARGvt VT_BYREF数组中的参数的域为时,该参数可以作为输出参rgvarg VARIANTARGvt VT_BYREF数;数组中的参数的域不为时,参数内的字符串或者指针变rgvarg VARIANTARGvt VT_BYREF量的所有权在客户,客户必须自己释放资源,实现接口的对象要想保留数据,则要IDispatch拷贝数据或者调用指针变量的函数AddRef数组中的参数的域为并且域为rgvarg VARIANTARGvt VT_ERROR,scode时,该参数可以作为可选参数,域作用是寄存返回的DISP EPARAMNOTFOUND scodeHRESULTo原理与应用》中曾经提到数组中的参数寄存次序和客户程序调用时传递的参数次«COM rgvarg序刚好相反,我这里对此表达怀疑有关命名参数的详细讨论我在背面将谈到,目前只需要懂得它可以不受参多次序的限制参数保留函数调用后的返回信息,由于已经将返回值用于通用的pVarResult InvokeCOMHRESULT;参数返回异常信息;pExcepInfo参数包括第一种产生错误的参数索引,当返回的值为puArgErr InvokeHRESULT或值时DISP_E_TYPEMISMATCH DISP_E_PARAM_NOTFOUND创立支持接口的对象IDispatch COM本节我们运用创立一种对象类从接口派生,并且同步支ATL
7.1COM BabyCBaby IDispatcho持方式我们设想他有一种属性性别,我们可以设置和获取宝宝的性别属性vtable Gender目前我们先创立一种项目然后添加类选择接口类型为双重我们先在ATL IDspCOMCBaby,看一下生成的文献中的接口定义idl[object,uuid22C1BD80-2937-42FB-A7F8-5CEBD1257CB8,dual,nonextensible,旧接口helpstring abypointer_defaultunique]interface IBaby:IDispatch};目前派生自接口,除了继承了的个措施和的个措施IBaby IDispatchIDispatch4IUnknown3外,它目前还没有任何自己的措施和属性将帮我们实现前面的个措施,我们无需关怀ATL7我们目前来创立自己的属性Gender我们通过向导创立了该属性,文献如下idlinterface IBaby:IDispatch{[propget,id1,helpstring属性Gender11]HRESULT Gender[out,retval]BSTR*pVal;[propput,id1,helpstring属性Gender11]HRESULT Gender[in]BSTR newVal;;我们可以看到其实属性就是一对措施----设置和获取属性措施两个措施共用一种值为在类的头文献中我们添加如下代码DISPID,1public:STDMETHODget_GenderBSTR*pVal;STDMETHODput_GenderBSTR newVal;private:CComBSTR m_Gender;在类的实现文献中我们添加如下代码STDMETHODIMP CBaby::get_GenderBSTR*pValm_Gender.CopyTopVal;return S_OK;}STDMETHODIMP CBaby::put_GenderBSTR newVal{m Gender=newVal;return S_OK;}原则方式调用接口的措施IDispatch好了,我们目前来编写客户程序创立组件的实例并设置和获取属性一般我们编写CBaby C++程序调用时需要导入组件文献或者文献不过由于接口提供了统一的方com dlltlb IDispatch式访问,因此我们可以不必非要这些文献目前我们创立程序Win32Console我们需要类的才可以创立该对象,我们可以通过来获取对应的CBaby CLSIDProgID CLSID,是一种友好的名称,用来标志一种组件实现类一般创立组件后,我们可以在工ProgID ATL程中找到组件名脚本文献,如下面的文献.RGS baby.rgs的缩写HKCR//HKEY_CLASSES_ROOT代表;代表;代表IDspCOM.Baby.1=s BabyClass7/s REG_SZ dREG.DWORD bREG_BINARYCLSID=s479278E86-6551-40EB-9BB0-25655A1EE60D}1IDspCOM.Baby=s BabyClass,CLSID=s479278E86-6551-40EB-9BB0-25655A1EE60D}1delete this;return0;}return m_RefCount;由于每一种对象都支持接口,因此可以通过来问询对象与否支持IUnknown Querylnterface您感爱好的其他接口接口通过接口来标识IDHRESULT IUnknown::QuerylnterfaceREFIID riidLPVOID FAR5*ppv{ifriid==IID_IUnknown||riid==IIDJDropTarget*ppv=LPVOIDthis;AddRef;Return S_OK;}else{*ppv=NULL;return ENOINTERFACE;用于唯一地辨别中条目的标识符是一种被称为全局唯一标识符或COM GUIDUUID通用唯一标识符Typedef struct_GUIDunsigned longDatal;unsigned shortData2;unsigned shortData3;unsigned charData
[8];}GUID;的取值范围非常大,个字节也许形成的不一样组合为GUID
163.4X10A38c在中传播格式化数据的工作是通过数据对象处理的,数据对象是支持COM IDataObject接口的对象接口支持如下措施IDataObjectI DataObject::GetDataIDataObject::GetDataHere注销组件时不能删除关键字NoRemove CLSID//CLSID〃写该键ForceRemove{79278E86-6551-40EB-9BB0-25655A1EE60D}=s BabyClass,时应当删除目前键和所有子健有版本号的Prog ID=s IDspCOM.Baby.1//Prog ID无版本号的VersionlndependentProglD=s IDspCOM.Baby//ProgID ForceRemoveProgrammablelnprocServer32=s^MODULE%{val ThreadingModel=s Both}val ApplD=s,%APPID%^,TypeLib=s,{5B0732AF-E621-4E5A-A3EE-7F543CFB6701},}程序可以代码如下使用编码定义控制台应用程WIN32coNSOLE Unicode//IDspTest.cpp:序的入口点//#include stdafx.hinclude iostream#include atlstr.h includestring usingnamespace std;int_tmainint argc,_TCHAR*argv[]::ColnitializeNULL;〃创立对象IDspCOM.BabyCLSID Clsid;::CLSIDFromProglDL,,IDspCOM.Baby,,,Clsid;IDispatch*plDsp=NULL;HRESULThr=::CoCreatelnstanceClsid NULL,CLSCTX_ALL,IIDJDispatch,void**plDsp;5ifFAILEDhrcout«nFailed ToCreate IDspCOM.Babyj ObjectH«endl;::Collninitialize;return0;//检查对象与否支持类型信息IDspCOM.BabyUINT Count;plDsp-GetTypelnfoCountCount;ifCount==0cout«IDspCOM.Babyj Objecthas notTypelnfon«endl;return0;〃获取属性IDDISPID PropertylD;〃可以在这里作为使用的前提是我们必须保证BSTR PropName
[1];BSTR OLECHAR*字符串中不内嵌字符BSTR NULL;PropName
[0]=SysAllocStringL”Gender”hr=plDsp-GetlDsOfNamesIID_NULL,PropName,1,LOCALE_SYSTEM_DEFAULT PropertylD;5SysFreeStringPropName
[0];〃设置属性值为男GenderDISPPARAMS Params;Params.cArgs=1;必须,原因不明Params.cNamedArgs=1;//〃必须,原因不明DISPID dispidPut=DISPID_PROPERTYPUT;须,原因不明Params.rgdispidNamedArgs=dispidPut;/MParams.rgvarg=new VARIANTARG
[1];Params.rgvarg[O].vt=VT_BSTR;男Params.rgvarg
[0].bstrVal=SysAllocStringUCComVariant Result;EXCEPINFO Info;UINT ArgErr;hr=plDsp-lnvokePropertylDJID_NULL,GetUserDefaultLCID,DISPATCH_PROPERTYPUT,Par ams,Result lnfo,ArgErr;5VariantClearParams.rgvarg;delete Params.rgvarg;〃获取属性值GenderDISPPARAMS dispparamsNoArgs={NULL,NULL,0,0};hr=plDsp-lnvokePropertylDJID_NULL,GetUserDefaultLCID,DISPATCH_PROPERTYGET dis5pparamsNoArgs,Result,lnfo,ArgErr;USES_CONVERSION;cout«W2AResult.bstrVal«endl;〃释放接口plDsp-Release;::CoUninitialize;return0;}采用智能指针类调用接口的措施ATL IDispatch采用原则措施调用非常繁琐,并且轻易出错,为了简化这些过程,类库中提IDispatch ATL
7.1供了一种智能指针类类定义如下CComDispatchDriver typedefCComQIPtrIDispatch,—uuidofIDispatch CComDispatchDriver;实质上它是类的特化版本源代码如下CComQIPtr//specialization forIDispatchtemplate classCComPtrIDispatch:public CComPtrBaseIDispatchpublic:CComPtr throwCComPtrIDispatch*Ip throw:CComPtrBaseIDispatchlpCComPtrconst CComPtrIDispatchIp throw:CComPtrBaseIDispatchlp.pIDispatch*operator=IDispatch*Ip throwreturn static_castIDispatch*AtlComPtrAssignIUnknown**p,Ip;IDispatch*operator=const COomPtrIDispatchIp throwreturnstatic_castIDispatch*AtlComPtrAssignIUnknown**p,Ip.p;}//IDispatch specificstuff乙HRESULT GetPropertyByNameLPCOLESTRIps VARIANT*pVar throwATLASSERTp;ATLASSERTpVar;DISPIDdwDispID;HRESULT hr=GetlDOfNamelpsz,dwDisplD;if SUCCEEDEDhrhr=GetPropertydwDisplD,pVar;return hr;HRESULT GetPropertyDISPID dwDispID,VARIANT*pVar throwreturnGetPropertyp,dwDispID,pVar;HRESULT PutPropertyByNameLPCOLESTRIpsz,VARIANT*pVar throwATLASSERTp;ATLASSERTpVar;DISPIDdwDispID;HRESULT hr=GetlDOfNamelpsz,dwDisplD;if SUCCEEDEDhrhr=PutPropertydwDisplD,pVar;return hr;HRESULT PutPropertyDISPID dwDispID,VARIANT*pVar throwreturnPutPropertyp,dwDispID,pVar;HRESULT GetlDOfNameLPCOLESTRIpsz,DISPID*pdispid throwreturn p-GetlDsOfNamesIID_NULL const_castLPOLESTR*lpsz,1,JLOCALE_USER_DEFAULT,pdispid;}//Invoke amethod byDISPID withno parametersHRESULTlnvokeODISPID dispid,VARIANT*pvarRet=NULL throwDISPPARAMS dispparams={NULL,NULL,0,0};return p-lnvokedispid,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_METHOD,dispparams,pvarRet,NULL,NULL;//Invoke amethod byname withno parametersHRESULTlnvokeOLPCOLESTR IpszName,VARIANT*pvarRet=NULL throwHRESULThr;DISPID dispid;hr=GetlDOfNamelpszName,dispid;if SUCCEEDEDhrhr=Invoke0dispid,pvarRet;return hr;//Invoke amethod byDISPID witha singleparameterHRESULT InvokelDISPID dispid,VARIANT*pvarParaml,VARIANT*pvarRet=NULLthrowDISPPARAMS dispparams={pvarParaml,NULL,1,0};return p-lnvokedispid,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_METHOD,dispparams,pvarRet,NULL,NULL;//Invoke amethod byname witha singleparameterHRESULT lnvoke1LPCOLESTR IpszName,VARIANT*pvarParaml,VARIANT*pvarRet=NULL throwHRESULThr;DISPIDdispid;hr=GetlDOfNamelpszName,dispid;if SUCCEEDEDhrhr=Invoke1dispid,pvarParaml,pvarRet;return hr;//Invoke amethod byDISPID withtwo parametersHRESULTlnvoke2DISPID dispid,VARIANT*pvarParaml,VARIANT*pvarParam2,VARIANT*pvarRet=NULL throw;//Invoke amethod byname withtwo parametersHRESULTlnvoke2LPCOLESTR IpszName,VARIANT*pvarParaml,VARIANT*pvarParam2,VARIANT*pvarRet=NULL throwHRESULThr;DISPID dispid;hr=GetlDOfNamelpszName,dispid;if SUCCEEDEDhrhr=Invoke2dispid,pvarParaml,pvarParam2,pvarRet;return hr;//Invoke amethod byDISPID withN parametersHRESULTlnvokeNDISPID dispid,VARIANT*pvarParams,int nParams,VARIANT*pvarRet=NULL throwDISPPARAMSdispparams={pvarParams,NULL,nParams,0};return p-lnvokedispid,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_METHOD,dispparams,pvarRet,NULL,NULL;//Invoke amethod byname withNparametersHRESULT lnvokeNLPCOLESTRIpszName,VARIANT*pvarParams,int nParams,VARIANT*pvarRet=NULL throwHRESULThr;DISPIDdispid;hr=GetlDOfNamelpszName,dispid;if SUCCEEDEDhrhr=lnvokeNdispid,pvarParams,nParams,pvarRet;return hr;static HRESULTPutPropertyIDispatch*p,DISPIDdwDispID,VARIANT*pVar throwATLASSERTp;ATLASSERTpVar!=NULL;if pVar==NULLreturn E_POINTER;ifp==NULLreturn EJNVALIDARG;ATLTRACEatlTraceCOM,2,_TCPropertyHelper::PutProperty/nn;DISPPARAMSdispparams={NULL,NULL,1,1};dispparams.rgvarg=pVar;DISPID dispidPut=DISPID_PROPERTYPUT;dispparams.rgdispidNamedArgs=dispidPut;if pVar-vt==VT_UNKNOWN||pVar-vt==VT_DISPATCH||pVar-vtVT_ARRAY||pVar-vtVT_BYREFHRESULT hr=p-lnvokedwDisplD,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_PROPERTYPUTREF,dispparams,NULL,NULL,NULL;if SUCCEEDEDhrreturn hr;return p-lnvokedwDisplD,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_PROPERTYPUT,dispparams,NULL,NULL,NULL;static HRESULTGetPropertyIDispatch*p,DISPIDdwDispID,VARIANT*pVar throwATLASSERTp;ATLASSERTpVar!=NULL;if pVar==NULLreturn E_POINTER;ifp==NULLreturn EJNVALIDARG;匚:;ATLTRACEatlTraceCOM,2,_TCPropertyHelpe GetProperty/n DISPPARAMSdispparamsNoArgs={NULL,NULL,0,0};returnp-lnvokedwDisplD,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_PROPERTYGET,dispparamsNoArgs,pVar,NULL,NULL;;下面我们的客户程序将使用该类完毕前一节的属性设置和获取功能int_tmainint argc,_TCHAR*argv[]{::ColnitializeNULL;CComDispatchDriver Dsp;Dsp.CoCreatelnstanceLIDspCOM.BabyH;女CComVariant ValueL”Dsp.PutPropertyByNameLnGendern,Value;CComVariant Value2;Dsp.GetPropertyByNameLHGender,Value2;::Collninitialize;return0;}使用类调用的措施COIeDispatchDriver IDispatch类是类库中用于操纵接口的类下面是该类的申明:COleDispatchDriver MFCIDispatch classCOIeDispatchDriver//Constructorspublic:COIeDispatchDriver;COIeDispatchDriverLPDISPATCH IpDispatch,BOOL bAutoRelease=TRUE;忙COIeDispatchDriverconst COIeDispahDriver dispatchSrc;//AttributesLPDISPATCH mJpDispatch;BOOL m_bAutoRelease;//OperationsBOOL CreateDispatchREFCLSIDclsid,COIeException*pError=NULL;BOOL CreateDispatchLPCTSTRIpszProgID,COIeException*pError=NULL;void AttachDispatchLPDISPATCHIpDispatch,BOOL bAutoRelease=TRUE;LPDISPATCH DetachDispatchQ;//detach andget ownershipof mJpDispatchvoid ReleaseDispatch;//helpers forIDispatch::lnvokevoid AFX_CDECL lnvokeHelperDISPIDdwDispID,WORD wFlags,VARTYPE vtRet,void*pvRet,const BYTE*pbParamlnfo,...;;void AFX_CDECL SetPropertyDISPIDdwDispID,VARTYPE vtProp,...voidGetPropertyDISPID dwDispID,VARTYPE vtProp,void*pvProp const;//special operatorsoperatorLPDISPATCH;const COIeDispatchDriveroperator=const COIeDispatchDriverdispatchSrc;//Implementationpublic:^COIeDispatchDriver;void lnvokeHelperVDISPIDdwDispID,WORD wFlags,VARTYPE vtRet,void*pvRet,constBYTE*pbParamlnfo,vajist argList;;先创立程序,选择支持然后编写如下代码Win32Console MFC,::ColnitializeNULL;COIeDispatchDriver Dsp;if!Dsp.CreateDispatchLnIDspCOM.BabyH创立失败coutvvL”vvendl;}DISPID PropID;可以在这里作为使用的前提是我们必须保证BSTR PropName
[1];//BSTR OLECHAR*字符串中不内嵌字符BSTR NULLPropName
[0]=SysAllocStringLnGender;Dsp.mJpDispatch-GetlDsOfNamesIID_NULL PropName,1,LOCALE_SYSTEM_DEFAULT,ProplD;男Dsp.SetPropertyProplD,VT_BSTR,LCString Value;Dsp.GetPropertyProplD,VT_BSTR,Value;Dsp.ReleaseDispatch;::Collninitialize;很明显,该类使用比类要繁琐的多,因此不推荐使用不过CComDispatchDriver MapObjects中的类采用派生自该类,同步为了便于使用增长了诸多组员函数的技术C++IDataObject::QueryGetDataIDataObject::GetCanonicalFormatEtc IDataObject::SetDataIDataObject::EnumFormatEtc IDataObject::DAdviseIDataObject::DUnadviseIDataObject::EnumDAdvise决定了设计方案以及接口中需要包括的措施和参数后,必须使用接口描述语言编写接口的/OL抽象定义编写好文献后,使用和自带得编译器编译,.IDL VC++PlatformSDK MicrosoftIDL并生成头文献、勇于构建调度借口调用的代理和占为程序的代码以及实现开发工具和调用接口所必需的类型库自动化,通过它对象可以将其功能提供应解释型客户如脚本编写语言而不是编译型客COM户使用在开发阶段,当接口客户被编译时,编译器读取源代码,通过查阅头文献或类COM型库中的接口定义将措施名称解析为条目,并生成目的代码,以便将必须的参数压入堆I/7BL栈并跳到接口中对应条目保留的地址编译过程也许需要很长时间,但编译后运行二进V78L制代码的速度相对较快而解释型客户在真正执行之前,不会将源代码解析为机器代码自动化对象通过一种叫做的原则接口暴露其所有的功能,而不是将每项功能作为自定义IDispatch接口的中的条目来暴露对对象的任何内部措施的调用都是通过该接口进行处理的V7BL接口的措施包括IDispatchIDispatch::lnvoke当客IDispatch::GetlDsOf NamesIDispatch::GetTypelnfo IDispatch::GetTypelnfoCount户想调用自动化对象的内部措施时,它调用对象的客户可使用IDispatch::lnvoke获得想要做事情的IDispatch::GetlDsOfNames ID类型库是对象厂商提供的静态数据构造的集合,通过接口进行访问,包具有关单个对ItypeLib象、接口或类的信息类型库可以包括描述下述内容的信息服务器支持的对象类型•每个对象措施及其参数和类型•每个对象属性及其类型•枚举常量值•到在线文档中特定条目的引用COM LanguageRequirementsTheonly languagerequirement forCOM isthat codeis generatedin alanguage thatcancreatestructures ofpointers and,either explicitlyor implicitly,callfunctions throughpointers.Object-oriented languagessuch asC++andSmalltalk provideprogramming mechanismsthatsimplify theimplementation ofCOMobjects,but languagessuch asC,Pascal,Ada,Java,andeven BASICprogrammingenvironments cancreate anduse COMobjects.以上摘自重要的是都可以用来编写MSDN,C,Pascal,Ada,Java,and evenBASIC COMo以上初步简介了有关的某些东西,不多也很浅,理解以上的部分就够花费很长一段时间COM的了,假如真地对感爱好的话,最佳有一定的基础,包括数据构造,面向对象的程序COM设计,编程等,不过也不一定都学,只是这些会对你学习并在短时间内掌握并Windows COM深入理解会有相称的好处COMCOM学习笔记二CoCreatelnstance详细内部实现[cpp]view plaincopy
1.CoCreateInstance....
2.{
4.IClassFactory*pClassFactory=NULL;
5.CoGetClassObjectCLSID_Object,CLSCTX_INPROC_SERVER,NULL,IID_IClassFactory,void**pClassFactory;
6.pClassFactory-CreateInstanceNULL工工DJUnknown,void**pUnk;J
7.pClassFactory-Release;
2.{
3.〃通过查注册表CLS工D_Object,得知组件DLL的位置、文献名
4.〃装入DLL库
5.〃使用函数GetProcAddress...得到DLL库中函数DllGetClassObject的函数指针
6.〃调用DllGetClassObject
7.}
8.Ill DllGetClassObject是干什么的,它是用来获得类厂对象的只有先得到类厂才能去创立组件.
9.Ill下面是DllGetClassObject的伪码
10.DllGetClassObject...
11.
13.CFactory*pFactory=new CFactory;〃类厂对象
14.pFactory-QueryInterfaceIID_IClassFactoryvoid**pClassFactory;
15.//查询IClassFactory指针
16.pFactory-Release;
18.}
19.Ill CoGetClassObject的流程已经到此为止,目前返回CoCreate工nstance,看看Createlnstance的伪码
21.{
23.CObject*pObject=new CObject;〃组件对象
24.pObject-QueryInterfaceIID_IUnknown,void**pUnk;
25.pObject-Release;
27.这部分我们将构造一种创立组件的最小框架构造,然后看一看其内部处理流程是怎样的COM[cpp]view plaincopy
1.IUnknown*pUnk=NULL;
2.lObject*pObject=NULL;
3.CoInitializeNULL;
4.CoCreateInstanceCLSID_Object,CLSCTX_INPROC_SERVER,NULL,IID_IUnknown,void**pllnk;
5.pUnk-QueryInterfaceIID_IOjbect void**pObject;
6.pUnk-Release;
7.pObject-Func;
8.pObject-Release;
9.CoUninitialize;cpp]view plaincopy
1.CoCreateInstance....
2.{
3.
4.IClassFactory*pClassFactory=NULL;卜CoGetClassObjectCLSID_Object,CLSCTX」NPROJSERVER,NULL,IID_IClassFactory,void**pClassFactory;
6.pClassFactory-CreateInstanceNULL1ID_工Unknown,void**pUnk;J
7.pClassFactory-Release;
8.9・}这就是一种经典的创立组件的框架,不过我的爱好在身上,让COM CoCreatelnstance我们来看看它内部做了某些什么事情如下是它内部实现的一种伪代码[cpp]view plaincopy
2.{
3.〃通过查注册表CLS3D_Object,得知组件DLL的位置、文献名
4.〃装入DLL库
5.//使用函数GetProcAddress.・.得至lj DLL库中函数DllGetClassObject的函数指针
6.//调用DllGetClassObject
7.
8.DllGetClassObject是干什么的,它是用来获得类厂对象的只有先得到类厂才能去创立组件.
9.下面是DllGetClassObject的伪码
10.DllGetClassObject...
11.
13.CFactory*pFactory=new CFactory;〃类厂对象
14.pFactory-QueryInterfaceIID_IClassFactoryvoid**pClassFactory;
15.〃查询IClassFactory指针
16.pFactory-Release;
18.}
19.CoGetClassObject的流程已经到此为止,目前返回CoCreatelnstance,看看Createlnstance的伪码
21.
23.CObject*pObject=new CObject;〃组件对象
24.pObject-QueryInterfaceIID_IUnknown void**pUnk;J
25..pObject-Release;
27.这段话的意思就是先得到类厂对象,再通过类厂创立组件从而得到指针IUnknown继续深入一步,看看的内部伪码:CoGetClassObject CoCr«4tcnktdrict-CcCetCIJSsCbject-CoLujdLibrary•corpur.er t.dll-.TW£LhVoluIc-LoodLfbrjr/*coipcrsent.dll*OIIMalr-GelProcAddre,*DllCelCl«siOlJecf L-D11Get1«sCbject DHGetCldssObjeetLpfacicryQuerlr urface、・Lp ctoryMURM-pfactory CreateInstaree^Faetory-CreateErstanceL gbjeeXuerm terQcc・LpOtject”:pfactory Reledsepfeclory Reled$eCLIENT COMPONENT上图是从技术内幕中来的一种例图,从图中可以清晰的看到COM+COPY的整个流程CoCreatelnstance一种经典的自注册的所必有的四个函数7COM DLL用于获得类厂指针DIIGetClassObject:注册某些必要的信息到注册表中DIIRegisterServer:匚卸载注册信息DllUnregisterServe系统空闲时会调用这个函数,以确定与否可以卸载DIICanUnloadNow:DLL尚有一种函数是这个函数在中并不规定一定要实现它,不过在生DLL DIIMain,COM VC成的组件中自动都包括了它,它的作用重要是得到一种全局的实例对象注册表在中的重要作用8COM首先要懂得的概念,中所有的类、接口、类型库都用来唯一标识,GUID COMGUID GUID是一种位的字串,根据特制算法生成的可以保证是全世界唯一的「组件的创128GUID COM立,查询接口都是通过注册表进行的有了注册表,应用程序就不需要懂得组件的文献DLL名、位置,只需要根据查就可以了当版本升级的时侯,只要改一下注册表信息就可CLSID以神不知鬼不觉的转到新版本的DLLoCOM学习笔记三IUnknown接口-接口定义任何一种接口都是继承于接口IUnknown客户同组件的交互都是通过一种接口完毕的在客户查询组件的其他接口时,也是通过接口完毕的这个接口就是lunkown,它的定义包括在Win32SDK中的UNKOWN.h头文献中[cpp]view plaincopy
1.interface lUnkown
2.
3.
4.
5.virtual HRESULT_stdcall QuerylnterfaceconstIIDiid,void**ppv=0;
7.virtual ULONG_stdcall AddRef=0;
8.
9.virtual ULONG_stdcall Release=0;
10.|
11.}JCAVtable客户IX,,,pixl------►V abe Ponter---------►Queryinterface——►QuerylnterlhceAddRefAddRefReleaseReleasefx图继承接口的接口的1lunkown COMvTable
2.获取lUnkown指针有一种叫Createlnstance的函数,它可以建立一种组件并返回一种lUkown指针:lUnkown*Createlnstance;在此,客户不必再使用new操作符
3.Queryinterface函数。
个人认证
优秀文档
获得点赞 0