什么是 Direct X Virtual Table?
What is a Direct X Virtual Table?
我有一个关于虚拟 table 的问题。
我所知道的虚拟table是可以找到的函数地址数组
多态对象调用虚函数时的函数地址。
但是在directx中,有人提到dx vtable table of d3d9.dll
的导入函数地址数组。
为什么调用导入函数地址数组到directx vtable?
它似乎与 vtable.
无关
难道我的知识有误?我可以详细了解 vtable 吗?
谢谢!
从 DLL 导出 COM object 时,通常有两个部分在起作用。首先是 'factory'。工厂可以是从 DLL 导出的标准 C-callable 函数(Direct3D 就是这种情况),或者是在系统注册表中注册的 class,在这种情况下,您将使用 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 接口 object。包含 D3D11CreateDevice
的 DLL 必须隐式链接到调用程序——或者您可以使用 LoadLibrary
进行显式链接,在这种情况下您将使用指向函数 D3D11CreateDevice
的指针这几乎是一回事。这是从 DLL 的 'import table' 派生的。
第二个示例是您在使用 Windows 成像组件 (WIC) 时所做的事情:
IWICImagingFactory* factory = nullptr;
hr = CoCreateInstance(
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
__uuidof(IWICImagingFactory),
&factory);
这让 COM 系统在注册表中查找 class GUID,加载引用的 DLL,然后调用其中的工厂方法来创建一个 COM 接口 object return送给你。
在这两种情况下,接口指向的实际内容都是以下形式:
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;
设计为 完全 映射到 virtual
:
的 Visual C++ 实现
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
};
COM 并没有真正的继承概念,但它再次为 ID3D11Device1
使用 C++ public 从 ID3D1Device
继承提供了便利,这只是因为语言类型具有没有声明数据成员。接口的 COM "inheritance" 实际上只是方法的串联,如果您查看 headers:
中的 C 定义,您可以看到这些方法
#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
It's important to note this only works for the case of pure virtual (i.e. abstract) C++ classes with no data members and only virtual methods using public inheritance (i.e. interfaces). Constructors and destructors are ignored as COM life-time is managed through reference-counting via IUnknown
.
方法的调用签名也将指向 COM object 的指针作为映射到 this
.
的 C++ 调用约定的第一个参数
COM 接口因此设计为像 C++ 虚拟方法一样工作,因此您可以使用 C++ 语法调用它们,但它们不一定是 C++ class objects全部.
从 IUnknown
开始,在 COM 中有一个已知的标准方法来获取特定接口,即 QueryInterface
。 Direct3D 工厂函数也为您处理这件事,只是 return 基本接口。对于 Direct3D 11,这是 ID3D11Device
.
If you want to get an interface to say 11.1, you then use QueryInterface
on the ID3D11Device
asking for a ID3D11Device1
. It would fail on a DirectX 11.0 system, but works on a DirectX 11.1 or later system.
对于使用 Direct3D 的 C++ 程序员,设计有意将 "COM" 保持在最低限度(俗称 "COM lite")。真正使用足够的 COM 可以更轻松地处理随时间变化的接口并提供合理的 ABI。 Direct3D 工厂函数是从已知 DLL 导出的简单 C-callable,因此您甚至不必处理标准 COM 工厂,事实上,如果您尝试 API 则无法正常工作使用 CoCreateInstance
。从技术上讲,您可以通过标准 MIDL compiler 与 C++ 定义一起生成的一些宏来使用 C,但这有点具有挑战性,而且目前还没有经过特别好的测试。
作为 Direct3D COM 的使用者,您真正需要知道的只是 IUnknown
引用计数和查询的基础知识——今天最好使用 Microsoft::WRL::ComPtr smart-pointer--and how to properly check HRESULT
values--see ThrowIfFailed.
See The Component Object Model and Reference Counting (Direct3D 10)
我有一个关于虚拟 table 的问题。
我所知道的虚拟table是可以找到的函数地址数组 多态对象调用虚函数时的函数地址。
但是在directx中,有人提到dx vtable table of d3d9.dll 的导入函数地址数组。
为什么调用导入函数地址数组到directx vtable? 它似乎与 vtable.
无关难道我的知识有误?我可以详细了解 vtable 吗? 谢谢!
从 DLL 导出 COM object 时,通常有两个部分在起作用。首先是 'factory'。工厂可以是从 DLL 导出的标准 C-callable 函数(Direct3D 就是这种情况),或者是在系统注册表中注册的 class,在这种情况下,您将使用 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 接口 object。包含 D3D11CreateDevice
的 DLL 必须隐式链接到调用程序——或者您可以使用 LoadLibrary
进行显式链接,在这种情况下您将使用指向函数 D3D11CreateDevice
的指针这几乎是一回事。这是从 DLL 的 'import table' 派生的。
第二个示例是您在使用 Windows 成像组件 (WIC) 时所做的事情:
IWICImagingFactory* factory = nullptr;
hr = CoCreateInstance(
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
__uuidof(IWICImagingFactory),
&factory);
这让 COM 系统在注册表中查找 class GUID,加载引用的 DLL,然后调用其中的工厂方法来创建一个 COM 接口 object return送给你。
在这两种情况下,接口指向的实际内容都是以下形式:
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;
设计为 完全 映射到 virtual
:
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
};
COM 并没有真正的继承概念,但它再次为 ID3D11Device1
使用 C++ public 从 ID3D1Device
继承提供了便利,这只是因为语言类型具有没有声明数据成员。接口的 COM "inheritance" 实际上只是方法的串联,如果您查看 headers:
#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
It's important to note this only works for the case of pure virtual (i.e. abstract) C++ classes with no data members and only virtual methods using public inheritance (i.e. interfaces). Constructors and destructors are ignored as COM life-time is managed through reference-counting via
IUnknown
.
方法的调用签名也将指向 COM object 的指针作为映射到 this
.
COM 接口因此设计为像 C++ 虚拟方法一样工作,因此您可以使用 C++ 语法调用它们,但它们不一定是 C++ class objects全部.
从 IUnknown
开始,在 COM 中有一个已知的标准方法来获取特定接口,即 QueryInterface
。 Direct3D 工厂函数也为您处理这件事,只是 return 基本接口。对于 Direct3D 11,这是 ID3D11Device
.
If you want to get an interface to say 11.1, you then use
QueryInterface
on theID3D11Device
asking for aID3D11Device1
. It would fail on a DirectX 11.0 system, but works on a DirectX 11.1 or later system.
对于使用 Direct3D 的 C++ 程序员,设计有意将 "COM" 保持在最低限度(俗称 "COM lite")。真正使用足够的 COM 可以更轻松地处理随时间变化的接口并提供合理的 ABI。 Direct3D 工厂函数是从已知 DLL 导出的简单 C-callable,因此您甚至不必处理标准 COM 工厂,事实上,如果您尝试 API 则无法正常工作使用 CoCreateInstance
。从技术上讲,您可以通过标准 MIDL compiler 与 C++ 定义一起生成的一些宏来使用 C,但这有点具有挑战性,而且目前还没有经过特别好的测试。
作为 Direct3D COM 的使用者,您真正需要知道的只是 IUnknown
引用计数和查询的基础知识——今天最好使用 Microsoft::WRL::ComPtr smart-pointer--and how to properly check HRESULT
values--see ThrowIfFailed.
See The Component Object Model and Reference Counting (Direct3D 10)