首页 > 解决方案 > C++ 输出参数导致内存泄漏

问题描述

我正在开发一个基于 COM 的 C++ 项目,我的代码有一个带有 out 参数的函数,该函数将一个对象作为输入并将一个类的新实例分配给它。但是当我使用 CRT 调试时,我发现函数中有一些内存泄漏,这是函数的代码。

bool __stdcall FluentCompositor::CreateCompositionHost(HWND hwnd, ICompositionHost** compositionHost)
{
    if (compositionHost != nullptr)
    {
        *compositionHost = reinterpret_cast<ICompositionHost*>(new CompositionHost(hwnd));
        if (compositionHost != nullptr)
        {
            return true;
        }
    }
    else
    {
        return false;
    }
    return false;
}

这个函数接受一个对象ICompositionHost并用对象初始化它,CompositionHost我应该在哪里释放内存以避免内存泄漏。

我使用 ComPtr 调用函数,但仍然存在内存泄漏

ComPtr<ICompositionHost> host;
compositor->CreateCompositionHost(hwnd,host.GetAddressOf());

完整代码:

FluentCompositor.cpp

#include "pch.h"
#include "ICompositionHost.h"
#include "IFluentCompositor.h"
#include "CompositionHost.h"
#include "FluentCompositor.h"

FluentCompositor::FluentCompositor() :ref(1)
{
}

ulong __stdcall FluentCompositor::AddRef()
{
    return (++ref);
}

ulong __stdcall FluentCompositor::Release()
{
    if (--ref == 0)
    {
        delete this;
        return 0;
    }
    return ref;
}

HRESULT __stdcall FluentCompositor::QueryInterface(REFIID iid, LPVOID* ppv)
{
    if (iid == IID_IFluentCompositor || iid == IID_IUnknown)
    {
        *ppv = (void*)this;
        AddRef();
    }
    else
    {
        *ppv = NULL;
    }
    return (*ppv == NULL) ? E_NOINTERFACE : S_OK;
}

HRESULT __stdcall CreateFluentCompositor(void** compositor)
{
    if (compositor != nullptr)
    {
        *compositor = reinterpret_cast<void*>(new FluentCompositor());
        if (compositor != nullptr)
        {
            return S_OK;
        }
    }
    else
    {
        return E_INVALIDARG;
    }
    return E_FAIL;
}

bool __stdcall FluentCompositor::CreateCompositionHost(HWND hwnd, ICompositionHost** compositionHost)
{
    if (compositionHost != nullptr)
    {
        *compositionHost = reinterpret_cast<ICompositionHost*>(new CompositionHost(hwnd));
        if (compositionHost != nullptr)
        {
            return true;
        }
    }
    else
    {
        return false;
    }
    return false;
}

主窗口.cpp

#include "pch.h"
#include "MainWindow.h"

int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
{
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    const wchar_t className[] = L"Fluent Compositor";

    WNDCLASS wc = {
        .lpfnWndProc = WndProc,
        .hInstance = reinterpret_cast<HINSTANCE>(&__ImageBase),
        .hCursor = LoadCursor(nullptr, IDC_ARROW),
        .lpszClassName = className,
    };

    RegisterClass(&wc);

    HWND hwnd = CreateWindowEx(WS_EX_NOREDIRECTIONBITMAP, className, L"Fluent Compositor Sample", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, wc.hInstance, nullptr);
    if (hwnd == nullptr)
    {
        return 0;
    }

    CreateCompositionEffect(hwnd);

    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    _CrtDumpMemoryLeaks();
    return 0;
}

bool CreateCompositionEffect(HWND hwnd)
{
    auto fluentCompositorLib = LoadLibrary(L"FluentCompositor.dll");
    if (!fluentCompositorLib)
    {
        return false;
    }

    CreateCompositor = (CreateFluentCompositor)GetProcAddress(fluentCompositorLib, "CreateFluentCompositor");
    if (!CreateCompositor)
    {
        return false;
    }

    CreateCompositor(&compositor);
    if (compositor == nullptr)
    {
        return false;
    }

    ComPtr<ICompositionHost> host;
    compositor->CreateCompositionHost(hwnd,&host);
    return true;
}

LRESULT __stdcall WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    switch (msg)
    {
        case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        }
    }
    return DefWindowProc(hwnd, msg, wparam, lparam);
}

