二、我们的第一个COM程序
1. COM环境的建立
首先用Visual Studio建立一个普通的控制台程序(去掉预编译头)。在前面添加windows.h的包含文件:
#include <windows.h>
使用COM之前首先要初始化COM环境——初始化COM环境使用CoInitialize(0)函数。它唯一的参数是保留的,必须始终为0:
HRESULT hr = CoInitialize(0);
ValidateHR(hr);
其中ValidateHR(hr)宏在上一节已经定义过了,用于检验COM调用是否成功,若失败则报错并退出程序。
在程序结束之前,要调用CoUninitialize()卸载COM环境,这个函数很简单,没有参数,没有返回值,直接调用即可:
CoUninitialize();
至此,我们的程序如下,这便是最简单的COM程序了:
2. 接口实例的获取、使用与释放
不同于大多数C++对象,COM组件是使用引用计数管理生命周期的。每一个接口实例都对应着组件的一个引用计数。每获得一个新的接口实例,组件的引用计数加一。每释放一个旧的接口实例,组件的引用计数减一。当引用计数减为0时,COM组件将析构并释放自己的内存。
获取一个COM接口实例有CoCreateInstance、QueryInterface、AddRef等很多种方法,而释放一个COM接口实例一般是调用Release。
a.释放一个接口实例
释放一个接口实例比较简单,但是正确地释放接口实例却非常重要。如果不正确释放,就会导致内存泄漏或者程序崩溃。上面已经讲了,每一个接口都有IUnknown的三个函数:QueryInterface、AddRef、Release。在使用完一个实例之后,程序要调用此接口的Release成员函数释放该实例。Release会使得引用计数减一,并于引用计数归零时删除组件本身。每一个实例都需要调用一次Release:
p1->Release(); // p1 是程序所拥有的一个COM接口实例
b.获取一个接口实例
获取接口实例最通用的的方法,是使用CoCreateInstance函数。由于CoCreateInstance建立了新的接口实例,因此CoCreateInstance调用后,它会使组件的引用计数加一。
CoCreateInstance函数的使用方法如下:
HRESULT hr = CoCreateInstance(CLSID_组件ID, NULL, CLSCTX_ALL, IID_接口ID, (void**)&接口指针);
第一个参数是组件ID(CLSID),第四个参数是接口ID(IID),可以在相关头文件中找到它的常量定义。最后一个参数是返回值,用以接收CoCreateInstance返回的接口指针。
第二个参数(外层IUnknown指针)不常用,一般指定为NULL。第三个参数(组件环境)一般指定CLSCTX_ALL,表示可以接受任何形式的COM服务(进程内、进程外、远程都可以)。
获取相同组件相同接口的新实例的方法,是使用AddRef。它也会使得组件的引用计数加一。AddRef的用法如下:
IMyInterface *p2 = p1; // p2和p1指向相同的实例
p2->AddRef(); // 在p2建立IMyInterface新的实例,引用计数加一
获取相同组件任意接口的新实例的方法,是使用QueryInterface。它同样会使组件的引用计数加一。
HRESULT hr = p1->QueryInterface(IID_接口ID, (void**)&接口指针);
QueryInterface的两个参数和CoCreateInstance最后两个参数的含义相同。
3. 获取、使用和释放接口实例的实际操作
在这里我们将要调用的是Windows的文字转语音(Text-To-Speech)引擎,需要包含头文件#include <sapi.h>:
在sapi.h中,文字转语音使用的接口是ISpVoice,其组件ID为CLSID_SpVoice,接口ID为IID_ISpVoice。ISpVoice的成员函数Speak可以让电脑开始说话。首先,我们要建立一个ISpVoice实例:
ISpVoice实例建立成功之后,我们就可以使用->操作符直接调用它的的Speak函数了:
最后,别忘了释放ISpVoice的实例!
最后的程序如下。把音量开到最大,按Ctrl+F5运行,即可听到电脑在说话![s::lol]
1. COM环境的建立
首先用Visual Studio建立一个普通的控制台程序(去掉预编译头)。在前面添加windows.h的包含文件:
#include <windows.h>
使用COM之前首先要初始化COM环境——初始化COM环境使用CoInitialize(0)函数。它唯一的参数是保留的,必须始终为0:
HRESULT hr = CoInitialize(0);
ValidateHR(hr);
其中ValidateHR(hr)宏在上一节已经定义过了,用于检验COM调用是否成功,若失败则报错并退出程序。
在程序结束之前,要调用CoUninitialize()卸载COM环境,这个函数很简单,没有参数,没有返回值,直接调用即可:
CoUninitialize();
至此,我们的程序如下,这便是最简单的COM程序了:
<code class="lang-cpp">#include <stdio.h> #include <stdlib.h> #include <windows.h> // 使用COM组件需要包含windows.h #define ValidateHR(hr) \ if (FAILED(hr)) { \ printf("HRESULT错误:%p,在%s第%d行\n", hr, __FILE__, __LINE__); \ exit(1); \ } int main() { HRESULT hr = CoInitialize(0); // 初始化COM环境,参数保留,必须为0 ValidateHR(hr); // 什么也不做 CoUninitialize(); // 卸载COM环境 return 0; }</windows.h></stdlib.h></stdio.h></code>
2. 接口实例的获取、使用与释放
不同于大多数C++对象,COM组件是使用引用计数管理生命周期的。每一个接口实例都对应着组件的一个引用计数。每获得一个新的接口实例,组件的引用计数加一。每释放一个旧的接口实例,组件的引用计数减一。当引用计数减为0时,COM组件将析构并释放自己的内存。
获取一个COM接口实例有CoCreateInstance、QueryInterface、AddRef等很多种方法,而释放一个COM接口实例一般是调用Release。
a.释放一个接口实例
释放一个接口实例比较简单,但是正确地释放接口实例却非常重要。如果不正确释放,就会导致内存泄漏或者程序崩溃。上面已经讲了,每一个接口都有IUnknown的三个函数:QueryInterface、AddRef、Release。在使用完一个实例之后,程序要调用此接口的Release成员函数释放该实例。Release会使得引用计数减一,并于引用计数归零时删除组件本身。每一个实例都需要调用一次Release:
p1->Release(); // p1 是程序所拥有的一个COM接口实例
b.获取一个接口实例
获取接口实例最通用的的方法,是使用CoCreateInstance函数。由于CoCreateInstance建立了新的接口实例,因此CoCreateInstance调用后,它会使组件的引用计数加一。
CoCreateInstance函数的使用方法如下:
HRESULT hr = CoCreateInstance(CLSID_组件ID, NULL, CLSCTX_ALL, IID_接口ID, (void**)&接口指针);
第一个参数是组件ID(CLSID),第四个参数是接口ID(IID),可以在相关头文件中找到它的常量定义。最后一个参数是返回值,用以接收CoCreateInstance返回的接口指针。
第二个参数(外层IUnknown指针)不常用,一般指定为NULL。第三个参数(组件环境)一般指定CLSCTX_ALL,表示可以接受任何形式的COM服务(进程内、进程外、远程都可以)。
获取相同组件相同接口的新实例的方法,是使用AddRef。它也会使得组件的引用计数加一。AddRef的用法如下:
IMyInterface *p2 = p1; // p2和p1指向相同的实例
p2->AddRef(); // 在p2建立IMyInterface新的实例,引用计数加一
获取相同组件任意接口的新实例的方法,是使用QueryInterface。它同样会使组件的引用计数加一。
HRESULT hr = p1->QueryInterface(IID_接口ID, (void**)&接口指针);
QueryInterface的两个参数和CoCreateInstance最后两个参数的含义相同。
3. 获取、使用和释放接口实例的实际操作
在这里我们将要调用的是Windows的文字转语音(Text-To-Speech)引擎,需要包含头文件#include <sapi.h>:
<code class="lang-cpp">#include <sapi.h></sapi.h></code>
在sapi.h中,文字转语音使用的接口是ISpVoice,其组件ID为CLSID_SpVoice,接口ID为IID_ISpVoice。ISpVoice的成员函数Speak可以让电脑开始说话。首先,我们要建立一个ISpVoice实例:
<code class="lang-cpp">// 建立ISpVoice接口实例(组件ID为CLSID_SpVoice,接口ID为IID_ISpVoice) ISpVoice *iSpVoice = NULL; hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void**)&iSpVoice); ValidateHR(hr);</code>
ISpVoice实例建立成功之后,我们就可以使用->操作符直接调用它的的Speak函数了:
<code class="lang-cpp">// 调用ISpVoice的Speak成员函数 iSpVoice->Speak(L"电脑在说话!", SPF_DEFAULT, NULL); ValidateHR(hr);</code>
最后,别忘了释放ISpVoice的实例!
<code class="lang-cpp">// 释放ISpVoice接口实例 iSpVoice->Release();</code>
最后的程序如下。把音量开到最大,按Ctrl+F5运行,即可听到电脑在说话![s::lol]
<code class="lang-cpp">#include <stdio.h> #include <stdlib.h> #include <windows.h> // 使用COM组件需要包含windows.h #include <sapi.h> // 使用ISpVoice语音组件需要包含sapi.h #define ValidateHR(hr) \ if (FAILED(hr)) { \ printf("HRESULT错误:%p,在%s第%d行\n", hr, __FILE__, __LINE__); \ exit(1); \ } int main() { HRESULT hr = CoInitialize(0); // 初始化COM环境,参数保留,必须为0 ValidateHR(hr); // 建立ISpVoice接口实例(组件ID为CLSID_SpVoice,接口ID为IID_ISpVoice) ISpVoice *iSpVoice = NULL; hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void**)&iSpVoice); ValidateHR(hr); // 调用ISpVoice的Speak成员函数 hr = iSpVoice->Speak(L"电脑在说话!", SPF_DEFAULT, NULL); ValidateHR(hr); // 释放ISpVoice接口实例 iSpVoice->Release(); CoUninitialize(); // 卸载COM环境 return 0; }</sapi.h></windows.h></stdlib.h></stdio.h></code>
200字以内,仅用于支线交流,主线讨论请采用回复功能。