语音编程之Speech SDK概述(2)

来源:互联网 发布:突厥 唐太宗 知乎 编辑:程序博客网 时间:2024/05/29 15:35

如果需要关于方法运行结果的更详细的信息,必须测试每一个相关的HRESULT值。但经常只关心方法是成功的还是失败的。一种可靠的测试HRESULT类型值说明成功还是失败的方法是利用如下的宏来判断,这些宏定义在Winerror.h中。

1)宏SUCCEEDED返回TRUE作为成功码,返回FALSE作为失败码;

2)宏FAILED返回TRUE作为失败码,返回FALSE作为成功码;

可以使用宏FAILED来修改上面的代码段:

// hr 是该方法返回的HRESULT类型值

 if(FAILED(hr))

  {

    // 处理错误

  }

else

  {

    // 处理成功

  }

这段代码能合理地处理E_NOTIMPLE_INVALIDARG之类的失败码。

大多数的COM方法返回结构化的HRESULT类型值,只有很少数量的方法使用HRESULT来返回简单的整数值,这类方法经常是成功的。如果将这类整数值传给宏SUCCESS,该宏将总是返回TRUE。常用的例子是IUnknown::Release方法,它减少一次对象的引用计数并返回当前的引用计数。将在管理对象的生命期一节中讨论引用计数的问题。

4)指针地址

阅读一些COM方法的参考文档时,经常看到如下的说明:

HRESULT CreateDevice(

  .

  .

  .

  IDirect3DDevice8 **ppReturnedDeviceInterface

);

CC++开发人员熟悉普通的指针,但是COM经常使用另外的间接层。这种间接的第二层用两个星号(**)跟着类型声明来表示。变量名一般使用“pp”作为前缀。在上面的例子中,参数ppReturnedDeviceInterface表示指向IDirect3DDevice8 接口的指针的地址。

C++不同,不需要直接访问COM对象的方法,而必须获取指向方法的接口的指针。然后像调用指向C++方法的指针一样来调用方法。例如,使用如下的语法来调用方法IMyInterface::DoSomething method

IMyInterface *pMyIface;

.

.

.

pMyIface->DoSomething(...);

这样做的原因是我们并不直接创建接口指针,而是必须调用不同的方法(例如上述的CreateDevice)来创建接口指针。为了使用这种方法来获取接口指针,应声明一个指向需要的接口的指针变量,并将该指针变量的地址,即一个指针的地址,传递给该方法。当该方法返回时,该变量将指向你要求的接口,可以使用该指针来调用接口的任何方法。将在使用COM接口一节中更详细地讨论接口指针的问题。

2.创建COM对象

有多种方法可以创建COM对象。以下是在编程中最常用的两种方法。

1)直接法: 将对象的CLSID传给CoCreateInstance函数。该函数将创建对象的一个实例,并返回指向你所指定接口的指针。

2)间接法: 调用一个特殊的方法或函数来创建对象。这类方法创建对象并返回该对象的接口。使用这种方式来创建对象时,通常并不能指定需要返回的接口。

创建对象之前,必须调用CoInitialize函数来初始化COM。如果使用间接法来创建对象,对象的创建方法将自动完成COM初始化。如果使用CoCreateInstance来创建对象,则必须明确地调用CoInitialize 当完成了所有的COM工作后,必须调用CoUninitialize 来卸载COM。如果调用了CoInitialize,则必须对应地调用一次CoUninitialize。一般地说,需要明确,初始化COM的应用程序在其启动过程中初始化COM,在其清除过程中卸载COM

CoCreateInstance 来创建一个COM对象的实例需要使用该对象的CLSID。如果其CLSID是公开的,可以在其参考手册或相应的头文件中找到。如果其CLSID不是公开的,则不能使用直接法来创建该对象。

CoCreateInstance函数有5个参数,一般可以按如下方式来设置其参数。

1rclsid:将该参数设为需要创建的对象的CLSID

2pUnkOuter:将该参数设为NULL。只有在聚合对象时才需要使用该参数。参见关于聚合的讨论。

