分享
 
 
 

DirectX8.1 01-使用COM

王朝other·作者佚名  2006-02-01
窄屏简体版  字體: |||超大  

这是我翻译的DirectX8.1SDK文档,第一次翻译,有些词汇翻译的不太准。

1. 什么是COM对象

COM对象同C++对象的不同:

A. COM对象比C++对象有强制性的更严格的封装。COM对象的方法被一个或多个接口组织起来。要使用一个方法,必须创建一个对象并且从对象中得到合适的接口。

B. COM对象不像C++对象那样创建。有几种方式创建COM对象,但都使用了特定的COM技术。

C. 必须通过特定的COM技术控制COM对象的生存期。

D. COM对象不需要显式地加载。

E. COM是一种二进制规范。

对象与接口:

A. 一个对象可以展示出任意数目的接口。

B. 多个对象可能展示出同样的接口。

COM对象要求接口的定义在其被发布后就不能被改变。

GUIDs,COM大量使用GUID是为了:

A. 标志一个唯一的特定COM对象。

B. 标志一个唯一的特定接口。

HRESULT,所有的COM方法都返回一个32位整数HRESULT。HRESULT中包含了两个信息:

A. 方法成功还是失败。

B. 方法提供的操作的结果的详细信息。

应该用SUCCESS / FAILED 宏来进行方法返回值的判断。

指针的地址

IDirect3DDevice8** ppReturnedDeviceInterface

不同于C++,你不能直接访问COM对象方法。取而代之,应该取得一个展示了该方法的接口指针。

二重指针的原因是你不能直接创建接口指针。必须调用某种Create方法取得接口指针。

声明一个变量指向该接口,并将改变量的地址传递给Create方法。

2. 创建COM对象

有几种方法创建COM对象,最常用的两种是:

A. 通过给函数CoCreateInstance传递对象的CLSID直接创建。该函数创建一个对象的实例,并且返回所指定的接口指针。

B. 通过调用DirectX创建对象的方法或函数间接创建。该方法创建一个对象并且返回对象上的一个接口指针。如果通过这种方法创建对象的话,通常不能指定返回哪个接口指针。

在创建对象之前,必须调用CoInitialize对COM初始化。如果间接创建对象,由对消创建方法处理初始化。如果需要通过CoCreateInstance创建对象,则必须显式调用CoInitialize。当结束的时候,必须通过调用CoUninitialize进行卸载(uninitialize)。如果调用了CoInitialize则必须调用CoUninitialize进行匹配。典型地,应用程序在启动过程中显式初始化COM,并在清理过程中卸载COM。

直接创建:

IDirectPlay8Peer* g_pDP = NULL;

...

CoInitialize(NULL );

...

hr = CoCreateInstance( CLSID_DirectPlay8, NULL, CLSCTX_INPROC_SERVER,

IID_IDirectPlay8Peer, (LPVOID*) &g_pDP);

if (FAILED (hr))

{

MessageBox (NULL, TEXT ("Failed Creating IDirectPlay8Peer. "),

TEXT ("DirectPlay Sample"), MB_OK | MB_ICONERROR);

return FALSE;

}

间接创建:

IDirect3DDevice8 *g_pd3dDevice = NULL;

...

if (FAILED (g_pD3D->CreateDevice(D3DADAPTER_DEFAULT,

3DDEVTYPE_HAL,

hWnd,

D3DCREATE_SOFTWARE_VERTEXPROCESSING,

&d3dpp,

&g_pd3dDevice)))

return E_FAIL;

3. 使用COM接口

当对象创建后,创建函数返回一个接口的指针。可以通过这个指针访问接口的方法。语法上与通过指针访问C++方法一样。

IDirectPlay8Peer* g_pDP = NULL;

...

CoInitialize (NULL);

...

hr = CoCreateInstance ( CLSID_DirectPlay8, NULL, CLSCTX_INPROC_SERVER,

IID_IDirectPlay8Peer, (LPVOID*) &g_pDP);

hr = g_pDP->Initialize( NULL, DirectPlayMessageHandler, 0 );

请求另外的接口

在许多时候,从创建方法得到的接口指针正好是你所需要的。事实上,从一个对象只导出一个IUnknown以外的接口并不常见。许多对象导出多个接口,你有可能需要其中的一些。如果你需要的不仅仅是从创建方法返回的那一个接口的话,并不需要创建一个新对象。取而代之的是使用对象的IUnkonwn::QueryInterface方法请求其他的接口指针。

如果使用CoCreateInstance创建对象,可以申请一个IUnknown接口指针,并通过调用IUnkonwn::QueryInterface申请需要的接口。然而,这种方法当只需要一个接口的时候并不方便,并且在创建不允许指定返回特定指针的时候一点也没有用。实际上,并不需要显式的包含一个IUnknown接口指针,因为所有COM接口都继承了或扩展了IUnknown接口。

扩展一个接口类似于继承一个C++类。子接口展示了所有父接口的方法,并且加上了一个或多个自己的方法。事实上,经常看到用“继承自”代替“扩展”。需要记住的是继承是内嵌在对象中的。你的程序不能继承或扩展一个对象的接口。然而,可以用一个子接口调用子或父的方法。

因为所有的接口都是IUnknown的子接口,你可以用已经拥有的对象的任何一个接口调用QueryInterface。这个时候,必须提供所申请的接口的IID以及一个接口指针的地址。

IDirectSoundBuffer8* pDSBPrimary = NULL;

IDirectSound3DListener8* pDSListener;

...

if(FAILED(hr = g_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL )))

return hr;

