×
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.
178 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×