




已阅读5页,还剩15页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
三、纯手工创建一个COM组件 1、从建工程到实现注册 在这一过程中我们将完成三个步骤:创建dll的入口函数,定义接口文件,实现注册功能 1.1创建一个类型为win32 dll工程 创建一个名为MathCOM的win32 dll工程。 在向导的第二步选择A smiple dll project选项。当然如果你选择一个空的工程,那你自己完成DllMain定义吧。 1.2 定义接口文件 生成一个名为MathCOM.idl的接口文件。并将此文件加入到刚才创建的那个工程里。 view source print?01./MathCOM.idl文件 02./ MathCOM.idl : IDL source for MathCOM.dll 03./ 04./ This file will be processed by the MIDL tool to 05./ produce the type library (MathCOM.tlb) and marshalling code. 06.import oaidl.idl; 07.import ocidl.idl; 08. 09.uuid(FAEAE6B7-67BE-42a4-A318-3256781E945A), 10.helpstring(ISimpleMath Interface), 11.object, 12.pointer_default(unique) 13. 14.interface ISimpleMath : IUnknown 15. 16.HRESULT Add(inint nOp1,inint nOp2,out,retvalint * pret); 17.HRESULT Subtract(inint nOp1,inint nOp2,out,retvalint * pret); 18.HRESULT Multiply(inint nOp1,inint nOp2,out,retval int * pret); 19.HRESULT Divide(inint nOp1,inint nOp2,out,retvalint * pret); 20.; 21.22. 23.uuid(01147C39-9DA0-4f7f-B525-D129745AAD1E), 24.helpstring(IAdvancedMath Interface), 25.object, 26.pointer_default(unique) 27. 28.interface IAdvancedMath : IUnknown 29. 30.HRESULT Factorial(inint nOp1,out,retvalint * pret); 31.HRESULT Fabonacci(inint nOp1,out,retvalint * pret); 32.; 33. 34.uuid(CA3B37EA-E44A-49b8-9729-6E9222CAE844), 35.version(1.0), 36.helpstring(MATHCOM 1.0 Type Library) 37. 1.42.library MATHCOMLib 43. 44.importlib(stdole32.tlb); 45.importlib(stdole2.tlb); 46.47. 48.uuid(3BCFE27E-C88D-453C-8C94-F5F7B97E7841), 49.helpstring(MATHCOM Class) 50. 51.coclass MATHCOM 52. 53.default interface ISimpleMath; 54.interface IAdvancedMath; 55.; 56.; 在编译此工程之前请检查Project/Setting/MIDL中的设置。正确设置如下图: 图1.4midl的正确设置 在正确设置后,如编译无错误,那么将在工程的目录下产生四个 文件名 作用 MathCOM.h 接口的头文件,如果想声明或定义接口时使用此文件 MathCOM_i.c 定义了接口和类对象以及库,只有在要使用到有关与GUID有关的东西时才引入此文件,此文件在整个工程中只能引入一次,否则会有重复定义的错误 MathCOM_p.c 用于存根与代理 dlldata.c 不明 1.3 增加注册功能 作为COM必须要注册与注销的功能。 1.3.1 增加一个MathCOM.def文件 DEF文件是模块定义文件(Module Definition File)。它允许引出符号被化名为不同的引入符号。 view source print?01./MathCOM.def文件 02.; MathCOM.def : Declares the module parameters. 03.04.LIBRARY MathCOM.DLL05.06.EXPORTS 07.DllCanUnloadNow 1 PRIVATE 08.DllGetClassObject 2 PRIVATE 09.DllRegisterServer 3 PRIVATE 10.DllUnregisterServer 4 PRIVATEDllUnregisterServer这是函数名称 4这是函数序号 PRIVATE 接下来大致介绍一下DllRegisterServer()和DllUnregisterServer()。(其他两个函数的作用将在后面介绍) 1.3.2 DllRegisterServer()和DllUnregisterServer() DllRegisterServer() 函数的作用是将COM服务器注册到本机上。 DllUnregisterServer() 函数的作用是将COM服务器从本机注销。 1.4 MathCOM.cpp文件 现在请将 MathCOM.cpp 文件修改成如下: view source print?01./ MATHCOM.cpp : Defines the entry point for the DLL application. 02./ 03.#include stdafx.h 04.#include 05.#include 06.#include MathCOM.h 07./standard self-registration table 08.const char * g_RegTable3= 09.CLSID3BCFE27E-C88D-453C-8C94-F5F7B97E7841,0,MathCOM, 10.CLSID3BCFE27E-C88D-453C-8C94-F5F7B97E7841InprocServer32, 11.0, 12.(const char * )-1 /*表示文件名的值*/, 13.CLSID3BCFE27E-C88D-453C-8C94-F5F7B97E7841ProgID,0,tulip.MathCOM.1, 14.tulip.MathCOM.1,0,MathCOM, 15.tulip.MathCOM.1CLSID,0,3BCFE27E-C88D-453C-8C94-F5F7B97E7841, 16.; 17.HINSTANCE g_hinstDll; 18.BOOL APIENTRY DllMain( HANDLE hModule, 19.DWORD ul_reason_for_call, 20.LPVOID lpReserved 21.) 22. 23.g_hinstDll=(HINSTANCE)hModule; 24.return TRUE; 25. 26./* 27.* Function Declare : DllUnregisterServer 28.* Explain : self-unregistration routine 29.* Parameters : 30.* void - 31.* Return : 32.* STDAPI - 33.* Author : tulip 34.* Time : 2003-10-29 19:07:42 35.*/36.STDAPI DllRegisterServer(void) 37. 38.HRESULT hr=S_OK; 39.char szFileName MAX_PATH; 40.:GetModuleFileName(g_hinstDll,szFileName,MAX_PATH); 41.42.int nEntries=sizeof(g_RegTable)/sizeof(*g_RegTable); 43.for(int i =0;SUCCEEDED(hr)&i nEntries;i+) 44. 45.const char * pszKeyName=g_RegTablei0; 46.const char * pszValueName=g_RegTablei1; 47.const char * pszValue=g_RegTablei2; 48.49.if(pszValue=(const char *)-1) 50. 51.pszValue=szFileName; 52. 53.54.HKEY hkey; 55.long err=:RegCreateKey(HKEY_CLASSES_ROOT,pszKeyName,&hkey); 56.if(err=ERROR_SUCCESS) 57. 58.err=:RegSetValueEx( hkey, 59.pszValueName, 60.0, 61.REG_SZ, 62.( const BYTE*)pszValue, 63.( strlen(pszValue)+1 ) ); 64.:RegCloseKey(hkey); 65. 66.if(err!=ERROR_SUCCESS) 67. 68.:DllUnregisterServer(); 69.hr=E_FAIL; 70. 71.72. 73.return hr; 74. 75.76.STDAPI DllGetClassObject(REFCLSID rclsid ,REFIID riid,void *ppv) 77. 78.return CLASS_E_CLASSNOTAVAILABLE; 79. 80.81.STDAPI DllCanUnloadNow(void) 82. 83.return E_FAIL; 84.我只是在此文件中加几个必要的头文件和几个全局变量。并实现了 DllRegisterServer()和DllUnregisterServer()。而对于其他两引出函数我只返回一个错误值罢了。 1.5 小结 现在我们的工程中应该有如下文件: 文件名 作用 Stdafx.h和stdafx.cpp 预编译文件 MathCOM.cpp Dll入口函数及其他重要函数定义的地方 MathCOM.def 模块定义文件 MathCOM.idl 接口定义文件(在1.2后如果编译的话应该还有四个文件) 好了到现在,我的所谓COM已经实现注册与注销功能。如果在命令行或运行菜单下项执行如下regsvr32 绝对路径MathCOM.dll就注册此COM组件。在执行完此命令后,请查看注册表项的HKEY_CLASSES_ROOTCLSID项看看3BCFE27E-C88D-453C-8C94-F5F7B97E7841这一项是否存在(上帝保佑存在)。 如同上方法再执行一下regsvr32 -u 绝对路径MathCOM.dll,再看看注册表。 其实刚才生成的dll根本不是COM组件,哈哈!因为他没有实现DllGetClassObject()也没有实现ISmipleMath和IAdvancedMath两个接口中任何一个。 让我们继续前行吧! 2、实现ISmipleMath,IAdvancedMath接口和DllGetClassObject() 2.1 实现ISmipleMath和IAdvancedMath接口 让我们将原来的 CMath 类修改来实现ISmipleMath接口和IAdvancedMath接口。 修改的地方如下: view source print?01.1) Math.h文件 02./*#-2003-10-29 21:33:44 (tulip)-#* 03.#include interface.h*/04.#include MathCOM.h/新增加的,以替换上面的东东 05.class CMath : public ISimpleMath, 06.public IAdvancedMath 07. 08.private: 09.ULONG m_cRef; 10.private: 11.int calcFactorial(int nOp); 12.int calcFabonacci(int nOp); 13.public: 14.CMath(); 15./IUnknown Method 16.STDMETHOD(QueryInterface)(REFIID riid, void *ppv); 17.STDMETHOD_(ULONG, AddRef)(); 18.STDMETHOD_(ULONG, Release)(); 19./ ISimpleMath Method 20.STDMETHOD (Add)(int nOp1, int nOp2,int * pret); 21.STDMETHOD (Subtract)(int nOp1, int nOp2,int *pret); 22.STDMETHOD (Multiply)(int nOp1, int nOp2,int *pret); 23.STDMETHOD (Divide)(int nOp1, int nOp2,int * pret); 24./ IAdvancedMath Method 25.STDMETHOD (Factorial)(int nOp,int *pret); 26.STDMETHOD (Fabonacci)(int nOp,int *pret); 27.;view source print?01.2) Math.cpp文件 02./*#-2003-10-29 21:32:35 (tulip)-#* 03.04.#include interface.h */05.#include math.h 06.07.08.STDMETHODIMP CMath:QueryInterface(REFIID riid, void *ppv) 09./ 这里这是实现dynamic_cast的功能,但由于dynamic_cast与编译器相关。 10.if(riid = IID_ISimpleMath) 11.*ppv = static_cast(this); 12.else if(riid = IID_IAdvancedMath) 13.*ppv = static_cast(this); 14.else if(riid = IID_IUnknown) 15.*ppv = static_cast(this); 16.else 17.*ppv = 0; 18.return E_NOINTERFACE; 19. 20.21.reinterpret_cast(*ppv)-AddRef(); /这里要这样是因为引用计数是针对组件的 22.return S_OK; 23. 24.25.STDMETHODIMP_(ULONG) CMath:AddRef() 26. 27.return +m_cRef; 28. 29.30.STDMETHODIMP_(ULONG) CMath:Release() 31. 32.ULONG res = -m_cRef; / 使用临时变量把修改后的引用计数值缓存起来 33.if(res = 0) / 因为在对象已经销毁后再引用这个对象的数据将是非法的 34.delete this; 35.return res; 36. 37.38.STDMETHODIMP CMath:Add(int nOp1, int nOp2,int * pret) 39. 40.*pret=nOp1+nOp2; 41.return S_OK; 42. 43.44.STDMETHODIMP CMath:Subtract(int nOp1, int nOp2,int * pret) 45. 46.*pret= nOp1 - nOp2; 47.return S_OK; 48. 49.50.STDMETHODIMP CMath:Multiply(int nOp1, int nOp2,int * pret) 51. 52.*pret=nOp1 * nOp2; 53.return S_OK; 54. 55.56.STDMETHODIMP CMath:Divide(int nOp1, int nOp2,int * pret) 57. 58.*pret= nOp1 / nOp2; 59.return S_OK; 60. 61.62.int CMath:calcFactorial(int nOp) 63. 64.if(nOp = 1) 65.return 1; 66.67.return nOp * calcFactorial(nOp - 1); 68. 69.70.STDMETHODIMP CMath:Factorial(int nOp,int * pret) 71. 72.*pret=calcFactorial(nOp); 73.return S_OK; 74. 75.76.int CMath:calcFabonacci(int nOp) 77. 78.if(nOp QueryInterface(riid,ppv); 18.19.return CLASS_E_CLASSNOTAVAILABLE; 20.2.4 客户端 接下来我们写个客户端程序对此COM进行测试。 新建一个空的名为 TestMathCOM 的 win32 Console 工程,将它添加到 MathCOM workspace 中。 在 TestMathCOM 工程里添加一个名为 main.cpp 的文件,此文件的内容如下: view source print?01./main.cpp文件 02.#include 03.#include ./MathCOM.h/这里请注意路径 04.#include ./MathCOM_i.c/这里请注意路径 05.#include 06.using namespace std; 07.08.void main(void) 09. 10./初始化COM库 11.HRESULT hr=:CoInitialize(0); 12.ISimpleMath * pSimpleMath=NULL; 13.IAdvancedMath * pAdvancedMath=NULL; 14.15.int nReturnValue=0; 16.17.hr=:CoGetClassObject(CLSID_MATHCOM, 18.CLSCTX_INPROC, 19.NULL,IID_ISimpleMath, 20.(void *)&pSimpleMath); 21.if(SUCCEEDED(hr) 22. 23.hr=pSimpleMath-Add(10,4,&nReturnValue); 24.if(SUCCEEDED(hr) 25.cout 10 + 4 = NRETURNVALUE QueryInterface(IID_IAdvancedMath, (void *)&pAdvancedMath); 28.if(SUCCEEDED(hr) 29. 30.hr=pAdvancedMath-Fabonacci(10,&nReturnValue); 31.if(SUCCEEDED(hr) 32.cout 10 Fabonacci is nReturnValue Release(); 35.pSimpleMath-Release(); 36.37.:CoUninitialize(); 38.39.:system(pause); 40.return ; 41.42.关于如何调试dll请参阅附录A 2.5 小结 到现在我们应该有 2 个工程和 8 个文件,具体如下: 工程 文件 作用 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组件 在此部分中我们已经完成一个可以实用的接近于完整的 COM组件。我们完成了此COM组件的客户端。如果你已经创建COM实例的话,你可能会发现在此部分的客户端并不是用CoCreateInstance()来创建COM实例,那是因为我们还没有在此COM组件里实现IClassFactory接口(此接口在下一部分实现)。通过这个例子,我希望大家明白以下几点: 1) DllGetClassObject()的作用,请参看COM组件调入大致过程这一节,同时也请将断点打在DllGetClassObject()函数上,仔细看看他的实现(在没有实现IClassFactory接口的情况下)和他的传入参数。2) 为什么在这个客户端程序里不使用CoCreateInstance()来创建COM实例而使用CoGetClassObject()来创建COM实例。你可以试着用CoCreateInstance()来创建Cmath,看看DllGetClassObject()的第一参数是什么?3) 实现IClassFactory接口不是必需的,但应该说是必要的(如何实现请看下一章) 4) 应掌握DllRegisterServer()和DllUnregisterServer()的实现。 5) 客户端在调用COM组件时需要那几个文件(只要由idl文件产生的两个文件) 3、类厂 附录 A 我对 dll 的一点认识 目标:写几个比较简单的dll并了解*.dll与*.lib的关系。 一:没有lib的dll 1.1建一个没有lib的dll 1) 新建一个com_1.cpp文件(注意此dll根本没有什么用) 2) 在com_1.cpp写下下面的代码 3) 按下F5运行,所有的东西都按确定。 4) 应该出现如下错误: view source print?1.Linking. 2.Creating library Debug/COM_1.lib and object Debug/COM_1.exp3.LIBCD.lib(crt0.obj) : error LNK2001: unresolved external symbol _main 4.Debug/COM_1.exe : fatal error LNK1120: 1 unresolved externals5)进入 project|setting,在 C/C+ 属性框的 project Options 里把 /D _console 修改成/D _WINDOWS。 6)进入project|setting,在 link 属性框的 project Options 里增加下面的编译开关 /dll 增加的编译开关大致如下: view source print?1.kernel32.lib user32.lib gdi32.lib winspool.lib 2.comdlg32.lib advapi32.lib shell32.lib 3.ole32.lib oleaut32.lib uuid.lib odbc32.lib 4.odbccp32.lib /nologo /dll /incremental:yes 5./pdb:Debug/COM_1.pdb /debug /machine:I386 /out: 6.Debug/COM_1.dll /implib:Debug/COM_1.lib 7./pdbtype:sept注意:/dll应该与后面的开关之间有一个空格 view source print?01./com_1.cpp 02.#include 03.BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved) 04. 05.HANDLE g_hModule; 06.switch(dwReason) 07. 08.case DLL_PROCESS_ATTACH: 09.g_hModule = (HINSTANCE)hModule; 10.break; 11.case DLL_PROCESS_DETACH: 12.g_hModule=NULL; 13.break; 14. 15.现在可以编译了,这小片段代码将会生成一个dll,但这个dll是没有用的。没有引出函数和变量。 1.2 调试没有 lib 的 dll 1) 新建一个工程 Client,工程类型为 console,将上面创建的 dll copy 到 client 工程目录下 2) 增加 Client.cpp(代码见下)到工程 Client 中去 3) 选中 Client 工程,并在 project|setting|debug|Category 下拉框,如图:图1.4 调试 注意这是一种调试 dll 的方法 5) 现在可以在Client和COM_1.dll里打断点调试了。 在这里我们只能调试DllMain()函数,因为那个dll里除了就没别的东西了,下面我开始增加一点东西。 二:带有lib的dll 2.1 创建一个带有lib的dll 我们在原来的基础上让上面的代码产生一个lib了。新的代码如下: view source print?01.#include 02.03.extern C _declspec(dllexport) void tulip (void) 04. 05.:MessageBox(NULL,ok,Iam fine,MB_OK); 06. 07.08.BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved) 09. 10.HANDLE g_hModule; 11.switch(dwReason) 12. 13.case DLL_PROCESS_ATTACH: 14.g_hModule = (HINSTANCE)hModule; 15.break; 16.case DLL_PROCESS_DETACH: 17.g_hModule=NULL; 18.break; 19. 20.21.return TRUE; 22.在这个dll里,我们引出一个tulip函数。如果此时我们想要在客户调用此函数应该用什么方法呢? 上面的代码除了生成dll外,他比第一个程序多产生一个lib文件,现在应该知道dll与lib的关系吧。Lib文件是dll输出符号文件。如果一个dll没有任何东西输出那么不会有对应的lib文件,但只要一个dll输出一个变量或函数就会相应的lib文件。总的说来,dll与lib是相互配套的。 当某个dll他有输出函数(或变量)而没有lib文件时,我们应该怎么调用 dll 的函数呢?请看下面的方法。 2.2 调试带有引用但没有头文件的 dll 注意:本方法根本没有用 COM_1.lib 文件,你可以把 COM_1.lib 文件删除而不影响。 此时的客户端代码如果下: view source print?01.#include 02.03.int main(void) 04. 05./定义一个函数指针 06.typedef void ( * TULIPFUNC )(void); 07.08./定义一个函数指针变量 09.TULIPFUNC tulipFunc; 10.11./加载我们的dll 12.HINSTANCE hinst=:LoadLibrary(COM_1.dll); 13.14./找到dll的tulip函数 15.tulipFunc=(TULIPFUNC)GetProcAddress(hinst,tulip); 16.17./调用dll
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 第九课 我们都能守纪律说课稿-2025-2026学年小学心理健康鄂教版一年级-鄂教版
- Unit2 What's your number(教学设计)-2024-2025学年人教精通版英语四年级上册
- 《第二单元 参考活动2 做出正确的决定》说课稿 -2023-2024学年初中综合实践活动苏少版八年级上册
- 第8课 金鱼饲养教学设计-2025-2026学年小学劳动小学高年级湘教版(广西)
- 2024年秋九年级化学上册 第2单元 课题3 制取氧气说课稿 (新版)新人教版
- Unit 2 No rules,no order Section A 2a-2f 说课稿 2024-2025学年人教版英语七年级下册
- 蔬果种植理论与实践课件
- 《包身工》教学设计 2023-2024学年统编版高中语文选择性必修中册
- 高中信息技术浙教版:2-3 三维模型创作-教学设计
- 2025年浙江省中考语文试题(含答案解析)
- 道路运输驾驶员心理与行为分析考核试卷
- ISO 22003-1:2022《食品安全-第 1 部分:食品安全管理体系 审核与认证机构要求》中文版(机翻)
- 《路基路面工程》全套教学课件
- DL∕T 2582.1-2022 水电站公用辅助设备运行规程 第1部分:油系统
- 【幼儿园园长论文:我将成为一名合格的园长4000字】
- 清廉经营声明函-餐饮服务
- 2024年长沙航空职业技术学院单招职业技能测试题库附答案
- 2022年黑龙江统招专升本艺术概论真题
- 初中历史新课标课程标准2022年版考试题库及答案
- 广告法理论与实务
- 法学研究中的案例比较与对比研究方法
评论
0/150
提交评论