CComPtr<IDirect3D9> MemLeak Gotcha
November 17th, 2009Everyone else but me probably has figured this out already, but I think there’s a chance this post will help someone else with this problem, too.
The other day it took me a good hour of guessing and cursing to get this one figured out. I got all cocky and thought: “Who the hell needs to read CComPtr documentation at all?!?”, and wrote this simple piece of code, before it turned out I should have read that documentation first:
#include <d3d9.h> // For IDirect3D9 and WinMain
#include <atlbase.h> // For CComPtr<T>
#pragma comment(lib, "d3d9.lib") // Add a reference to d3d9.lib for linking
INT WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
// Check for memory leaks at program shutdown
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
CComPtr<IDirect3D9> direct3d = Direct3DCreate9(D3D_SDK_VERSION);
return 0;
}
After you run this under DirectX Debug Runtime, you will get useful information in your Output window in Visual Studio:
Direct3D9: (INFO) :MemFini! Direct3D9: (ERROR) :Memory still allocated! Alloc count = 10
One would think that when CComPtr gets out of the scope, it will Release() the COM resource it’s pointing to, and… he would be right. The less obvious thing is – when you assign a T* the CComPtr<T>, the assignment operator calls another AddRef(). The instance passed as a right-hand side argument of the assignment already has a reference count of 1. Therefore, if you assign it to the CComPtr, it will have a reference count of 2. When it gets out of scope, its destructor is called, and only one Release() is called for the IDirect3D interface, and therefore we have a resource leak.
The right way to assign the IDirect3D9 interface to CComPtr<IDirect3D9> is:
CComPtr<IDirect3D9> direct3d; direct3d.Attach(Direct3DCreate9(D3D_SDK_VERSION));</span></pre>
If you hover your cursor over the Attach method, your trusty Intellisense or Visual Assist will tell you:
void CComPtrBase<IDirect3D9>::Attach(_In_opt IDirect3D9* p2) throw() // Attach to an existing interface (does not AddRef)
Voila! Exactly what we need! The whole rest of the IDirect3DXxx references can be used as CComPtr<T> pointers. The DirectX 9 API will do the right thing for you. If you just consistently use CComPtr<T> for what it was designed for, you will probably never see a resource leak again. And yes, CComPtr<T> can be used whenever the Direct3D API expects T* as a parameter, even as an output parameter.








