Memory Leak in juce::HWNDComponentPeer? (Resolved)

Hi all,

I think I found a memory leak in juce::HWNDComponentPeer on Windows. I’m using JUCE v5.4.4.

HWNDComponentPeer::createWindow() does this:
dropTarget = new FileDropTarget (*peer);

FYI, dropTarget is declared like so:
FileDropTarget* dropTarget = nullptr;

However, the HWNDComponentPeer destructor cleans it up by doing this:
if (dropTarget != nullptr)
{
dropTarget->peerIsDeleted = true;
dropTarget->Release();
dropTarget = nullptr;
}

Notice that it calls Release(), which cleans up the underlying IDropTarget COM object, but it doesn’t actually delete the dropTarget object itself.

I was getting the JUCE leak assert when closing my app. Couldn’t figure out what I was doing wrong, so I dug into the JUCE code and found this. Calling delete dropTarget clears it up nicely.

Thanks,
Ben

Or even better than calling delete, change it to an std::unique_ptr of course.

No!! It’s a COM object, so is ref-counted and you never delete them, you just release them and they delete themselves at the right time. If that object is leaking then it must be because some other object still has a ref-count to it, so if you forcibly delete it then you leave that other object using a dangling pointer!

Thanks for the quick reply. Hmm, admittedly my COM is a bit rusty. Lets ignore the new/delete/AddRef/Release thing for a moment.

Bearing in mind dropTarget is a private juce::HWNDComponentPeer member variable, can you think of anything my program could have done to cause it to leak?

Edit: Re the new/delete/AddRef/Release thing, I’m aware that you don’t call ‘delete’ to clean up a COM object, but FileDropTarget is a C++ object. As we all know, if you create a C++ object with ‘new’, you must clean it up with ‘delete’, hence my original comment.

That said, I now realise that FileDropTarget inherits from juce::ComBaseClassHelperBase<>, which is an RAII wrapper around the underlying COM object, and does indeed call delete on itself when the ref count reaches zero, in addition to calling Release() on the COM object.

Obviously that’s not normally the case outside of this JUCE specific context, even when using something like CComPtr<>, hence my original assumption.

I felt the need to clarify, since your reply suggests I thought it was generally a good idea to call delete on COM objects!

I managed to resolve the issue, so here’s a quick update in case anyone runs into something similar.

As Jules suspected in his reply, it wasn’t a JUCE leak.

I was calling removeFromDesktop() after the HWND had already been destroyed (in a window wrapper class destructor). As a result, JUCE was skipping the code that cleans up the IDropTarget COM object - hence the leak.

Instead, it was simply a matter of moving the removeFromDesktop() call to the WM_DESTROY handler instead, which is called before the HWND is destroyed.