×
Namespaces

Variants
Actions

Windows Phone上的声音录制与编码(三):Windows C++简介

From Nokia Developer Wiki
Jump to: navigation, search
WP Metro Icon Multimedia.png
SignpostIcon WP7 70px.png
Article Metadata

兼容于
文章
WS_YiLunLuo 在 01 Mar 2012 创建
最后由 hamishwillee 在 16 Jul 2013 编辑

Contents

简介

之前的两篇wiki介绍了如何在Windows Phone上录制声音,并且编码成wav格式,然后上传至服务器。接下来的一个话题很自然,就是在server上将wav文件编码成更常见的格式了,例如mp4。我们的这一系列文章假设大家在微软的平台上进行所有开发。在微软的Windows平台上,为了对视频和音频进行编码,有必要使用Media Foundation,并且用C++编写代码。考虑到很多Windows Phone开发人员都没有接触过C++,就算接触过也很可能没有针对Windows平台使用过C++进行开发,我们决定写一篇文章做一个简要介绍。但是想要学习更多相关知识,还是需要自行查阅相关资料。在本文的结尾会提供一些很不错的资源,不过绝大多数都是英文的。

注意本文介绍的是标准C++语言,不是Managed C++,也不是Windows 8上的用于开发WinRT程序的特殊C++(C++/CX)语法。那些特殊的C++语法是在标准C++基础上添加了一些功能,在需要时查找文档即可。

C++和C#

首先的一个话题是关于C++和C#的对比。它们都是C类语言,而且都支持面向对象的编程方式,所以有很多地方都十分的类似,但是也有不少区别。以下我们主要说明一些常用的功能,而不会涉及到一些诸如操作寄存器之类的非常少用的功能。

头文件

C++和C#很显著的一个区别在于头文件。在C#中,你就只有一种cs文件,可是在C++中,既有h文件(头文件),又有cpp文件。通常在h文件中定义类,以及类中的各种成员变量和方法,但是方法的实现则往往放在cpp文件中。

举个例子,在h文件中,你可以定义一个类和各种成员,但是并不实现任何的方法:

#pragma once
#include “Global.h”
class MyClass
{
private:
int m_count;
wstring m_name;
public:
void Test();
int Test(int value);
};

然后在cpp文件中,实现各种方法:

#include “MyClass.h”
MyClass::Test()
{
}
int MyClass::Test(int value)
{
return value + 1;
}

注意到在cpp文件的第一行,有一句#include “MyClass.h”,这意味着在编译时这个h文件会被嵌入到cpp文件的头上。也就是说h文件本身其实在编译时是没有用的,它的存在只是为了让你的代码实现与定义分离,编译器会自动帮你将h文件的代码复制到cpp的头上。你可能会觉得这样一来其实没有必要使用h文件,但是C++的编码规范告诉我们,cpp文件应该尽可能地只包含方法的实现。

如果你开发的是一个类库,你很可能会将h文件的代码直接公开,但是cpp文件的代码则不公开。使用你的类库的人,需要在自己的cpp文件中引用你的h文件,然后就可以使用其中各种功能了。同样,你要使用别人写的类库,也必须引用相应的h文件。此外,可能还需要引用一个lib文件,这个等一下我们再说明。

在有,有时候你可能会需要重复利用某一个h文件,例如实现一些全局的通用的方法。这时候要注意不能重复引用。例如:如果你在MyClass.h里面引用了Global.h,然后又在MyClass.cpp中同样引用了Global.h,就很可能出现问题,因为Global.h会被复制两次。为了防止这种情况的发生,可以使用#pragma once这个指令,通常将它写在h文件的开头。这个指令告诉编译器请不要将h文件复制多次到cpp文件中。

从上面的代码中你还可以看到,很多C#中的功能,例如类,成员,方法,重载,等等,在C++中都有对应的功能,但是语法可能有所不同。下面我们指出常见的语法区别。

语法的主要区别

就语发而言,其实很多语法上都只有细微的差异。例如:

  • C++中每个class结尾必须添加一个分号:
class MyClass
{
};
  • class本身不支持public private之类的定义
  • 对于成员变量而言,public private之类的定义可以同时定义多个变量:
private:
int m_count;
wstring m_name;
public:
void Test();
int Test(int value);

这意味着private:这一行下面所有的成员都是private的,而public:这一行下面所有的成员都是public的。

这边你还会注意到方法重载基本上和C#是完全一样的。

  • using 变成了using namespace:

