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
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 *pIHelloSimpleObject,一个原始的COM接口指针。
二者对->的操作的用法与意义一样。因为对智能指针的->操作会转换为对 _NoAddRefReleaseOnCComPtr
不过智能指针不能执行->AddRef()与->Release()。因为_NoAddRefReleaseOnCComPtr
智能指针禁用->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断言报错,从而提醒程序员进行相应代码的更改。