if(FAILED(hr = pDSBPrimary->QueryInterface(IID_IDirectSound3DListener8,

(LPVOID *)&pDSListener)))

return hr;

4. 管理一个COM对象的生存期

当创建好一个对象时,系统分配给必要的内存资源。当不再需要对象时,应该将其销毁掉(destroy)。系统就能将那些内存用在别的地方。对C++对象而言,可以直接通过new和delete操作符控制对象的生存期。COM不允许直接创建或销毁对象。原因是同一个对象可能被许多程序使用。如果一个程序破坏了一个对象,其他的程序可能就会失败。取而代之的是COM用一套引用计数的系统控制对象的生存期。

一个对象的引用计数是该对象的接口被申请的次数。每当一个接口被申请的时候,就增加引用计数。当应用程序不再需要接口的时候释放该接口,就减少引用计数。只要引用计数大于零,对象就保留在内存中。当引用计数为零时,对象销毁自身。你无需知道一个对象的引用计数。只要适当的取得或释放接口,对象就有合适的生存期。

注意:适当的处理引用计数是COM编程中很重要的一部分。处理得不好很容易导致内存泄露。COM程序员最常见的错误就是没有释放接口。这样的话,引用计数永远不会为零,对象就会一直驻留在内存中。

增加或减少引用计数

当取得一个新的接口指针的时候,必须通过调用IUnknown::AddRef来增加引用计数。然而,你的应用程序无需调用这个方法。当通过一个对象创建方法,或是通过IUnknown::QueryInterface取得对象接口指针时,对象会自动增加引用计数。然而,当你通过别的方法创建接口指针的时候,比如说拷贝一个接口指针,必须显式调用IUnknown::AddRef。否则,当你释放原始指针的时候,尽管你可能仍需使用拷贝指针,对象也可能被销毁。

IDirectSoundBuffer8* pDSBPrimary = NULL;

IDirectSound3DListener8* pDSListener = NULL;

IDirectSound3DListener8* pDSListener2 = NULL;

...

//Create the object and obtain an additional interface.

//The object increments the reference count.

if(FAILED(hr = g_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL )))

return hr;

if(FAILED(hr=pDSBPrimary->QueryInterface(IID_IDirectSound3DListener8,

(LPVOID *)&pDSListener)))

return hr;

//Make a copy of the IDirectSound3DListener8 interface pointer.

//Call AddRef to increment the reference count and to ensure that

//the object is not destroyed prematurely

pDSListener2 = pDSListener;

pDSListener2->AddRef();

...

//Cleanup code. Check to see if the pointers are still active.

//If they are, call Release to release the interface.

if(pDSBPrimary != NULL)

{

pDSBPrimary->Release();

pDSBPrimary = NULL;

}

if(pDSListener != NULL)

{

pDSListener->Release();

pDSListener = NULL;

}

if(pDSListener2 != NULL)

{

pDSListener2->Release();

pDSListener2 = NULL;

}

5. 使用C访问COM对象

尽管C++是最常用的COM编程语言,你仍然可以使用C访问COM对象。通过相对直接但是需要某些更加复杂的语法。

A. 在所有方法的参数列表的开始要添加一个额外的参数。该参数必须设成接口指针。

B. 必须显式引用对象的vtable。

每一个COM对象都有一个vtable,包含了对象展示的方法指针的列表。一个接口指针指向vtable中合适的位置,vtable中依次包含了指向所调用的方法的指针。Vtable在文档的其他地方并没有被提到,因为对C++而言,vtable是不可见的。然而,如果要通过C调用COM方法,必须包含一个显式引用vtable的间接层。

C++:

g_pDP->Initialize( NULL, DirectPlayMessageHandler, 0 );

C:

g_pDP->lpVtbl->Initialize(g_pDP,NULL, DirectPlayMessageHandler, 0);

某些组件在其头文件中包含了一些宏定义,用于决定正确的调用约定。详情请参阅“使用宏调用DirectX COM方法”。

6. 使用宏调用DirectX COM对象

许多Microsoft® DirectX®接口都为每个方法定义了宏,使在应用程序使用这些方法更方便。可以在接口声明的同一个头文件中找到这些宏的定义。这些宏设计成被C和C++程序使用。要使用C++宏,必须定义_cplusplus。否则,将使用C宏。宏的语法在两种语言中是一样的,但是在头文件中包含了扩展成合适的调用约定的独立的宏定义集。

#if !defined(__cplusplus) || defined(CINTERFACE)

...

#define IDirect3D8_GetAdapterIdentifier(p,a,b,c) (p)->lpVtbl->GetAdapterIdentifier(p,a,b,c)

...

#else

...

#define IDirect3D8_GetAdapterIdentifier(p,a,b,c) (p)->GetAdapterIdentifier(a,b,c)

...

#endif

要使用这些宏中的一个,首先必须取得关联的接口的指针。宏的第一个参数必须设置为该指针。其他的参数影射到方法的参数。宏的返回值为方法所返回的HRESULT值。

pD3D为IDirect3D8接口指针:

hr = IDirect3D8_GetAdapterIdentifier(pD3D,

Adapter,

dwFlags,

pIdentifier);

7. DirectX COM文档约定

根据约定,在本文档中提到的COM方法及接口都是通过相应的C++类名来引用。因此,接口IDirectPlay8Peer的Initialize方法为IDirectPlay8Peer::Initialize。这种约定的主要原因是,不同的接口可能导出同样名字但是功能及语法完全不同的方法。比如,许多接口都有Init或Initialize方法,但是功能以及参数可能十分不同。使用C++类名是一种唯一标识方法的捷径。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有