using namespace std; 这意味着引用std这个namespace(C++标准类库提供的namespace)。

  • 构造函数:C++的构造函数支持特殊的语法为成员变量赋值:
MyClass() :
m_id(1),
m_name(L”Lante”)
{
}

当然,你在构造函数的方法体中直接写this->m_id = 1;也是可以的。

这边我们在字符串前加了一个L,表示这是一个Unicode字符串,不加的话就是ANSI字符串,不推荐使用。

  • throw可以throw任何类型,并不一定是Exception。事实上C++本身并没有Exception这个类,但你可以自己写。不过很多场合下都只是throw一个int,long之类的类型而以,代表错误代码。
  • Lambda Expression的写法有较大不同,但是概念和C#是一样的。限于篇幅原因,本文就不讨论了,毕竟我们在使用Media Foundation时很少会用到,不过它确实是一个很重要的功能。你可以自这里学习更多信息。
  • var关键字变成了auto。
  • null变成了nullptr(注意,早期版本的C++通常写作NULL,但是现在我们推荐使用nullptr,这样不仅写起来方便,不需要切换大小写输入,而且也不会和0搞错。NULL其实就是0,但nullptr是真正代表空的一个特殊的关键字)。
  • 数组的写法不同。在C++中,数组要这样写:

int myArray[2]

  • C++中没有foreach,你必须使用for循环。
  • C++中没有属性,你必须使用Get/Set方法。
  • C++中没有事件,也没有委托。但是有一个类似的功能叫做函数指针。
  • 全局函数:C++可以写全局函数,简单说来就是不在任何类中的函数(函数和方法其实是同一回事,只是在C++中,全局的方法通常被称作函数),它们可以在任何地方被调用。当然,像C#那样使用静态方法也是可以的。
  • 调用静态方法不用.,而用::。例如:

MyClass::MyStaticMethod();

而不像C#中那样:

MyClass.MyStaticMethod();

除此之外,还有很多的细小的区别,大家在使用C++时若使发现一个熟悉的C#语法报了错,请查找网上的相关资源,解决问题。

lib和dll

在C#程序中,你往往需要引用别人写的类库,或者.NET提供的类库。同样的任务也常常需要在C++程序中执行。不过C++程序和C#程序在这一点上有很大的不同,主要是因为C++程序并不是基于.NET的,因此添加assembly reference这一点并不适用。C++类库通常以两种方式给出:lib和dll。

一个lib文件是标准的C++类库,其引用添加方式和.NET中的assembly很像。在你的C++项目的属性页面中(右击Solution Explorer中的项目,选择属性),在Linker tab下,有一个Input选项。其中的Additional Dependencies就是用来添加lib引用的。你可以点击右边的小箭头,编辑引用。注意不像在C#程序中那样,Visual Studio会提供一个列表给你。你必须手工输入和中引用的名字。

LinkInput.png CppLibrary.png

上述截图中,我们添加了一些Media Foundation需要的lib文件的引用。

CppDirectories.png

默认情况下Visual Studio认识Windows SDK目录下的全部lib文件。如果你需要指定其它路径,可以在VC++ Directories下的Library Directories中找到。

除了lib类型的类库之外,还有一种类库是dll,它只能用于Windows平台上。一个lib文件在链接时会被完整地复制到你的应用程序中,因此会导致你的exe的大小变大。但是一个dll文件是动态链接的,它不会在链接时被复制到exe中,因此exe的体积可以减小。所谓的链接(link),你可以简单认为是编译的一部分。C++的编译被分成compile和link两个部分,这和.NET有很大的不同。不过,本文不会详细介绍C++的编译链接模型,因为这不是一篇文章能说完的。你可以在网上搜索c++ compile link获得相关信息。

此外,lib文件只能被C++程序使用,但dll文件也能被.NET程序使用。当然,dll也是有一定的缺点的,不过今天我们就不讨论那些了。

dll文件不需要引用,但是你的程序还是需要发现它。限于篇幅关系,本文就不再讨论如何在一个C++程序中使用dll文件了。你可以在这里找到很多很多的信息。等一会儿我们会引领大家使用C++创建一个dll类库,并且在一个C#程序中访问。

指针

C++中有一个比较重要的概念是指针。在C#中,有引用类型和值类型的区别。类是引用类型,当你在方法A中创建一个class的实例后,将该对象传到方法B中,这个对象不会被复制,而是和A中的对象是同一个实例。结构是值类型,当你在方法A中创建一个struct的实例后,将该对象传到方法B中,这个对象会被复制一份,B中的对象和A中的对象是不同的。

