




已阅读5页,还剩17页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
内容 简介 定义集合对象 辅助函数 我们集合对象的函数 应用程序获得我们集合对象的方法 VBScript例程 C例程 IEnumVARIANT对象 另一个VBScript例程 另一个C例程 一个更通用的方法 添加、删除元素简介 有时我们可能需要维护一个元素列表。例如,我们可能设计了一个COM组件用于操作我们设计的PCI硬件板卡,并且用户可以在一台计算机上安装几个这种板卡,我们要让我们的组件能控制所有可用的板卡,允许应用程序获得每块板卡的信息,同时可单独访问每块板卡。 换句话说,当我们组件运行时,它需要查询系统中的板卡,然后把所有可用板卡的信息的列表给应用程序。为了这点,假设我们只需要每块可用板卡的“名字”,系统中第一块板卡名字可能是“Port 1”,第二块板卡名字是“Port 2”,等等。 因为我们事先不知道系统中到底有多少块板卡,所以最好的方法是创建一个结构,它们可以链接同类其它成员成一个链表。例如我们可以定义一个IENUMITEM结构来保存板卡信息:cppview plaincopyprint?1. typedefstruct_IENUMITEM2. /TolinkseveralIENUMITEMsintoalist.3. /把多个IENUMITEM链接到一个列表中4. struct_IENUMITEM*next;5. /Thisitemsvalue(ie,itsportname).6. /元素值(也就是它的端口名)7. char*value;8. IENUMITEM; 如果我们有三个端口,我们的IENUMITEM链表看起来可以是这样(我们通常应该通过GlobalAlloc分配IENUMITEM,但为了省事我在下面把它们申明为静态的并初始化它们):cppview plaincopyprint?1. IENUMITEMPort1=&Port2,Port1;2. IENUMITEMPort2=&Port3,Port2;3. IENUMITEMPort3=0,Port3; 在COM术语中,我们把一组相关元素叫做“集合(collection)”。所以上面三个元素的链表就是我们的集合。 但我们上面的IENUMITEM存在一个问题,它有一个char*成员,它不是自动化兼容类型。我们可以把它改为BSTR,这样它就是自动化兼容类型了。更好一点,我们可以放一个VARIANT在IENUMITEM中,这样做的优点是构造了一个通用的IENUMITEM(也就是说它可以存储各种自动化数据类型数据)。像我们稍后看到的,我们必须用VARIANT来返回元素的值给应用程序。因为我们总是要处理VARIANT类型数据,所以我们就把它定义为VARIANT存储在我们的IENUMITEM中。下面是IENUMITEM的新定义:cppview plaincopyprint?1. typedefstruct_IENUMITEM2. struct_IENUMITEM*next;3. VARIANT*value;4. IENUMITEM; 下面是我们如何分配一个实例并把它的值设置为“Port 1”(忽略错误检查):cppview plaincopyprint?1. IENUMITEM*enumItem;2. enumItem=(IENUMITEM*)GlobalAlloc(GMEM_FIXED,sizeof(IENUMITEM);3. enumItem-next=0;4. enumItem-value.vt=VT_BSTR;5. enumItem-value.bstrVal=SysAllocString(LPort1);定义集合对象 记得脚本语言没有指针概念。这些语言本身不可能遍历上面的列表,因为每个IENUMITEM的第一个成员是指向下一个IENUMITEM的指针。所以我们需要提供一个对象来帮助应用程序遍历这个列表,获取每个元素的值。 因为微软应Visual Basic程序员提出这个对象,所以他们选择了它是基于IDispatch的。换句话说,我们对象的虚表必须像所有COM对象那样以三个IUnknown函数(QueryInterface、AddRef和Release)开始,后面必须紧跟四个标准的IDispatch函数(GetTypeInfoCount、GetTypeInfo、GetIDsOfName和Invoke)。我们的对象还必须有三个函数。在我们IDL文件中,当我们定义我们要创建的这个对象的虚表(也就是接口)时,我们必须把这三个额外的函数命名为Count、Item和_NewEnum。在我们的这个对象的实际虚表中,我们可以使用其它的名字来命名这些指针(尽管我坚持使用了这几个名字)。为什么呢?因为永远不会有人直接调用它们,甚至永远不会有人知道我们会添加这三个函数到我们的虚表中。这些函数只能通过我们对象的Invoke函数来调用即使你使用像可以直接调用它们的C一类的语言,因为是微软的Visual Basic程序员设计它来提供给更多有影响的语言。为了让Visual Basic程序员可以使用它我们要付出极大的代价。此外在我们的IDL文件中,我们必须分配一个ID为DISPID_VALUE给Item函数,和DISPID_NEWENUM给_NewEnum函数。我们必须这么做。我们可以选择一个正数分配给Count函数做ID。似乎有点逻辑混乱?不是吗?记住它是Visual Basic程序员设计的。 为了在我们的IDL文件(也就是类型库)中使用,我们还需要给这个对象的虚表生成一个新的GUID。 让我们看一下这个对象的定义。我们可以用我们喜欢的名字给它命名。我选择用ICollection给它命名:cppview plaincopyprint?1. /我们的ICollection虚表的GUID2. /F69902B1-20A0-4e99-97ED-CD671AA87B5C3. DEFINE_GUID(IID_ICollection,0xf69902b1,0x20a0,0x4e99,0x97,4. 0xed,0xcd,0x67,0x1a,0xa8,0x7b,0x5c);5. /我们的ICollection的Vtable6. #undefINTERFACE7. #defineINTERFACEICollection8. DECLARE_INTERFACE_(INTERFACE,IDispatch)9. 10. /IUnkown函数11. STDMETHOD(QueryInterface)(THIS_REFIID,void*)PURE;12. STDMETHOD_(ULONG,AddRef)(THIS)PURE;13. STDMETHOD_(ULONG,Release)(THIS)PURE;14. 15. /IDispatch函数16. STDMETHOD_(ULONG,GetTypeInfoCount)(THIS_UINT*)PURE;17. STDMETHOD_(ULONG,GetTypeInfo)(THIS_UINT,LCID,ITypeInfo*)PURE;18. STDMETHOD_(ULONG,GetIDsOfNames)(THIS_REFIID,LPOLESTR*,UINT,LCID,DISPID*)PURE;19. STDMETHOD_(ULONG,Invoke)(THIS_DISPID,20. REFIID,LCID,WORD,DISPPARAMS*,21. VARIANT*,EXCEPINFO*,UINT*)PURE;22. /额外函数23. STDMETHOD(Count)(THIS_long*);24. STDMETHOD(Item)(THIS_long,VARIANT*);25. STDMETHOD(_NewEnum)(THIS_IUnknown*);26. ; 这看起来完全不陌生。首先,我运行GUIDGEN.EXE来创建另一个GUID。我把它粘在上面,然后给它取了一个IID_Icollection的变量名。这就是我们ICollection对象虚表的GUID。 接下来,我们定义我们ICollection对象的虚表。我们使用了我们在上一篇文章定义IExample2虚表中使用的同样的宏。就像早些时候提到的,它以三个IUnknown函数开始,紧接着是四个IDispatch函数,就像我们为IExample2添加脚本语言支持那样。这些函数完成像在IExamle2中各自函数的职责。 最后,我们添加三个额外的函数Count、Item和_NewEnum。稍后,我们会展示它们能做什么。 记住上面的宏会自动把我们的ICollection对象定义成这样:cppview plaincopyprint?1. typedefstruct2. ICollectionVtbl*lpVtbl;3. ICollection; 换句话说,它被定义为只有一个数据成员一个指向我们对象虚表的指针。当然这个成员被命名为lpVtbl。但我们需要添加一个额外的DWORD成员来做引用计数(就像我们在IExample2做的那样,因此定义一个MyRealIExample2来包含这些额外的数据成员)。因此,我们这样定义MyRealICollection:cppview plaincopyprint?1. typedefstruct2. ICollectionVtbl*lpVtbl;3. DWORDcount;4. MyRealICollection; 现在,让我们看看我们怎样在我们的IDL文件中定义这个VTable(接口):cppview plaincopyprint?1. uuid(F69902B1-20A0-4e99-97ED-CD671AA87B5C),oleautomation,object2. interfaceICollection:IDispatch3. 4. propget,id(1)5. HRESULTCount(out,retvallong*);6. propget,id(DISPID_VALUE)7. HRESULTItem(inlong,out,retvalVARIANT*);8. propget,id(DISPID_NEWENUM),restricted9. HRESULT_NewEnum(out,retvalIUnknown*);10. ; 注意对于我们的ICollection VTable我们使用一个新创建的GUID。 我们使用oleautomation关键字,因为我们的函数只接受和返回自动化兼容的数据类型。 最后,我们用object关键字来表明这个虚表是对象。它不是一个应用程序可以通过我们的IClassFactory来获得的对象。(稍后我们会看到应用程序是如何获得我们的ICollection对象的)。事实上,对于应用程序来说,除了知道我们的ICollection对象是一个标准的IDispatch对象,其他一无所知(也就是说我们的ICollection对象伪装成一个应用程序或脚本引擎关心的普通的IDispatch)。由于我们的对象伪装成一个普通的IDispatch,在我们的IDL文件中不需要明确定义ICollection对象本身(不像我们在IExample2做的那样,在列出所有接口时要指明哪个是缺省的接口)。所有的脚本语言和应用程序已经知道了一个普通的IDipatch对象意味着什么,所以不再需要放一些关于IDispatch的信息在我们的IDL文件中模拟(ICollection)对象本身。所以,我们只需要像上面那样定义它的虚表,把虚表标记为是对象。 注意,interface一行,我们指定了IDispatch函数包含在VTable中位置和顺序。所以,它就理所当然可以伪装成一个IDispatch了。 然后我们列出三个额外函数。注意他们全部被定义为propget。还要注意Item函数有一个DISPID_VALUDE的ID(DISPID),_NewEnum函数有一个DISPID_NEWENUM的ID 。(对于后者我们也使用restricted关键字,因为我们不希望对象浏览器显示_NewEnum函数。它是一个只由脚本引擎在内部调用的函数,实际的脚本根本不会调用它。)对于Count函数,我们可以选择正数作为ID值,因此我随便选择了1。(对于IExample2的Buffer函数也有一个值是1的ID,这点没关系。因为这些VTable被用于两个不同的对象,所以在两个VTble间他们的ID不需要是唯一的)。 稍后我们会写这些函数。 在我们修改我们的IExample2源代码前,我们拷贝整个IExample2目录到一个新的命名为IExample3目录。我们重命名所有文件名映射到新目录(即IExample2.h变为IExample3.h,IExample2.c变为IExample3.c等等)。做完这些后,我们编辑新目录中的文件,把IExampl2对象重命名为IExample3来与我们先前的代码加以区别。我们要做的是搜索和用“IExample3”替换每个“IExample2”实例。然后不要忘记运行GUIDGEN.EXE来为IExample3生成一个新的GUID,用新的UUID更新IExample3.idl。毕竟我们不希望我们的新的DLL(我们给他命名为IExample3.dlll)与我们先前的IExample2.dll冲突。我为你做了这一切,把结果文件放在IExample3目录中。辅助函数 我们需要构造我们的port名字列表。我们写一个辅助函数来做这些。我们假定我们有三个port,所以创建三个IENUMITEM。我们把这个列表头存储到一个全局变量PortsList中。同时我们还需要一个辅助函数在我们使用完后释放这个列表。cppview plaincopyprint?1. IENUMITEM*PortsList;2. /释放PostsList的辅助函数。当DLL被卸载时调用。3. voidfreePortsCollection(void)4. 5. IENUMITEM*item;6. item=PortsList;7. /有其他元素在列表中吗?8. while(item=PortsList)9. 10. /在我们删除这个元素前得到下一个元素11. PortsList=item-next;12. /如果元素的值是一个对象。我们需要对它Relesase()。如果它是一个BSTR,我们需13. /要它他SysFreeString()。我们对它调用VariantClear。14. VariantClear(&item-value);15. /释放IENUMITEM16. GlobalFree(item);17. 18. 19. /初始化我们的Portslist的辅助函数。当DLL第一次被加载时调用20. HRESULTinitPortsCollection(void)21. 22. IENUMITEM*item;23. /添加一个“Port1”IENUMITEM到我们的列表中24. if(PortsList=item=(IENUMITEM*)GlobalAlloc(GMEM_FIXED,sizeof(IENUMITEM)25. 26. item-next=0;27. item-value.vt=VT_BSTR;28. if(item-value.bstrVal=SysAllocString(LPort1)29. 30. /添加一个“Port2”IENUMITEM到我们的列表中31. if(item-next=(IENUMITEM*)GlobalAlloc(GMEM_FIXED,sizeof(IENUMITEM)32. 33. item=item-next;34. item-value.vt=VT_BSTR;35. if(item-value.bstrVal=SysAllocString(LPort2)36. 37. /添加一个“Port3”IENUMITEM到我们的列表中38. if(item-next=(IENUMITEM*)GlobalAlloc(GMEM_FIXED,sizeof(IENUMITEM)39. 40. item=item-next;41. item-next=0;42. item-value.vt=VT_BSTR;43. if(item-value.bstrVal=SysAllocString(LPort3)44. return(S_OK);45. 46. 47. 48. 49. 50. /错误51. freePortsCollection();52. return(E_FAIL);53. 我们还得加第二个名为CollectonTypeInfo的全局变量来为我们的ICollection保存一个ITypeInfo嗯,IDispatch对象。(我们稍后讨论为什么需要这个)。因此我们需要加全局变量,于是我们写两个辅助函数一个把这个变量初始化为零,另一个来Release这个ITypeInfo:cppview plaincopyprint?1. /我们的Icoolection的ITypeInfo。我们只需要一个所以我们把它定义为全局的2. ITypeInfo*CollectionTypeInfo;3. /初始化我们的ICollectonTypeInfo辅助函数4. voidinitCollectionTypeInfo(void)5. 6. /我们还没有为我们的ICollection创建ITypeInfo7. CollectionTypeInfo=0;8. 9. 10. /Release()我们的ICollection的TypeInfo的辅助函数。当我们的DLL被卸载时调用它11. voidfreeCollectionTypeInfo(void)12. 13. if(CollectionTypeInfo)14. CollectionTypeInfo-lpVtbl-Release(CollectionTypeInfo);15. 现在我们需要修改我们的DllMain来调用这些辅助函数:cppview plaincopyprint?1. BOOLWINAPIDllMain(HINSTANCEinstance,DWORDfdwReason,LPVOIDlpvReserved)2. 3. switch(fdwReason)4. 5. caseDLL_PROCESS_ATTACH:6. 7. 8. MyTypeInfo=0;9. /初始化我们的ICollection原型10. initCollectionTypeInfo();11. /初始化我们的Port列表12. if(initPortsCollection()13. 14. MessageBox(0,CantallocatethePortsList,ERROR,MB_OK);15. return(0);16. 17. OutstandingObjects=LockCount=0;18. MyIClassFactoryObj.lpVtbl=(IClassFactoryVtbl*)&IClassFactory_Vtbl;19. DisableThreadLibraryCalls(instance);20. break;21. 22. caseDLL_PROCESS_DETACH:23. 24. 25. /释放我们Port列表26. freePortsCollection();27. /释放我们ICollectionITypeInfo28. freeCollectionTypeInfo();29. if(MyTypeInfo)MyTypeInfo-lpVtbl-Release(MyTypeInfo);30. 31. 32. return(1);33. collection对象函数 现在,我们来写ICollection的实际函数(事实上是我们的MyRealICollection)。比把这些代码放在IExample3.c中更好的是,我们单独为它创建一个名为PortNames.c的源文件。然后我们把我们的ICollection的 Vtable和他的GUID放在一个独立的PortsNames.h文件中。(我们也把我们上面的辅助函数放在PortNames.c中)IUnknown函数(QueryInterface、AddRef和Release)和 IDispatch函数(GetTypeInfoCount、GetTypeInfo、GetIDsOfNames和Invoke)几乎与我们的IExample3对象相应的函数一样。所以最好是复制代码到这,我要你查看PortNames.c文件(在IExample3目录中)。 当然一个不同是我们的ICollection函数传递一个ICollection对象指针(而不是一个IExample3对象指针)。ICollection的Release函数也有点不同(不像IExample3的Release,因为不需要释放buffer成员)。 另外主要区别是对GetTypeInfoOfGuid的调用。注意我们传递的是ICollection VTable的GUID(而不是像我们在IExample3.c中传递的是IExample3 VTable的GUID)。在这我们这样做,当我们获得一个IExample3的ITypeInfo时(通过在IExample3.c中调用 loadMyTypeInfo),我们传递IExample3的VTable的GUID给OLE函数GetTypeInfoOfGuid。这暗示着微软给我们创建的缺省的ITypeInfo只能获得我们IExample3 VTable中函数的信息。它不能用于获得其他对象的VTable的函数信息。这样我们就需要一个能提供给我们ICollection函数信息的ITypeInfo。所以现在我们必须再次调用GetTypeInfoOfGuid,但这次我们传递的是我们的ICollection对象的VTable的GUID(即我创建的新GUID)。它会返回第二个ITypeInfo(我们把它存储到我们添加的名为CollectonTypeInfo的全局变量中)。这个第二个ITypeInfo可用于使用ICollection的IDispatch 函数来获取ICollection的函数信息。它也可用于在ICollection的Invoke和GetIDsOfName函数中使用 DispInvoke和DispGetIDsOfNames来为我们做几乎所有的工作就像我们用IExample3的ITypeInfo做的那样。 注意ICollection的IDispatch函数使用这个新的ITypeInfo,而IExample3的IDispatch函数使用IExample3的ITypeInfo。他们不是同一个ITypeInfo,也不能交替使用。 剩下的事是写这个三个额外函数,Count、Item和_NewEnum。 Count函数非常简单。它传入一个指向long的指针。Count用在我们的列表中的元素总数来填充这个指针。例如,前面在我们的列表中有三个port(IENUMITEM结构),那么我们返回3。 这是我们的Count函数:cppview plaincopyprint?1. STDMETHODIMPCount(ICollection*this,long*total)2. 3. DWORDcount;4. IENUMITEM*item;5. /Count通过从头到尾遍历IENUMITEM,对每个元素增加count来获得元素总数6. count=0;7. item=(IENUMITEM*)&PortsList;8. while(item=item-next)+count;9. /返回total10. *total=count;11. return(S_OK);12. Item函数也很简单。它传入一个long告诉我们它需要那个元素(0表示第一元素,1表示第二个元素等等)。同时传入一个VARIANT用于我们拷贝这个元素的值给它。 这是我们的Item函数:cppview plaincopyprint?1. STDMETHODIMPItem(ICollection*this,longindex,VARIANT*ret)2. 3. IENUMITEM*item;4. /假定我们什么也不返回。5. ret-vt=VT_EMPTY;6. /定位调用者需要的元素7. item=(IENUMITEM*)PortsList;8. while(item&index-)item=item-next;9. /还有其他元素吗?10. if(item)11. 12. /拷贝这个元素的值到调用者提供的VARIANT中。如果我们返回给调用者是一个对象,13. /我们必须体替调用者对它调用AddRef。调用者在使用完后应该对他调用Release。如14. /果我们返回的是一个BSTR,那么我们必须通过SysAllocString来做一个拷贝,调用15. /者也应该对它调用SysFreeString。其他数据类型只要像这样简单拷贝到调用者的16. /VARIANT中。VariantCopy()为我们做了这一切。如果一切没问题返回S_OK。17. return(VariantCopy(ret,&item-value);18. 19. /如果没有其他元素,返回S_FALSE。20. return(S_FALSE);21. 22. 就像你在上面注释中看到的,OLE函数VariantCopy替我们做了所有的工作。 现在,我们掩掉_NewEnum函数。我们做一个空桩返回E_NOTIMPL。 一旦我们写完ICollection的全部函数,我们静态申明它的VTable:cppview plaincopyprint?1. staticconstICollectionVtblICollectionVTable=2. Collection_QueryInterface,3. Collection_AddRef,4. Collection_Release,5. GetTypeInfoCount,6. GetTypeInfo,7. GetIDsOfNames,8. Invoke,9. Count,10. Item,11. _NewEnum;应用程序获得我们集合对象的方法 让我们考虑应用程序如何获得我们的MyRealICollection对象的一个实例。最容易做的是添加另一个(额外的)函数到IExample3对象。应用程序调用这个新的函数来分配和接受我们的MyRealICollection对象的一个实例。(但我们要欺骗一下应用程序,告诉应用程序它就是一个普通的IDispatch) 我们需要改变IExample3的VTable(在IExample3.h中)的定义,添加这个新的函数,我随意给它命名GetPorts。我把它定义为可接受传入一个IDispatch句柄,我们会把我们新分配的MyRealICollection指针通过它返回哦,是IDispatch。是,是它。它就是一个IDispatch。Wink, wink。这是我们更新后的IExample3的VTable。cppview plaincopyprint?1. /IExample3的VTable2. #undefINTERFACE3. #defineINTERFACEIExample34. DECLARE_INTERFACE_(INTERFACE,IDispatch)5. 6. /IUnkown函数7. STDMETHOD(QueryInterface)(THIS_REFIID,void*)PURE;8. STDMETHOD_(ULONG,AddRef)(THIS)PURE;9. STDMETHOD_(ULONG,Release)(THIS)PURE;10. /IDispatchfunctions11. /IDispatch函数12. STDMETHOD_(ULONG,GetTypeInfoCount)(THIS_UINT*)PURE;13. STDMETHOD_(ULONG,GetTypeInfo)(THIS_UINT,LCID,ITypeInfo*)PURE;14. STDMETHOD_(ULONG,GetIDsOfNames)(THIS_REFIID,LPOLESTR*,15. UINT,LCID,DISPID*)PURE;16. STDMETHOD_(ULONG,Invoke)(THIS_DISPID,REFIID,LCID,17. WORD,DISPPARAMS*,VARIANT*,EXCEPINFO*,UINT*)PURE;18. /额外函数19. STDMETHOD(SetString)(THIS_BSTR)PURE;20. STDMETHOD(GetString)(THIS_BSTR*)PURE;21. STDMETHOD(GetPorts)(THIS_IDispatch*)PURE;/-在这添加GetPorts22. ; 注意我在VTable的最后面添加了GetPorts。还要注意我指明GetPorts要用一个IDipatch指针来填充(尽管它其实是我们的MyRealICollection)。它是一个IDispatch。诚实点。我撒谎了? 同时我必须对在IDL文件中我们的IExample3的VTable做同样的改变:cppview plaincopyprint?1. uuid(CFADB388-9563-4591-AABB-BE7794AEC17C),dual,oleautomation,hidden,nonextensible2. interfaceIExample3VTbl:IDispatch3. 4. helpstring(Setstheteststring.)5. id(1),propputHRESULTBuffer(inBSTR);6. helpstring(Getstheteststring.)7. id(1),propgetHRESULTBuffer(out,retvalBSTR*);8. helpstring(Getstheenumerationforourhardwareports.)9. id(2),propgetHRESULTPorts(out,retvalIDispatch*);/-在这添加GetPorts10. ; 注意我给新添加的函数加了propget,就像Buffer那样。这样脚本就可以用一个普通的方法来获取我们的MyRealICollection了咄!IDispatch对象。这个脚本相关的成员叫“Ports”。不用担心实际上在我们的IExample3对象中没有Ports这个数据成员。这是一个假的成员。但脚本不需要知道这些。 同时我随意给它一个2作为DISPID。 不要忘记我们需要把这个函数添加到IExample3.c中我们的IExample3Vtbl静态声明中:cppview plaincopyprint?1. staticconstIExample3VtblIExample3_Vtbl=QueryInterface,2. AddRef,3. Release,4. GetTypeInfoCount,5. GetTypeInfo,6. GetIDsOfNames,7. Invoke,8. SetString,9. GetString,10. GetPorts;/lpVtbl=(ICollectionVtbl*)&ICollectionVTable;10. /对它进行AddRef11. collection-count=1;12. /需要另一个未完对象因为我们会把它返回给应用程序,应用程序应该对它调用Release13. InterlockedIncrement(&OutstandingObjects);14. 15. /把它当作IDispatch(它可以被用作)返回16. return(IDispatch*)collection);17. 我们做完了。你可以编译这个IExample3.dll。注册它,就是用IExample的注册工具(RegIExample2),用“IExample3”替换每个“IExample2”。毕竟除了我们要注册的是IExample3,其他与IExample2没什么区别。同样,对于反注册,修改IExample2的反注册工具(UnRegIExample2)。VBScript例程 让我们来写一个使用我们的集合来显示port名的VBScript例程。我已经泄了这个例程(IExample3.vbs),把它放在IExample3目录中。 当然这个VBScript需要首先调用CreateObject来获得我们的IExample3对象的一个实例。如果正确地安装了它,它应该
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025中医院结构化面试题库设计与提问技巧考核
- 2025年品牌授权使用合同书
- 2025年农业技术合作开发合同书
- 2025年智慧城市供用热力合同(GF-1999-0503)技术应用协议
- 2025年低空经济乘数效应下航空租赁行业研究报告
- 基于离散元法的考虑尺寸和配位数影响的颗粒破碎准则研究
- 2025年化学药物制剂考试试卷及答案
- 2025年低空经济无人机冷链物流温控技术政策环境报告
- 2025年低空经济「航空制造」智能制造与产业链协同报告
- 2025年大学生就业指导期末试卷及答案
- 2025贵州盐业(集团)黔南有限责任公司公开招聘工作人员6人考试参考试题及答案解析
- 无人机理论培训课件
- 燃气管道工程信息化管理平台建设方案
- 2025版全新舞台租赁及演出项目管理合同
- 行政执法检查规范课件
- QC/T 983-2025汽车变速器总成清洁度检测方法
- 2025海南省纪委监委所属事业单位招聘事业编制人员8人(第1号)笔试参考题库附答案解析
- 行政法专业毕业论文
- 2025年中国银行考试题目及答案
- 小学德育工作会议记录
- 2025年《临床输血技术规范》
评论
0/150
提交评论