COM组件笔记(五)
一、COM组件的注册
- 我们目前用ATL制作出来的简单对象是*.dll的形式,我们在之前的例子中能调用这个dll,是因为我们的VisualStudio在编译生成这个dll时,对它进行了注册。
- 如何手动注册COM组件:
1)打开控制台窗口(一般情况下要以管理员身份运行,因为注册COM组件的程序一般需要管理员权限):
2)查看注册的动态库信息,需要加载注册表:
查找对应的GUID信息
3.对COM组件进行注册的意义:
我们刚才从注册表中看到了注册信息记录了组件的CLSID,组件的路径,组件的PropID等。
而我们的COM组件是跨应用的,网页,MFC,C#的WinForm,VB需要识别并创建我们的组件。最终需要得到我们组件的路径。而路径是可能改变的,比如用户安装程序时选择不同目录,CLSID跟PropID是不变 的,我们采用CLSID或PropID去创建组件,不管COM组件的ll在哪个位置,都跟我们的创建代码无关。
从vista开始,注册需要权限了,我们可以做一个不需要注册的COM。这个内容放到以后高级编程中来讲。
二、智能指针的使用
- C++在调用COM接口指针时是很危险的,因为使用过程需要每一个使用都严格并且正确地调用AddRef()与Release()方法。一旦出现问题,就会造成对象不能被正常释放或者对象被重复删除。
- 所以C++程序员使用COM接口时,得小心翼翼的。有没有办法改变这种现状,让我们的编程过程更简单呢?答案是有的,那就是智能指针。
- CComPtr是智能指针,是ATL提供的一个模板类,能封装COM接口的AddRef()与Release()方法。
- CComPtr声明出来的变量是一个对象,这个对象封装了COM接口指针,这个对象的使用访问跟COM接口的使用方法几乎一样。
案例:
1 // 案例在MFC下进行,函数写在一个按钮中 2 void CMFCTestSectionDemo1Dlg::OnBnClickedButton1() 3 { 4 // TODO: 在此添加控件通知处理程序代码 5 #pragma region 不使用智能指针 6 HRESULT hr = E_FAIL; 7 hr = CoInitialize(NULL); // 使用com组件之前要添加这一句,用COM组件的入口点 8 9 if (SUCCEEDED(hr)) { 10 IHelloSimpleObject* pIHelloSimpleObject = NULL; 11 hr = CoCreateInstance(CLSID_HelloSimpleObject, NULL, 12 CLSCTX_INPROC_SERVER, IID_IHelloSimpleObject, (void**)&pIHelloSimpleObject); 13 if (SUCCEEDED(hr)) { 14 15 // 加法方法测试 16 LONG lSum = 0; 17 hr = pIHelloSimpleObject->SumLong(9, 99, &lSum); 18 19 // 读描述属性 20 BSTR bstrDescriptionA = SysAllocString(L""); 21 hr = pIHelloSimpleObject->get_Description(&bstrDescriptionA); 22 SysFreeString(bstrDescriptionA); 23 bstrDescriptionA = NULL; 24 25 // 写属性描述 26 BSTR bstrDescriptionB = SysAllocString(L"我运行在MFC中,我是新的描述属性!"); 27 hr = pIHelloSimpleObject->put_Description(bstrDescriptionB); 28 SysFreeString(bstrDescriptionB); 29 bstrDescriptionB = NULL; 30 31 // 读描述属性 32 BSTR bstrDescriptionC = SysAllocString(L""); 33 hr = pIHelloSimpleObject->get_Description(&bstrDescriptionC); 34 SysFreeString(bstrDescriptionC); 35 bstrDescriptionA = NULL; 36 } 37 pIHelloSimpleObject->Release(); // 不适用这个函数,引用计数减1 38 } 39 40 CoUninitialize(); //不使用com组件对象时添加这一句,用COM组件的出口点 41 #pragma endregion 42 43 #pragma region 使用智能指针 44 HRESULT hr = E_FAIL; 45 hr = CoInitialize(NULL); 46 47 if (SUCCEEDED(hr)) { 48 CComPtr<IHelloSimpleObject> spHelloSimpleObject; 49 hr = spHelloSimpleObject.CoCreateInstance(CLSID_HelloSimpleObject); 50 /* 51 * 对比以下 52 hr = CoCreateInstance(CLSID_HelloSimpleObject, NULL, 53 CLSCTX_INPROC_SERVER, IID_IHelloSimpleObject, (void**)&pIHelloSimpleObject); 54 */ 55 if (SUCCEEDED(hr)) { 56 // 加法方法测试 57 LONG lSum = 0; 58 hr = spHelloSimpleObject->SumLong(9, 99, &lSum); 59 60 // 读描述属性 61 BSTR bstrDescriptionA = SysAllocString(L""); 62 hr = spHelloSimpleObject->get_Description(&bstrDescriptionA); 63 SysFreeString(bstrDescriptionA); 64 bstrDescriptionA = NULL; 65 66 // 写描述属性 67 BSTR bstrDescriptionB = SysAllocString(L"我运行在MFC中,我是新的描述属性!"); 68 hr = spHelloSimpleObject->put_Description(bstrDescriptionB); 69 SysFreeString(bstrDescriptionB); 70 bstrDescriptionB = NULL; 71 72 // 读描述属性 73 BSTR bstrDescriptionC = SysAllocString(L""); 74 hr = spHelloSimpleObject->get_Description(&bstrDescriptionC); 75 SysFreeString(bstrDescriptionC); 76 bstrDescriptionC = NULL; 77 } 78 79 // spHelloSimpleObject->Release(); 不需要调用,也不能调用 80 } 81 82 CoUninitialize(); 83 #pragma endregion 84 85 }
三、智能指针与COM接口指针的比较
- CComPtr<IHelloSimpleObject> spHelloSimpleObject 创建了一个智能指针,其它是一个类对象,对象内部有一个IHelloSimpleObjectt*的指针变量,并且被初始化为NULL。
- IHelloSimpleObject *pIHelloSimpleObject,一个原始的COM接口指针。
- 二者对->的操作的用法与意义一样。因为对智能指针的->操作会转换为对 _NoAddRefReleaseOnCComPtr<IHelloSimpleObjectSub>* 变量的->操作。 _NoAddRefReleaseOnCComPtr<IHelloSimpleObjectSub>是 IHelloSimpleObjectSub的子类。
- 不过智能指针不能执行->AddRef()与->Release()。因为_NoAddRefReleaseOnCComPtr<T>类把AddReft()与Release()方法的访问权限设置为private。如果智能指针执行->AddRef()与->Release(),编译时,直接报错。
- 智能指针禁用->AddRef()与->Release()的原因是智能指针封装了COM接口指针的AddRef与Release操作,会智能判断何时对象内部会调用COM接口的AddRef,何时调用COM接口的Release。
- 之前对COM接口指针的赋值是需要做AddRef操作来添加引用计数,如果采用智能指针,它会智自地执行AddRef我们不必为此担心。
- 之前不再使用COM接口指针时,需要做Release操作来减少引用计数,如果采用智能指针,它会在对象销毁时(析构时),做COM接口指针的Release操作。
- 智能指针的变量是一个对象。
- 如果是局部变量,将在这个局部变量生命期结束时行智能指针的析构函数;
- 如果是成员变量,将在成员所在的类对象销毁时,执行智能指针的析构函数;
- 如果是静态变量,将在程序结束时,执行智能指针的析构函数。
四、智能指针的注意点
- 如果要释放一个智能指针直接给它赋NULL值就可以了。这样内部的COM接口指针也会执行Release操作,来减少引用计数;
- 当对智能指针取地址时(&运算符操作),要确保智能指针为NULL,因为&是要返回内部的COM接口指针的,如果不为NULL,则旧的COM接口指针将没有执行Release而直接赋值了一个旧的COM接口指针;
- 不过大家可以放心,因为如果这时智能指针不为NULL时,智能指的代码会通过assert断言报错,从而提醒程序员进行相应代码的更改。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!