在C++中,class和struct基本上是等价的。事实上,不作特殊处理里的话,对象都是值类型。想要使用引用类型,你必需用指针。指针用*表示。

最简单的是用指针的方式是通过new创建一个对象的实例。在C++中,new返回的是一个指针。所以你要写:

MyClass* pMyClass = new MyClass();

注意不能少掉*。我们通常将指针对象命名为p什么什么,为的是区别于非指针对象。当然,名字是可以任意取的。

要访问指针所指向的对象的内容,需要使用->,而不是.例如:

pMyClass->name

而不是 pMyClass.name

大多数时候指针的用法和C#中的类是一样的,例如,你可以在MyMethod1中通过方法的参数将一个指针传递给另外一个方法:

void MyMethod2(MyClass* pMyClass)

在MyMethod2中的pMyClass,和之前创建的是同一个实例。你对pMyClass进行操作,例如将它的name的值改变,当你返回到MyMethod1,会发现pMyClass的name也改变了。

若是想要获得类似于C#中的struct的性质,你不能使用new。在C++中,你可以不通过new创建对象。例如:

MyStruct myStruct = MyStruct();

虽然我们的例子取名为MyStruct,但其实也可以用在类上。这样的写法中,myStruct正如C#中的struct那样,是一个值类型。每次将它作为参数传给另外一个方法(该方法必须接受值类型对象,而不是指针),都会对它进行一次复制。在方法B中改变副本的属性,方法A中的原本并不会受到影响。

以上,C++的指针和C#中的概念很类似,只是你必须显示写出指针而已。此外,指针也有一些相对高级一点的应用。例如,在C#中,为了在一个方法中改变值类型参数的值,你通常会使用ref关键字。

public void MyMethod(ref int i)

这样一来,虽然int是一个值类型对象,但是你在MyMethod中对它进行改变,在外面也会发生变化。在C++中,你通过指针实现类似的效果: void MyMethod(int* i)

在调用这个方法时,你通过&获取某个int对象的地址(指针):

int i = 0; MyMethod(&i);

在Media Foundation的代码中,你常常会看到**这样的类型。例如:

void MyMethod(MyClass** ppMyClass)

这个指的是MyMethod希望创建一个新的MyClass实例。你可以这样调用它:

MyClass* pMyClass = nullptr; MyMethod(&pMyClass);

这样一来,当MyMethod返回后,pMyClass就已经是一个真的对象,而不是nullptr了。

其实C#中也有类似的东西,比较常见的一个例子是WCF中的message inspector中的方法,例如:

object BeforeSendRequest(ref Message request, IClientChannel channel)

在这里,虽然Message是一个类,但是你在方法中可以为它创建一个实例,从而让调用这在方法返回后可以获得实例:

Message message = null; BeforeSendRequest(ref message, channel);

最后,指针和数组也可以混用。例如,你可以写myArray[2],获得myArray中的第二项,也可以写&myArray + 2,获取myArray的第二项的地址。

可以看到,很多人认为神秘的指针,其实在C#中都有对应的功能,只是写法不同罢了。其实C#中也可以直接用指针,如果你想写unsafe code的话。不过由于大多数场合下都有替代品,并不会需要直接使用指针。在C#中,只有少数场合下才会使用指针。例如,在WPF中想要使用WriteableBitmap操作像素,就必须用指针。

C++中还有一个概念叫宏(macro),它的作用是为一段代码或一个数字指定一个别名。例如:

  1. define DEFAULT_AGE 26

这意味着今后如果你在代码中使用DEFAULT_AGE,在编译时会被自动替换成26。宏通常用全大写字母命名,可以用来指定一个默认值,也可以用来指定一个GUID,通常要记住一个GUID是不太现实的,用宏给它一个别名,使用起来就方便多了。在Media Foundation中你会看到很多使用宏定义的GUID别名。

宏也可以用来为一段代码指定别名。例如:

  1. define TEST(value) if (value == true) { return true; } else { return false; }

之后你可以在代码中使用它:

TEST(myValue)

注意看上去宏和函数很像,但是是有不同的。在编译时,宏里面的代码会直接替换掉。因此你要非常小心,使用宏时如果发生了编译错误,将会很难定位。因此通常宏比较少用于给一段代码指定别名,只是用于给默认值或者GUID指定别名,那个不容易搞错的。

小结