3dwClsContext:将该参数设为CLSCTX_INPROC_SERVER。该值说明对象是在DLL中实现的,将作为你的应用程序进程的一部分来运行。

4riid:将该参数设为需要返回的接口的IID。该函数将创建指定的对象,并通过参数ppv返回所请求的接口指针。

5ppv:将该参数设为riid所指定的接口的指针地址。该变量应该声明为一个指向请求的接口的指针。在参数表中,该参数应被强制为(LPVOID *)类型。

例如,下面的代码段创建了ISpVoice对象的一个新的实例,函数返回时,m_IpVoice 变量是指向ISpVoice接口的指针。如果发生错误,程序将终止,并显示一个消息框。

   CComPtr<ISpVoice> m_IpVoice = NULL;

 

HRESULT hr;

   hr = m_IpVoice.CoCreateInstance(CLSID_SpVoice);

 

   if (FAILED(hr))

   {

  AfxMessageBox("Error creating voice");

  return FALSE;

   }

用间接法创建对象往往简单得多。只要将接口指针的地址传给对象的创建方法,该方法就会创建对象并返回接口指针。但间接地创建对象时,一般不能选择返回哪个接口。但是可以指定如何来创建对象。

3IUnknown接口

所有的COM对象都支持一个叫做IUnknown的接口。该接口提供了对对象的生命期的控制和检索对象的其他接口的方法。IUnknown接口有以下3个方法。

1AddRef:当一个接口或另一个应用程序与对象绑定时,对对象的引用计数加1

2QueryInterface:查询对象所支持的功能,并请求指向指定的接口的指针。

3Release:对对象的引用计数减1。当引用计数变为0时,对象将被释放。

AddRefRelease方法维护对象的引用计数。例如,当创建一个对象时,该对象的引用计数变为1。每次一个函数返回一个指向该对象的接口的指针时,该函数都必须调用AddRef来增加其引用计数。AddRef的每一次调用都必须与Release的调用相匹配。在指针被释放前必须对该指针调用Release。当一个对象的引用计数变为0时,该对象将被释放,它的所有接口都将变为无效接口。

QueryInterface方法用来确定一个对象是否支持指定的接口。如果一个对象支持一个接口,QueryInterface返回一个指向该接口的指针。然后就可以使用该接口的方法来与对象进行通信。如果QueryInterface成功地返回一个指向接口的指针,它将明确地调用AddRef来增加引用计数。因此在释放该接口的指针之前,应用程序必须调用Release来减少引用计数。

4.使用COM接口

获得接口指针后,可以用该指针来访问接口的任何方法。

在许多情形中,从创建方法接收到的接口指针就是所需要的。实际上,只暴露一个除IUnknown之外的接口的对象是很不常见的。相反,许多的对象暴露多个接口,需要指向这些接口的多个指针。如果需要创建方法所返回的接口之外的更多的接口,则并不需要再创建一个新的对象。可以使用对象的IUnknown::QueryInterface 方法来请求其他接口的指针。

如果使用CoCreateInstance来创建对象,则可以请求一个IUnknown接口指针,然后调用IUnknown::QueryInterface方法来请求需要的每一个接口。然而,当只需要一个接口时,这种方法显得很不方便。而且,如果使用不允许指定哪个接口应该返回的创建方法时,这种方法更不能工作。在实践中,经常不需要获得一个明确的IUnknown指针,因为所有的COM接口都是从IUnknown接口继承或扩展而来的。

扩展一个接口很像继承一个C++类。子接口暴露父接口的所有方法,并增加一个或多个自己的方法。事实上,经常看见“继承”而不是“扩展”。需要记住的是,继承只能出现在对象的内部。应用程序不能继承或扩展另一个对象的接口,但是可以使用子接口来调用它自己或其父接口的方法。

由于所有接口都是IUnknown的子接口,因此可以使用已经获得的任何该对象接口的指针来调用QueryInterface。这样做时,需要提供你请求的接口的IID和当方法返回时存放接口指针的指针。

原创粉丝点击