-
Notifications
You must be signed in to change notification settings - Fork 517
ComPtr
Microsoft::WRL::ComPtr
is a C++ template smart-pointer for COM objects that is used extensively in Windows Runtime (WinRT) C++ programming. It works in Win32 desktop applications as well. It is similar to ATL's CComPtr
with some useful improvements. Microsoft::WRL:::ComPtr
is in the Windows 8.x SDK and Windows 10 SDK, which, unlike ATL, is available when using the Express versions of Visual Studio. It is used extensively in DirectX Tool Kit to properly handle COM reference counting maintenance.
#include <wrl.h>
// or
#include <wrl/client.h>
In keeping with C++ best practice, you should use fully-qualified names in .h
header files.
Microsoft::WRL::ComPtr<T> variable;
In .cpp
source files, you can add the following to your module to make it less verbose to use ComPtr:
using Microsoft::WRL::ComPtr;
ComPtr<T> variable;
Whenever you need to obtain a raw pointer from a ComPtr such as calling a Direct3D function that takes a *
(i.e. a pointer), use Get()
ComPtr<ID3D11RasterizerState> rasterState;
device->RSSetState( rasterState.Get() );
If you want to know why we have to use
.get()
and.Get()
to convert smart-pointers to standard 'raw' pointers, see this post
A number of Direct3D APIs actually take an array of pointers to COM objects because they can be passed one or more objects at once. When you have just one to pass, you'll often see GetAddressOf() used because you are passing to a function parameter that is **
(i.e. pointer-to-a-pointer). Because operator&
maps to ReleaseAndGetAddressOf(), when calling Direct3D functions that take a pointer to an array of inputs and you are passing a single pointer, use the explicit GetAddressOf() method instead:
context->OMSetRenderTargets(1, m_renderTargetView.GetAddressOf(),
m_depthStencilView.Get());
// or
auto rt = m_renderTargetView.Get();
context->OMSetRenderTargets(1, &rt, m_depthStencilView.Get());
For example, if you used context->OMSetRenderTargets(1, &m_renderTargetView, m_depthStencilView.Get());
the m_renderTargetView
variable would get released before the call to OMSetRenderTargets
and the result would likely crash.
If building an array of Direct3D interface pointers, you should build one as a local variable:
ID3D11SamplerState* samplers[] = { sampler1.Get(), sampler2.Get() };
context->PSSetSamplers(0, _countof(samplers), samplers);
Generally, if you are creating a fresh ComPtr and then using a COM creation function or factory, you can use GetAddressOf() since you know the ComPtr is initially null.
ComPtr<ID3D11Device> device;
hr = D3D11CreateDevice( ..., device.GetAddressOf(), ... );
// or
hr = D3D11CreateDevice( ..., &device, ... );
If you are using a ComPtr as a class member, then you should use ReleaseAndGetAddressOf() to ensure any existing object reference is properly released. The overload for operator&
does the same thing as ReleaseAndGetAddressOf(), but the explicit use of the method name is more clear in code.
ComPtr<ID3D11Device> m_device;
hr = D3D11CreateDevice( ..., m_device.ReleaseAndGetAddressOf(), ... );
// or
hr = D3D11CreateDevice( ..., &m_device, ... );
Note that ATL's CComPtr asserted that the object was always null for operator&
, which had a potential to cause resource leaks.
Keep in mind is that passing ComPtr variables by value will result in incrementing/decrementing the reference count. You can avoid this by either passing the smart-pointer by const reference or passing a raw pointer instead. Raw pointers are generally the more flexible option, but require the use of Get() at the call-site.
void func( const Microsoft::WRL::ComPtr<T>& param );
// or
void func( T* param );
When passing ComPtr variables to constructors, you usually want the new class to increase the reference count, which is handled automatically by assigning the raw pointer to a new ComPtr<T>
or by copying one ComPtr<T>
to another.
ComPtr provides a much simpler syntax for doing QueryInterface
calls on COM Objects
ComPtr<ID3D11Device> device;
hr = D3D11CreateDevice( ..., device.GetAddressOf(), ... );
if (SUCCEEDED(hr))
// device created
ComPtr<ID3D11Device1> device1;
hr = device.As( &device1 );
if (SUCCEEDED(hr))
{
// was able to obtain the ID3DDevice1 interface
// (we are on a DirectX 11.1+ system)
device1->...
}
ComPtr<ID3D11Debug> d3dDebug;
hr = device.As( &d3dDebug );
if (SUCCEEDED(hr))
{
// was able to obtain the ID3D11Debug interface
d3dDebug->...
}
You can also use
CopyTo
to perform similar operations toAs
.
If you wish to release a reference from a particular ComPtr, you can use Reset() which sets the ComPtr to nullptr releasing any previous reference.
ComPtr<ID3D11Debug> d3dDebug;
...
d3dDebug.Reset();
d3dDebug = nullptr;
has the same effect, butReset
is a bit more clear.
Like many smart-pointers, there is an operator bool
overload to let you easily check if a ComPtr is currently set to null which is often useful in assert statements:
ComPtr<ID3D11Debug> d3dDebug;
...
if ( !d3dDebug )
// d3dDebug is currently set to nullptr
...
assert( d3dDebug ); // trigger error in debug builds if d3dDebug is set to null
Remember that if you set a ComPtr to a raw pointer or another ComPtr via the copy constructor or the assignment operator, the behavior is to increase the reference count. This is assuming the original raw pointer or ComPtr will still be calling Release
, and the new copy will also be calling Release
.
If you want to give a raw pointer to a ComPtr without changing the reference count, you need to use Attach
.
If you want to give a pointer in one ComPtr to another ComPtr without changing the reference count (i.e. transferring ownership), you need to use Detach
from the old ComPtr followed by an Attach
to the new one. Alternatively, you can use Swap
to achieve the same thing when one of the ComPtr variables involved is already null.
ComPtr has a move constructor and a move assignment operator both of which effectively perform a Swap
.
When using operator
&
,GetAddressOf
orReleaseAndGetAddressOf
the reference count is not changed as typically these are used in contexts where the reference count has already been increased (i.e. newly constructed objects coming from APIs, getter methods, etc.).
ComPtr goes through some lengths to hide the base IUnknown methods AddRef
, Release
, and QueryInterface
. You should instead make use of the various ComPtr facilities to achieve the same effect (as discussed above).
It is asking for serious trouble if you try to circumvent this by doing something like
object.Get()->Release()
.
The bulk of the Windows Runtime Template Library (WRL) is intended to make it easier to use the new WinRT style APIs introduced with Windows Store and the universal Windows app platform. It is a pure C++ template library and does not make use of the C++/CX language extensions, which are themselves another way to consume WinRT APIs from C++. You can, however, mix usage of the two easily and most C++/CX programs will use at least Microsoft::WRL::ComPtr
for managing COM objects for non-WinRT APIs such as Direct3D.
Because WRL is a pure C++ template library, it has no particular platform support requirements beyond being in the Windows 8.x or Windows 10 SDKs. You can therefore freely use it in Windows desktop applications built for Windows Vista, Windows 7, Windows 8.x, or Windows 10 as well as in Windows Store, Windows phone, Xbox One, and universal Windows apps.
The Windows 8.0 SDK and later that includes WRL does not support targeting Windows XP / Windows Server 2003. For these older operating systems you have to make use of the Windows 7.1A SDK instead. See this blog post for more details.
Making use of the older _com_ptr_t
Visual Studio smart-pointer class which was originally intended as a non-ATL option is not recommended.
If using C++/WinRT projections, you could also choose to use winrt::com_ptr
instead of Microsoft::WRL:ComPtr
.
All content and source code for this package are subject to the terms of the MIT License.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.
- Universal Windows Platform apps
- Windows desktop apps
- Windows 11
- Windows 10
- Windows 8.1
- Xbox One
- x86
- x64
- ARM64
- Visual Studio 2022
- Visual Studio 2019 (16.11)
- clang/LLVM v12 - v18
- MinGW 12.2, 13.2
- CMake 3.20