什么是Direct X虚拟表?

时间:2017-07-20 06:51:27

标签: c++ directx

我对虚拟桌有疑问。

我知道的虚拟表是可以找到的函数地址数组 当多态对象调用虚函数时的函数地址。

但是在directx中,有人提到dx vtable表d3d9.dll 导入函数地址数组。

为什么他们将导入函数地址数组调用为directx vtable? 它似乎与vtable无关。

我的知识不正确吗?我可以详细了解vtable吗? 谢谢!

1 个答案:

答案 0 :(得分:4)

从DLL导出COM对象时,通常有两个部分在起作用。首先是工厂'。工厂可以是从DLL导出的标准C可调用函数(Direct3D就是这种情况),或者是在系统注册表中注册的类,在这种情况下,您使用CoCreateInstance来创建COM接口实例。第一个示例是创建Direct3D设备:

ID3D11Device* d3dDevice = nullptr;

hr = D3D11CreateDevice(
    nullptr,
    D3D_DRIVER_TYPE_HARDWARE,
    0,
    0,
    nullptr,
    0,
    D3D11_SDK_VERSION,
    &d3dDevice,
    nullptr,
    nullptr);

在此调用之后,d3dDevice接口指向已分配的COM接口对象。包含D3D11CreateDevice的DLL必须隐式链接到调用程序 - 或者您可以使用LoadLibrary进行显式链接,在这种情况下,您使用指向函数{{1这相当于一件事。这来自“导入表”。对于DLL。

第二个示例是使用Windows Imaging Component(WIC)时所执行的操作:

D3D11CreateDevice

这让COM系统在注册表中查找类GUID,加载引用的DLL,然后在那里调用工厂方法来创建一个它返回给你的COM接口对象。

在这两种情况下,界面指向的实际内容都是以下形式:

IWICImagingFactory* factory = nullptr;
hr = CoCreateInstance(
         CLSID_WICImagingFactory,
         nullptr,
         CLSCTX_INPROC_SERVER,
         __uuidof(IWICImagingFactory),
         &factory);

设计为完全映射到typedef struct IUnknownVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IUnknown * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IUnknown * This); ULONG ( STDMETHODCALLTYPE *Release )( IUnknown * This); END_INTERFACE } IUnknownVtbl; 的Visual C ++实现:

virtual

COM确实没有继承的概念,但MIDL_INTERFACE("00000000-0000-0000-C000-000000000046") IUnknown { public: BEGIN_INTERFACE virtual HRESULT STDMETHODCALLTYPE QueryInterface( /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject) = 0; virtual ULONG STDMETHODCALLTYPE AddRef( void) = 0; virtual ULONG STDMETHODCALLTYPE Release( void) = 0; END_INTERFACE }; 使用来自ID3D11Device1的C ++公共继承也很方便,因为语言类型没有数据成员声明。 COM"继承"接口实际上只是方法的串联,如果你查看标题中的C定义,你可以看到这些方法:

ID3D1Device
  

值得注意的是,这仅适用于纯虚拟(即抽象)C ++类,没有数据成员,只有虚拟方法使用公共继承(即接口)。由于COM生命期通过#if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("cc86fabe-da55-401d-85e7-e3c9de2877e9") ID3D11BlendState1 : public ID3D11BlendState { public: virtual void STDMETHODCALLTYPE GetDesc1( /* [annotation] */ _Out_ D3D11_BLEND_DESC1 *pDesc) = 0; }; #else /* C style interface */ typedef struct ID3D11BlendState1Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( /* ... */ ); ULONG ( STDMETHODCALLTYPE *AddRef )( ID3D11BlendState1 * This); ULONG ( STDMETHODCALLTYPE *Release )( ID3D11BlendState1 * This); void ( STDMETHODCALLTYPE *GetDevice )( /* ... */ ); HRESULT ( STDMETHODCALLTYPE *GetPrivateData )( /* ... */ ); HRESULT ( STDMETHODCALLTYPE *SetPrivateData )( /* ... */ ); HRESULT ( STDMETHODCALLTYPE *SetPrivateDataInterface )( /* ... */ ); void ( STDMETHODCALLTYPE *GetDesc )( ID3D11BlendState1 * This, /* [annotation] */ _Out_ D3D11_BLEND_DESC *pDesc); void ( STDMETHODCALLTYPE *GetDesc1 )( ID3D11BlendState1 * This, /* [annotation] */ _Out_ D3D11_BLEND_DESC1 *pDesc); END_INTERFACE } ID3D11BlendState1Vtbl; #endif 引用计数来管理,因此忽略构造函数和析构函数。

方法的调用签名还将指向COM对象的指针作为映射到IUnknown的C ++调用约定的第一个参数。

COM接口因此设计为像C ++虚拟方法一样工作,因此您可以使用C ++语法来调用它们,但它们根本不是C ++类对象。

this开始,COM中有一种已知的标准方法来获取特定接口,即IUnknown。 Direct3D工厂功能也为您完成这项工作,并返回基本接口。对于Direct3D 11,这是QueryInterface

  

如果您想要一个说11.1的界面,那么您可以在ID3D11Device上使用QueryInterface,要求ID3D11Device。它在DirectX 11.0系统上会失败,但在DirectX 11.1或更高版本的系统上运行。

对于使用Direct3D的C ++程序员,该设计故意保留" COM"至少(通俗地称为" COM lite")。真正足够的COM用于使随着时间的推移更容易处理接口更改并提供合理的ABI。 Direct3D工厂函数是来自已知DLL的简单C可调用导出,因此您甚至不必使用标准COM工厂进行调试,事实上,如果您尝试使用API​​,API不能正常工作ID3D11Device1。从技术上讲,您可以通过标准MIDL compiler生成的一些宏以及C ++定义来使用C,但这样做有点难度并且目前还没有经过特别好的测试。

作为Direct3D COM的消费者,您真正需要知道的是CoCreateInstance引用计数和查询的基础知识 - 今天最好使用Microsoft::WRL::ComPtr智能指针 - 以及如何要正确检查IUnknown值,请参阅ThrowIfFailed

  

请参阅The Component Object ModelReference Counting (Direct3D 10)