事实上C++只是一种语言而已,和C#相比,它提供了一些特殊的功能,也少了一些功能,可是大多数功能都是一致的,只是语法不同。正好像Java和C#的关系那样,其实语言层面的区别并不是很至关重要的的。

真正的区别在于运行时,以及类库。下面我们就简单说一下这两点。

本机代码和.NET

所谓的本机代码(native code),有两种定义。第一,被直接编译成汇编的代码。这是相对于.NET这种将代码编译成中间语言,并且需要一个专门的运行时而定义的。第二,不是运行在浏览器中的程序,这是相对于web应用程序定义的。今天我们使用第一种定义。

编译方式

使用标准的C++,以及Windows 8中的C++/CX(用于开发WinRT程序),开发出来的都是本机代码。编译器直接将你的代码编译成汇编。因此它们不需要任何运行时,只需要有支持的硬件和操作系统,就可以运行了。

C++的编译稍微有点复杂,被分为编译和链接两个部分。不过这通常不是很重要,因此本文就不再描述了。

与此相对的是.NET,使用.NET语言(例如C#和managed C++)编写的代码被编译成一种中间语言,这个中间语言不能直接被运行,所以需要一个特定的运行时,也就是.NET runtime。运行时可以说是有好处也有坏处。.NET runtime非常强大,例如拥有垃圾回收机制,让你通常不用担心内存泄漏,而且做好了很多安全检查,等等。但是缺点就是可能会降低程序运行的效率,毕竟在操作系统和你的程序之间还隔了一层运行时。当然,通常有运行时还是利大于弊的,只是在写一些性能关键的代码时,你可能要考虑使用本机代码。

顺带一提,在WinRT中,C++代码虽然直接被编译成汇编,但是编译器同时也会生成一个和.NET兼容的metadata文件,告诉别人怎样使用这个组件。因此一个使用C++开发的WinRT组件也可以被.NET程序甚至是JavaScript程序使用。程序运行时并不需要这个metadata文件,只是在开发时才会用到。

内存管理

本机代码的一大缺点在于没有自动的内存管理机制,因此稍不注意就会导致内存泄漏。在C++中,你通过new创建出的对象会占用内存,而且系统不会自动释放掉这部分内存,你必须手工释放,也就是将该对象delete掉。例如:

MyClass* pMyClass = new MyClass(); delete pMyClass;

通常在某个方法中创建的对象,要直接在这个方法结束前调用delete。而对于类的成员,会在一个叫析构函数的地方统一清理。析构函数的语法是~加上类名:

~MyClass()
{
if (this->m_pMyObject != nullptr)
{
delete this->m_pMyObject;
}
}

注意到我们在这里还特意检查了一下m_pMyObject是不是空的,这是因为如果你尝试delete一个空的对象,会报错的。

此外,有时候你可能还会希望在delete某个对象之后直接将该指针赋值为nullptr,这样一来下次检查时由于已经是nullptr,就不会再次delete了。当然在析构函数中没有这个必要,毕竟析构函数是对象生命周期的最后阶段。

只有指针需要被delete,值类型的对象不需要。方法退出后,或者对象被销毁后,值类型的对象会自动被清空。

可以看到,你既不能忘记delete,又不能对同一个对象调用两次delete,这是C++中一个比较麻烦的地方。为了方便大家,各种各样的framework出现了。我们今天简单介绍一下ATL中的CComPtr。

CComPtr只能对COM对象使用(之后我们会说明什么是COM)。它被称作智能指针。简单说来就是帮你做好了delete的工作。它能确保你的对象肯定会被delete掉,同时又不会delete多次。虽然工作原理以及对象销毁的时机不同,可是效果和.NET的垃圾回收是类似的。

使用CComPtr很简单,它是一个范型类,你要将你自己的类型传递给它:

CComPtr<MyClass> pMyClass;

注意没必要将给对象赋值为nullptr,CComPtr会自动将它设置成nullptr。今后也没有必要(事实上也不允许)将它delete掉,一切都是自动的。

有关更多信息,请参考这里

在WinRT中,有一个类似的ComPtr,只能用于WinRT对象。

对于一般的C++对象,你可以使用STL中的shared_ptr以及unique_ptr智能指针。注意shared_ptr和unique_ptr不能用于COM和WinRT对象。

类库

本机C++使用的类库和.NET有很大的区别,基本上是互不兼容的。C++中常见的类库有:

  • STL:这是一个国际通用的类库,不是微软的。如果你的代码只使用了C++语言以及STL,是可以被轻松移植到非Windows平台上的。所以如果STL中间有相应的功能能满足你的需求,推荐优先考虑。STL包括了很多程序通用的功能,例如常见的数据结构和算法(虽然用法和.NET完全不同!),I/O操作,等等。但是它没有任何特殊的功能。例如,它不包括任何涉及到GUI的事项。你不能用它绘图,也不能进行电影和歌曲的编码。
  • WinRT:Windows 8专用的的一个类库,不能用于非Windows 8环境下。开发Windows 8的Metro程序,势必会用到它。即使是.NET和JavaScript程序,在后台都会用到WinRT。WinRT不仅仅提供了很多STL中没有覆盖到的基础功能,也让你可以调用各种系统组件。
  • MFC:这是一个被淘汰掉的类库,不过还是有不少人在使用。

除了以上这些提供了各种基础功能的类库之外,还有很多针对某个场景而开发的类库。例如Media Foundation就是专门针对电影和歌曲编码这个场景开发的,类似的,Direct2D是针对编辑2D矢量图形开发的,WIF和DirectComposition是针对编辑位图开发的,诸如此类。

有很多.NET类库提供的功能可能目前在C++中还没有现成的,例如,LINQ就没有。这时候你就只能寻找替代品,或者自己写了。同理,许多C++中的类库,在.NET中要么没有对应的功能,要么就需要使用非官方的wrapper。例如,Media Foundation就没有官方对.NET的支持,虽然第三方有人写过一些wrapper,让你可以在.NET程序中使用它。

小结

使用C++,你更多的困惑应该不在于语言,而在于熟悉的类库都没有了,必须使用全新的类库。还有就是需要自行管理内存。

使用C++开发Windows程序

接下来我们简要介绍一下如何使用C++开发Windows程序。本文不会介绍如何使用C++/CX和XAML开发Metro应用程序(这和Windows Phone程序其实是很像的),也不会介绍如何使用普通的C++开发Windows桌面程序。我们会将精力放在dll的开发以及COM的使用上,因为这正是在server环境中需要使用C++的场合。若是想要学习如何开发桌面版的C++程序,请参考Hilo系列教学。若是想要学习如何开发Metro C++程序,请先学好Windows Phone开发,然后你会发现很多概念都可以直接套用。

用C++开发dll

之前我们简单介绍过dll,现在来看看如何着手自己开发一个。

要开发dll,请在C++项目模板中选择Win32,然后是Win32 Project。

CreateCppProject.png

接下来你会看到一个向导,请点击Next,在这个界面上选择DLL以及Export Symbols。再点击Finish,项目就创建完成了。

DllProject.png

在自动生成的代码中,你会发现这样的代码:

// This is an example of an exported variable
MYTESTDLL_API int nMyTestDll=0;
 
// This is an example of an exported function.
MYTESTDLL_API int fnMyTestDll(void)
{
return 42;
}
// This class is exported from the MyTestDll.dll
class MYTESTDLL_API CMyTestDll {
public:
CMyTestDll(void);
// TODO: add your methods here.
};

其中的MYTESTDLL_API(根据你的项目名字不同这个名字也会不同)是一个宏,你可以在头文件中找到它的定义:

  1. define MYTESTDLL_API __declspec(dllexport)

这里的__declspec(dllexport)意味着nMyTestDll这个变量,fnMyTestDll这个函数,以及CMyTestDll这个类,将被导出(export),从而可以在其它程序中使用。

你可以定义更多的函数,类,以及变量,只要在前面加上MYTESTDLL_API,就会自动被导出。

此外,若是想要在.NET程序中调用这个dll,你必须对代码进行一些修改,将要导出的函数包括在一个extern “C”块中。

extern "C"
{
MYTESTDLL_API int fnMyTestDll(void);
}

创建dll其实就是这么简单,至于具体的实现,就看你的需求了,本文不再说明。

在C#中使用C++ dll

用C++开发的dll可以在其它C++程序中调用,但本文就不再描述了。同时你也可以在.NET程序中访问它们。

为了调用dll,首先必须找到它们,因为dll不像lib,在编译时不会被嵌入到exe中。程序在运行时通常会在两个地方寻找:当前目录以及win32目录。当然还有其它一些使用metadata之类的用法,我们就不说明了。这里假设大家希望将dll文件放到C#程序的输出目录下。为此,你需要将之前C++项目编译生成的dll作为一个existing item添加到C#项目中,并且将Copy to Output Directory设置成Copy if newer。

CopyToOutput.png

现在就可以使用标准的DllImport来使用C++ dll中的函数了。

        [DllImport("MyTestDll.dll", CharSet = CharSet.Unicode)]
private static extern int fnMyTestDll();
 
static void Main(string[] args)
{
Console.WriteLine(fnMyTestDll());
Console.ReadKey();
}

注意在DllImport中我们将CharSet指定为Unicode,这是推荐的作法,除非你用一个很老的不支持Unicode的类库。

再有要注意DllImport只支持全局函数,你不能在C#中使用C++ dll的类。想要使用类,需要通过其它途径,例如COM。

COM

最后我们要简单介绍一下COM。Windows的很多组件都是COM组件,包括Media Foundation在内,很多类库的使用都会需要COM。自己开发COM组件是比较麻烦的,但是好在如果只是调用系统自带的COM组件,会方便很多。

在使用COM之前,首先需要了解一下COM的错误处理机制,否则当你遇上错误时很可能会摸不着头脑。绝大多数的系统COM组件提供的方法都会返回一个HRESULT,它也是一个宏,事实上就是long,代表的是成功或错误代码。如果返回的是S_OK或者S_FALSE等几个特殊的值(也是宏,事实上是一个数值),就意味着方法运行成功了,若返回其它值,例如E_FAIL,就代表方法运行失败。

Windows提供了两个宏名叫FAILED和SUCCEEDED,用来判断一个返回值代表成功还是错误。推荐使用这两个宏,而不是自己检查S_OK,因为S_OK并不是唯一代表成功的代码。

之所以会用这种方式是有很多历史原因的,我们不能说这是一个很好的错误处理方式,但是必须接受。不过在我们自己的程序中,还是推荐使用try catch throw的方式比较好。所以我们可以写一个函数检查返回值是否代表成功。若是失败了,就将它throw出来:

inline void ThrowIfFailed(HRESULT hr)
{
if (FAILED(hr))
{
throw hr;
}
}
 
ThrowIfFailed(
SomeComMethodThatReturnsHRESULT()
);

之前说过,C++中throw的并不一定是Exception,这里我们就将HRESULT throw出来了。至于inline,指的是在编译时将这段函数的代码直接复制到调用者那里。这样一来运行时可以少调一个函数,稍微提高一点点性能。当然这也意味着生成的exe和dll会稍微变大一点点。事实上用不用inline并不是很重要啦,所以我们就不仔细说明了。

你可能在网上看到很多其它的错误处理方法,例如用一个宏,使用goto,一旦有了错误就goto到方法的最下面作一些清理然后退出,等等。但是我们推荐大家使用异常,因为这不仅仅是绝大多数.NET程序员熟悉的方式,当异常发生时也可以通过调试比较准确地定位发生的位置。当然要注意,如果异常发生了,你还是要确保所有的内存都被释放。使用CComPtr就不用担心内存了,但是我们也可能遇上不能使用CComPtr的地方,这时候需要小心行事。

说好了错误处理,就来看看怎样使用COM组件吧。首先需要初始化COM。这时候需要用到CoInitializeEx

ThrowIfFailed(

   CoInitializeEx(nullptr, COINIT_MULTITHREADED)

);

注意COM认为几乎所有的方法都可能出问题,所以一定要做好异常处理。上面的第一个参数通常就用nullptr,第二个参数视你的需求而定。若是想要指定COM组件可以在多个线程中共享,就用COINIT_MULTITHREADED吧,这也是Media Foundation需要的。

有些类库并不需要大家显示调用 CoInitializeEx。例如在Direct2D中,你可以直接使用D2D1CreateFactory创建一个Direct2D factory,这会在后台自动初始化COM。不过保险点还是显示调用比较好。

等一切和COM有关的事情完成后,需要调用CoUninitialize来关闭COM。这是少数几个不返回HRESULT的方法之一,因此不需要做异常处理。

初始化好COM之后就可以创建各种对象,调用各种方法了。具体的用法不同的类库都有所不同。例如,在Direct2D中,很多对象通过factory模式创建。你通过调用ID2D1Factory上的各种Create方法创建各种各样的对象。Media Foundation则提供了很多全局的函数帮助你创建对象。

有关COM今天我们就说到这里,下一篇wiki中在介绍Media Foundation时再让大家看看具体如何使用。

总结

使用C++开发Windows程序可以说简单也可以说难。本文只是简单介绍了一些基础知识。真正要写好程序,还是需要花时间去尝试去学习。下面我们提供一些资源:

This page was last modified on 16 July 2013, at 10:37.
186 page views in the last 30 days.