CComPtr<IDirect3D9> MemLeak Gotcha

November 17th, 2009

Everyone 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.

Leave a Reply