




已阅读5页,还剩24页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
COM学习笔记(一)初识COMCOM-ComponentObject Model,组件对象模型。一向以难学著称,有人曾说过这样的话:世界上只有两个程序员真正理解COM,他们都在微软工作。这句话虽然有点过,但基本上说出了COM确实有些难理解。不过,不用担心,本文并不探求多深多高的技术领域,而是带领大家浏览一下我们的COM,就像本文的题目一样:初识COM。首先,我们先来了解一下有关COM的概念:COM(Component Object Model,组件对象模型)是微软公司的最高级的、包罗万象的二进制通信规范,用于软件组件间跨越多个进程、机器、硬件和操作系统进行互操作。下面我们来看看COM的一些特点:在COM中,应用程序不是通过诸如ShowWindow()的API函数进行操纵。程序是由对象组成的,对象向外提供一个或多个接口。接口是一组相关的函数,函数操作他们所属的对象。不能直接访问对象中的数据,而只能通过对象的接口函数访问。学过C+和数据结构的人应该对上述说法并不陌生。在COM中,没有指向对象的指针这种东西,有的只是指向对象接口的指针。实际上,是指向另一个指针的指针。第二个指针指向一个指针表,表中的指针指向接口成员函数。该指针表称为VTBL。将指针指向对象后,就可以通过调用接口中的成员函数与该对象通信。如何将指针指向第一个对象呢?可以调用一个返回指向对象指针的COM函数如:CoCreateInstace()。COM对象都提供一个叫IUnknown的接口,该接口包含方法AddRef()、Release()和QueryInterface()。每个接口都是从IUnknown接口派生出来的。前两个方法操纵一个控制对象使用期限的内部引用计数。当对象第一次被创建时,创建者必须调用该对象的AddRef(),将计数加1。每当其他的用户将一个指针指向该对象时,必须再次调用该对象的AddRef()方法。当用户不再使用对象时,它调用对象的Release()方法,将引用计数减1。当最后一个用户调用对象的Release()方法后,计数值变为0,导致对象释放自己。下面是AddRef()和Release()方法的简单实现:ULONG IUnknown:AddRef(void) m_RefCount+; return m_RefCount;ULONG IUnknown:Release(void) m_RefCount-; if(m_RefCount=0) delete this; return 0; return m_RefCount; 由于每一个对象都支持IUnknown接口,因此可以通过QueryInterface()来询问对象是否支持您感兴趣的其它接口。接口通过接口ID来标识。HRESULT IUnknown:QueryInterface(REFIID riid,LPVOID FAR *ppv) if(riid=IID_IUnknown|riid=IID_IDropTarget)*ppv=(LPVOID)this; AddRef(); Return S_OK; else *ppv=NULL; return E_NOINTERFACE;用于唯一地区分COM中条目的标识符是一个被称为GUID(全局唯一标识符)或UUID(通用唯一标识符)。Typedef struct_GUID unsigned long Data1; unsigned short Data2; unsigned short Data3; unsigned char Data8;GUID;GUID的取值范围非常大,16个字节可能形成的不同组合为3.41038。在COM中传输格式化数据的工作是通过数据对象处理的,数据对象是支持IDataObject接口的对象。IDataObject接口支持以下方法:IDataObject:GetDataIDataObject:GetDataHereIDataObject:QueryGetDataIDataObject:GetCanonicalFormatEtcIDataObject:SetDataIDataObject:EnumFormatEtcIDataObject:DAdviseIDataObject:DUnadviseIDataObject:EnumDAdvise决定了设计方案以及接口中需要包括的方法和参数后,必须使用接口描述语言(IDL)编写接口的抽象定义。编写好.IDL文件后,使用VC+和PlatformSDK自带得MicrosoftIDL编译器编译,并生成头文件、勇于构建调度借口调用的代理和占为程序的代码以及实现开发工具和调用接口所必需的类型库。自动化,通过它COM对象可以将其功能提供给解释型客户(如脚本编写语言)而不是编译型客户使用。在开发阶段,当COM接口客户被编译时,编译器读取源代码,通过查阅头文件或类型库中的接口定义将方法名称解析为VTBL条目,并生成目标代码,以便将必须的参数压入堆栈并跳到接口VTBL中相应条目保存的地址。编译过程可能需要很长时间,但编译后运行二进制代码的速度相对较快。而解释型客户在真正执行之前,不会将源代码解析为机器代码。自动化对象通过一个叫做IDispatch的标准接口暴露其所有的功能,而不是将每项功能作为自定义接口的VTBL中的条目来暴露。对对象的任何内部方法的调用都是通过该接口进行处理的。IDispatch接口的方法包括:IDispatch:InvokeIDispatch:GetIDsOfNamesIDispatch:GetTypeInfoIDispatch:GetTypeInfoCount当客户想调用自动化对象的内部方法时,它调用对象的IDispatch:Invoke()。客户可使用IDispatch:GetIDsOfNames获得想要做事情的ID。类型库是对象厂商提供的静态数据结构的集合,通过ItypeLib接口进行访问,包含关于单个对象、接口或类的信息。类型库可以包含描述下述内容的信息:服务器支持的对象类型 每个对象方法及其参数和类型 每个对象属性及其类型 枚举常量值 到在线文档中特定条目的引用COM Language RequirementsTheonly language requirement for COM is that code is generated in a language thatcan create structures of pointers and, either explicitly or implicitly, callfunctions through pointers. Object-oriented languages such as C+ andSmalltalk? provide programming mechanisms that simplify the implementation ofCOM objects, but languages such as C, Pascal, Ada,Java, and even BASICprogramming environments can create and use COM objects.以上摘自MSDN,重要的是C, Pascal, Ada, Java,and even BASIC都可以用来编写COM。以上初步介绍了关于COM的一些东西,不多也很浅,理解以上的部分就够花费很长一段时间的了,如果真地对COM感兴趣的话,最好有一定的基础,包括:数据结构,面向对象的程序设计,Windows编程等,不过也不一定都学,只是这些会对你学习COM并在短时间内掌握并深入理解COM会有相当的好处。COM学习笔记(二)CoCreateInstance具体内部实现cppview plaincopy1. CoCreateInstance(.)2. 3. /.4. IClassFactory*pClassFactory=NULL;5. CoGetClassObject(CLSID_Object,CLSCTX_INPROC_SERVER,NULL,IID_IClassFactory,(void*)&pClassFactory);6. pClassFactory-CreateInstance(NULL,IID_IUnknown,(void*)&pUnk);7. pClassFactory-Release();8. /.9. 这段话的意思就是先得到类厂对象,再通过类厂创建组件从而得到IUnknown指针。继续深入一步,看看CoGetClassObject的内部伪码:cppview plaincopy1. CoGetClassObject(.)2. 3. /通过查注册表CLSID_Object,得知组件DLL的位置、文件名4. /装入DLL库5. /使用函数GetProcAddress(.)得到DLL库中函数DllGetClassObject的函数指针。6. /调用DllGetClassObject7. 8. /DllGetClassObject是干什么的,它是用来获得类厂对象的。只有先得到类厂才能去创建组件.9. /下面是DllGetClassObject的伪码:10. DllGetClassObject(.)11. 12. /.13. CFactory*pFactory=newCFactory;/类厂对象14. pFactory-QueryInterface(IID_IClassFactory,(void*)&pClassFactory);15. /查询IClassFactory指针16. pFactory-Release();17. /.18. 19. /CoGetClassObject的流程已经到此为止,现在返回CoCreateInstance,看看CreateInstance的伪码:20. CFactory:CreateInstance(.)21. 22. /.23. CObject*pObject=newCObject;/组件对象24. pObject-QueryInterface(IID_IUnknown,(void*)&pUnk);25. pObject-Release();26. /.27. 这部分我们将构造一个创建COM组件的最小框架结构,然后看一看其内部处理流程是怎样的cppview plaincopy1. IUnknown*pUnk=NULL;2. IObject*pObject=NULL;3. CoInitialize(NULL);4. CoCreateInstance(CLSID_Object,CLSCTX_INPROC_SERVER,NULL,IID_IUnknown,(void*)&pUnk);5. pUnk-QueryInterface(IID_IOjbect,(void*)&pObject);6. pUnk-Release();7. pObject-Func();8. pObject-Release();9. CoUninitialize();cppview plaincopy1. CoCreateInstance(.)2. 3. .4. IClassFactory*pClassFactory=NULL;5. CoGetClassObject(CLSID_Object,CLSCTX_INPROC_SERVER,NULL,IID_IClassFactory,(void*)&pClassFactory);6. pClassFactory-CreateInstance(NULL,IID_IUnknown,(void*)&pUnk);7. pClassFactory-Release();8. .9. 这就是一个典型的创建COM组件的框架,不过我的兴趣在CoCreateInstance身上,让我们来看看它内部做了一些什么事情。以下是它内部实现的一个伪代码:cppview plaincopy1. CoGetClassObject(.)2. 3. /通过查注册表CLSID_Object,得知组件DLL的位置、文件名4. /装入DLL库5. /使用函数GetProcAddress(.)得到DLL库中函数DllGetClassObject的函数指针。6. /调用DllGetClassObject7. 8. DllGetClassObject是干什么的,它是用来获得类厂对象的。只有先得到类厂才能去创建组件.9. 下面是DllGetClassObject的伪码:10. DllGetClassObject(.)11. 12. .13. CFactory*pFactory=newCFactory;/类厂对象14. pFactory-QueryInterface(IID_IClassFactory,(void*)&pClassFactory);15. /查询IClassFactory指针16. pFactory-Release();17. .18. 19. CoGetClassObject的流程已经到此为止,现在返回CoCreateInstance,看看CreateInstance的伪码:20. CFactory:CreateInstance(.)21. 22. .23. CObject*pObject=newCObject;/组件对象24. pObject-QueryInterface(IID_IUnknown,(void*)&pUnk);25. pObject-Release();26. .27. 这段话的意思就是先得到类厂对象,再通过类厂创建组件从而得到IUnknown指针。继续深入一步,看看CoGetClassObject的内部伪码:上图是从COM+技术内幕中COPY来的一个例图,从图中可以清楚的看到CoCreateInstance的整个流程。(7) 一个典型的自注册的COM DLL所必有的四个函数DllGetClassObject:用于获得类厂指针DllRegisterServer:注册一些必要的信息到注册表中DllUnregisterServer:卸载注册信息DllCanUnloadNow:系统空闲时会调用这个函数,以确定是否可以卸载DLLDLL还有一个函数是DllMain,这个函数在COM中并不要求一定要实现它,但是在VC生成的组件中自动都包含了它,它的作用主要是得到一个全局的实例对象。(8) 注册表在COM中的重要作用首先要知道GUID的概念,COM中所有的类、接口、类型库都用GUID来唯一标识,GUID是一个128位的字串,根据特制算法生成的GUID可以保证是全世界唯一的。 COM组件的创建,查询接口都是通过注册表进行的。有了注册表,应用程序就不需要知道组件的DLL文件名、位置,只需要根据CLSID查就可以了。当版本升级的时侯,只要改一下注册表信息就可以神不知鬼不觉的转到新版本的DLL。COM学习笔记(三)IUnknown接口一: 接口定义 任何一个接口都是继承于IUnknown接口。 客户同组件的交互都是通过一个接口完成的。在客户查询组件的其他接口时,也是通过接口完成的。这个接口就是Iunkown,它的定义包含在Win32 SDK中的UNKOWN.h头文件中。cppview plaincopy1. interfaceIUnkown2. 3. 4. 5. virtualHRESULT_stdcallQueryInterface(constIID&iid,void*ppv)=0;6. 7. virtualULONG_stdcallAddRef()=0;8. 9. virtualULONG_stdcallRelease()=0;10. 11. ;2.获取IUnkown指针 有一个叫CreateInstance的函数,它可以建立一个组件并返回一个IUkown指针: IUnkown* CreateInstance(); 在此,客户不必再使用new操作符。3.QueryInterface函数客户可使用IUnkown中的成员函数QueryInterface查询组件是否支持某个特定的接口,如果支持,将返回一个指向此接口的指针;如果不支持,则返回一个错误代码。 HRESULT _stdcall QueryInterface(const IID& iid, void* ppv); 其中,第一个参数iid标志客户所需的接口,第二个参数ppv存放所请求接口的指针地址。4. 使用QueryInterfacecppview plaincopy1. IUnkown*pI;2. /defineapointerfortheinterface3. IX*pIX=NULL;4. /askforinterfaceIX5. HRESULThr=pI-QueryInterface(IID.IX,(void*)&pIX);6. /checkreturnvalue7. if(SUCCEEDED(hr)8. 9. /useinterface10. pIX-fx();11. 在这段代码中,我们查询了pI是否支持由IID.IX所标识的接口。若成功返回,那么我们就可以使用它返回的指针了。5.QueryInterface的实现 QueryInterface需要根据某个给定的IID返回指向相应接口的指针。如果组件支持客户指定的接口,那么返回S_OK及相应的指针,否则,返回E_NOINTERFACE并将相应的指针返回值设为NULL。例如我们有类CA,及其接口的继承关系:cppview plaincopy1. HRESULT_stdcallCA:QueryInterface(constIID&iid,void*ppv)2. 3. if(iid=IID.IUnkown)4. 5. 6. /theclientwantstheIUnkowninterface7. *ppv=static_cast(this);8. 9. elseif(iid=IDD.IX)10. 11. /theclientwantstheIXinterface12. *ppv=static_cast(this);13. 14. elseif(iid=IDD.IY)15. 16. /theclientwantstheIYinterface17. *ppv=static_cast(this);18. 19. else20. 21. /wedontsupporttheinterfacetheclientwants.22. /besuretosettheresultingpointertoNULL23. *ppv=NULL;24. returnE_NOINTERFACE;25. 26. static_cast(*ppv)-AddRef();27. returnS_OK;28. 在这里,QueryInterface使用一个简单的if-then-else语句实现。当然用其他任何一种可以将一种类型映射成另外一种类型的结构也是可以实现的。如可用数组、哈希表或树来实现。6.QueryInterface的实现规则(1) QueryInterface必须总是返回同一IUnkown指针可以通过查询两个接口的IUnkown接口,并比较其返回值来确定这两个接口是否指向同一组件。(2) 若客户曾经获取过某个接口,那么它将总能获取此接口(3) 客户可以再次获取已经拥有的接口(4) 客户可以返回到起始接口(5) 若能够从某个接口获取某特定接口,那么可以从任意接口都将可以获取此接口COM学习笔记(四)IDispatch接口原理与应用目录:IDispatch接口原理与应用目录:前言:IDispatch接口的定义:创建支持IDispatch接口的COM对象:标准方式调用IDispatch接口的方法:采用ATL智能指针类调用IDispatch接口的方法:使用类COleDispatchDriver调用IDispatch的方法:前言:尽管com接口是跨语言的,但是很多语言在使用com时更多地通过Automation技术来和com对象通信。IDispatch接口是Automation的核心技术。尽管c+程序员并不喜欢甚至讨厌使用IDispatch接口,因为调用它实在是非常的麻烦而且易出错。但是不可否认大量的现存组件是只基于IDispatch接口技术而开发的,有时候你没有选择,而且如果你想要写一些组件能够在web上运行,你也离不开IDisptch接口,因为VBScript这样的脚本语言不会聪明到能够理解你的基于虚函数表的普通com接口。与其躲避它,不如征服它。本文中,我将结合自己的经验和读者一起探讨IDispatch接口的各种应用。并介绍几种能够加快我们使用IDispatch接口的c+类。IDispatch接口的定义:参照文件oaidl.h中的定义-MIDL_INTERFACE(00020400-0000-0000-C000-000000000046)IDispatch :publicIUnknownpublic:virtualHRESULT STDMETHODCALLTYPE GetTypeInfoCount(/* out */UINT *pctinfo) = 0;virtualHRESULT STDMETHODCALLTYPE GetTypeInfo(/* in */UINT iTInfo,/* in */LCID lcid,/* out */ITypeInfo *ppTInfo) = 0;virtualHRESULT STDMETHODCALLTYPE GetIDsOfNames(/* in */REFIID riid,/* size_isin */LPOLESTR *rgszNames,/* in */UINT cNames,/* in */LCID lcid,/* size_isout */DISPID *rgDispId) = 0;virtual/* local */HRESULT STDMETHODCALLTYPE Invoke(/* in */DISPID dispIdMember,/* in */REFIID riid,/* in */LCID lcid,/* in */WORD wFlags,/* outin */DISPPARAMS *pDispParams,/* out */VARIANT *pVarResult,/* out */EXCEPINFO *pExcepInfo,/* out */UINT *puArgErr) = 0;我们通过IDispatch的GUID到注册表中搜索,可以搜索到如下结果:注意在IDispatch接口GUID下面还有两个展开的GUID项,他们分别是ITypeInfo和ITypeLib接口。这两个接口在自动化应用中也是非常重要的。今后我们会经常看到他们。IDispatch接口方法简介:1)HRESULT STDMETHODCALLTYPE GetTypeInfoCount(/* out */UINT *pctinfo) ;判断实现了IDispatch接口的对象是否支持类型信息,如果返回1则支持,返回0则不支持。2)HRESULT STDMETHODCALLTYPE GetTypeInfo(/* in */UINT iTInfo,/* in */LCID lcid,/* out */ITypeInfo *ppTInfo) = 0;获取对象的类型信息接口指针,该方法调用之前总应该先调用方法GetTypeInfoCount()确认是否支持类型信息。参数iTInfo必须为0,否则该方法将返回DISP_E_BADINDEX表示失败参数lcid传递类型信息的地域标志。IDispatch接口的方法和属性在不同的语言环境(地域标志)可以使用不同的名称,因而lcid不同可能会导致返回的ITypeInfo接口指针不同。如果我们创建的组件根据不同的地域标志对属性和方法起不同的名字,我们就要使用这个参数,否则可以忽略。3)HRESULT STDMETHODCALLTYPE GetIDsOfNames(/* in */REFIID riid,/* size_isin */LPOLESTR *rgszNames,/* in */UINT cNames,/* in */LCID lcid,/* size_isout */DISPID *rgDispId)IDispatch接口的属性实质上是方法,方法也就是成员函数,IDispatch接口把所有成员函数的入口地址放入到一个数组中,并且内部组织了一个Map,将数组索引和方法名称一一映射。我们常见的DISPID就是这些方法在数组中的索引。如果我们想调用某一个方法,我们就需要DISPID来让我们找到该方法的地址。参数riid必须为NULL。参数rgszNames为字符串数组,第一个字符串为方法或者属性的名称,后续的字符串为参数名称,IDispatch接口的参数也可以有名字。参数cNames指定rgszNames数组中字符串的个数。参数lcid传递地域标志,同GetTypeInfo方法中的参数。参数rgDispId输出一个数组,每个数组成员对应rgszNames中的一个字符串名称。关于DISPID的进一步说明:typedefLONG DISPID;typedefDISPID MEMBERID;DISPID小于等于0的值都是有特殊意义的,如下面介绍的-/* DISPID reserved to indicate an unknown name */* only reserved for data members (properties); reused as a method dispid below */如果GetIDsOfNames函数找不到与名称相对应的DISPID,返回该值#defineDISPID_UNKNOWN( -1 )/* DISPID reserved for the value property */如果调用时不指定方法或者属性,则使用该缺省值#defineDISPID_VALUE( 0 )/* The following DISPID is reserved to indicate the param* that is the right-hand-side (or put value) of a PropertyPut*/表明属性设置函数中某一个参数将接受新属性值#defineDISPID_PROPERTYPUT( -3 )/* DISPID reserved for the standard NewEnum method */用于集合对象#defineDISPID_NEWENUM( -4 )/* DISPID reserved for the standard Evaluate method,脚本语言中可以用调用该方法 */#defineDISPID_EVALUATE( -5 )/* 表示某方法具有和构造函数相同的功能*/#defineDISPID_CONSTRUCTOR( -6 )/* 表示某方法具有和析构函数相同的功能*/#defineDISPID_DESTRUCTOR( -7 )/*TheCollectproperty. You use this property if the method you are calling through Invoke is an accessor function.*/#defineDISPID_COLLECT( -8 )/* The range -500 through -999 is reserved for Controls */* The range 0x80010000 through 0x8001FFFF is reserved for Controls */* The range -5000 through -5499 is reserved for ActiveX Accessability */* The range -2000 through -2499 is reserved for VB5 */* The range -3900 through -3999 is reserved for Forms */* The range -5500 through -5550 is reserved for Forms */* The remainder of the negative DISPIDs are reserved for future use */4) HRESULT STDMETHODCALLTYPE Invoke(/* in */DISPID dispIdMember,/* in */REFIID riid,/* in */LCID lcid,/* in */WORD wFlags,/* outin */DISPPARAMS *pDispParams,/* out */VARIANT *pVarResult,/* out */EXCEPINFO *pExcepInfo,/* out */UINT *puArgErr)参数dispIdMember为方法或者属性的DISPID,就是我们通过GetIDsOfNames获得的。参数riid必须为IID_NULL。参数lcid为地域标志,同前面两个方法。参数wFlags有下面若干值-ValueDescriptionDISPATCH_METHOD表示将调用方法。如果属性名称和方法名称相同,则和DISPATCH_PROPERTYGET标志一起设置。DISPATCH_PROPERTYGET获得属性DISPATCH_PROPERTYPUT设置属性DISPATCH_PROPERTYPUTREF通过引用设置属性参数pDispParams为参数信息数组,元素类型为DISPPARAMStypedefstructtagDISPPARAMS/* size_is */VARIANTARG *rgvarg;/参数数组/* size_is */DISPID *rgdispidNamedArgs;/参数中的DISPID数组UINT cArgs;/数组中的参数个数UINT cNamedArgs;/命名参数的个数DISPPARAMS;注意:如果是属性设置,rgvarg数组中只有一个参数,如果是方法调用,可以包含0到多个参数;rgvarg数组中的VARIANTARG参数的vt域为VT_BYREF时,该参数可写,否则为只读;rgvarg数组中的VARIANTARG参数的vt域为VT_BYREF时,该参数可以作为输出参数;rgvarg数组中的VARIANTARG参数的vt域不为VT_BYREF时,参数内的字符串或者指针变量的所有权在客户,客户必须自己释放资源,实现IDispatch接口的对象要想保留数据,则要拷贝数据或者调用指针变量的AddRef函数。rgvarg数组中的VARIANTARG参数的vt域为VT_ERROR,并且scode域为DISP_E_PARAMNOTFOUND时,该参数可以作为可选参数,scode域作用是存放返回的HRESULT。中曾经提到rgvarg数组中的参数存放顺序和客户程序调用时传递的参数顺序刚好相反,我这里对此表示怀疑。关于命名参数的详细讨论我在后面将谈到,现在只需要知道它可以不受参数次序的限制。参数pVarResult保存函数调用后的返回信息,因为Invoke已经将返回值用于COM通用的HRESULT;参数pExcepInfo返回异常信息;参数puArgErr包含第一个产生错误的参数索引,当Invoke返回的HRESULT值为DISP_E_TYPEMISMATCH或DISP_E_PARAM_NOTFOUND值时。创建支持IDispatch接口的COM对象:本节我们利用ATL7.1创建一个COM对象Baby。CBaby类从IDispatch接口派生,并且同时支持vtable方式。我们设想他有一个属性Gender (性别),我们可以设置和获取宝宝的性别属性现在我们先创建一个ATL项目IDspCOM。然后添加类CBaby,选择接口类型为双重。我们先在看一下生成的idl文件中的接口定义:object,uuid(22C1BD80-2937-42FB-A7F8-5CEBD1257CB8),dual,nonextensible,helpstring(IBaby 接口),pointer_default(unique)interfaceIBaby : IDispatch;现在IBaby派生自IDispatch接口,除了继承了IDispatch的4个方法和IUnknown的3个方法外,它现在还没有任何自己的方法和属性。ATL将帮我们实现前面的7个方法,我们无需关心。我们现在来创建自己的属性Gender。我们通过向导创建了该属性,idl文件如下:interfaceIBaby : IDispatchpropget,id(1),helpstring(属性 Gender) HRESULT Gender(out,retval BSTR* pVal);propput,id(1),helpstring(属性 Gender) HRESULT Gender(in BSTR newVal);我们可以看到其实属性就是一对方法-设置和获取属性方法。两个方法共用一个DISPID,值为1。在类的头文件中我们添加如下代码:public:STDMETHOD(get_Gender)(BSTR* pVal);STDMETHOD(put_Gender)(BSTR newVal);private:CComBSTR m_Gender;在类的实现文件中我
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 六一活动超市活动方案
- 六一游园活动活动方案
- 六一甜品diy活动方案
- 六一美德少年活动方案
- 六一读书活动方案
- 六一车位营销活动方案
- 六一雪糕接龙活动方案
- 六周年蛋糕活动方案
- 六年级学校美德活动方案
- 药学选拔考试试题及答案
- 2025年高考作文专练(25道真题+审题立意+范文)- 2025年高考语文作文备考总复习
- 2022年贵州省贵阳市(初三学业水平考试)中考生物试卷及答案
- 血管通路并发症窃血综合征
- 患者日常生活护理
- 药物化学智慧树知到答案2024年山西医科大学
- 《中华民族一家亲-同心共筑中国梦》队会课件
- TCAICC 001-2024 张家界莓茶质量等级评价
- 安徽省铜陵市义安区2023-2024学年七年级下学期期末生物题(无答案)
- 消防接警员应知应会考试题库大全-上(单选、多选题)
- 07J623-3 天窗挡风板及挡雨片
- 2024结肠锯齿状病变诊断及治疗进展
评论
0/150
提交评论