主窗口.h

#pragma once
#include "IFluentCompositor.h"

using namespace Microsoft::WRL;

extern "C" IMAGE_DOS_HEADER __ImageBase;

ComPtr<IFluentCompositor> compositor;

typedef BOOL(__stdcall* CreateFluentCompositor)(IFluentCompositor** compositor);
CreateFluentCompositor CreateCompositor;

bool CreateCompositionEffect(HWND hwnd);
LRESULT __stdcall WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);

标签: c++oopwinapivisual-c++interface

解决方案


您对reinterpret_cast(以及一般的类型转换)的使用是完全错误的。如果您的类实现了正确的接口,则无需手动转换它们(可能 in 除外QueryInterface()),编译器将为您隐式执行正确的转换。

此外,您没有new正确检查 的返回值。或者,就此而言,处理new默认情况下在失败时抛出异常的可能性,而不是返回nullptr。如果您想要nullptr失败,请改用nothrowof 的版本new

另外,在使用时ComPtr,应该使用它的重载operator&而不是它的GetAddressOf()方法。特别是如果ComPtr已经拥有一个接口。GetAddressOf()不会释放接口(这就是为什么有一个单独的ReleaseAndGetAddressOf()方法),但operator&会。

试试这个:

#include <new>

HRESULT __stdcall FluentCompositor::QueryInterface(REFIID iid, LPVOID* ppv)
{
    if (!ppv) return E_POINTER;

    if (iid == IID_IFluentCompositor)
    {
        *ppv = static_cast<IFluentCompositor*>(this);
        /* alternatively:
        IFluentCompositor *comp = this;
        *ppv = comp;
        */
    }
    else if (iid == IID_IUnknown)
    {
        *ppv = static_cast<IUnknown*>(static_cast<IFluentCompositor*>(this));
        /* alternatively:
        IFluentCompositor *comp = this;
        IUnknown *unk = comp;
        *ppv = unk;
        */
    }
    else
    {
        *ppv = nullptr;
    }

    if (!*ppv)
        return E_NOINTERFACE;

    AddRef();
    return S_OK;
}

// similar for CompositionHost::QueryInterface() ...

HRESULT __stdcall CreateFluentCompositor(void** compositor)
{
    if (!compositor) return E_POINTER; // not E_INVALIDARG
    // make sure FluentCompositor has a refcount of 1 when created!
    *compositor = static_cast<IFluentCompositor*>(new(std::nothrow) FluentCompositor);
    return (*compositor) ? S_OK : E_FAIL;
}

bool __stdcall FluentCompositor::CreateCompositionHost(HWND hwnd, ICompositionHost** compositionHost)
{
    if (!compositionHost) return false;
    // make sure CompositionHost has a refcount of 1 when created!
    *compositionHost = new(std::nothrow) CompositionHost(hwnd);
    return (*compositionHost);
}
ComPtr<ICompositionHost> host;
compositor->CreateCompositionHost(hwnd, &host);

或者,ComPtr在创建对象时考虑在内部使用,例如:

#include <new>

HRESULT __stdcall CreateFluentCompositor(void** compositor)
{
    if (!compositor) return E_POINTER; // not E_INVALIDARG
    // make sure FluentCompositor has a refcount of 0 when created,
    // as the ComPtr constructor will increment it!
    ComPtr<IFluentCompositor> obj(new(std::nothrow) FluentCompositor);
    return (obj) ? obj->QueryInterface(IID_IFluentCompositor, compositor) : E_FAIL;
}

bool __stdcall FluentCompositor::CreateCompositionHost(HWND hwnd, ICompositionHost** compositionHost)
{
    // make sure CompositionHost has a refcount of 0 when created,
    // as the ComPtr constructor will increment it!
    ComPtr<ICompositionHost> obj(new(std::nothrow) CompositionHost(hwnd));
    return ((obj) && (obj->QueryInterface(IID_ICompositionHost, reinterpret_cast<void**>(compositionHost)) == S_OK));
}

让您的对象以 0 而不是 1 的引用计数开始是个好主意,因为它们不知道它们是否将与接口指针或对象指针一起使用(如果它们甚至与指针一起使用) . 不要增加一个对象的引用计数,除非它实际上被分配给一个被AddRef()'ed 并且需要被Release()'d 的接口指针。


推荐阅读