You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++ COM项目中输出参数引发内存泄漏的问题咨询:如何正确释放内存?

搞定COM项目的内存泄漏问题!

嘿,我仔细看了你的代码,发现内存泄漏的根源其实是**CompositionHost没遵守COM的引用计数规则**,再加上CreateCompositionHost里有个小逻辑错误,咱们一步步来修复:

1. 先把CreateCompositionHost的逻辑坑填上

你现在的函数里有个明显的判断错误:赋值完*compositionHost后,你检查的是compositionHost(指针的指针)不为空,这永远是真的啊!应该检查的是咱们刚创建的对象指针是否有效。修正后的代码如下:

bool __stdcall FluentCompositor::CreateCompositionHost(HWND hwnd, ICompositionHost** compositionHost) {
    // 先判断输入参数是否合法
    if (compositionHost == nullptr) {
        return false;
    }
    // 先把输出指针置空,避免后续出问题
    *compositionHost = nullptr;
    try {
        // 创建CompositionHost实例
        *compositionHost = reinterpret_cast<ICompositionHost*>(new CompositionHost(hwnd));
        // 检查对象是否创建成功
        if (*compositionHost != nullptr) {
            // 划重点:COM规范里,新创建的对象引用计数初始为1,调用者负责Release
            // 只要你的CompositionHost构造函数把refCount设为1,这里不用额外加AddRef
            return true;
        }
    } catch (const std::bad_alloc&) {
        // 处理内存分配失败的情况
        return false;
    }
    return false;
}

2. 给CompositionHost加上正确的COM引用计数

这是解决泄漏的核心!COM对象的生命周期完全靠AddRef/Release管理,你的CompositionHost必须实现IUnknown的三个核心方法:AddRef、Release、QueryInterface。

假设你的ICompositionHost已经继承了IUnknown,那CompositionHost的实现应该是这样的:

CompositionHost.h

#pragma once
#include "ICompositionHost.h"

class CompositionHost : public ICompositionHost {
private:
    // 用long类型保证线程安全,避免多线程下计数混乱
    long refCount;
    HWND hwnd;
public:
    explicit CompositionHost(HWND hwnd);
    ~CompositionHost();

    // 实现IUnknown的纯虚方法
    ULONG __stdcall AddRef() override;
    ULONG __stdcall Release() override;
    HRESULT __stdcall QueryInterface(REFIID iid, void** ppv) override;

    // 这里放你的ICompositionHost接口方法
};

CompositionHost.cpp

#include "pch.h"
#include "CompositionHost.h"

CompositionHost::CompositionHost(HWND hwnd) : refCount(1), hwnd(hwnd) {
    // 这里写你的构造逻辑,比如初始化和hwnd相关的资源
}

CompositionHost::~CompositionHost() {
    // 这里做资源清理,比如释放和hwnd绑定的合成资源
}

ULONG __stdcall CompositionHost::AddRef() {
    // 用InterlockedIncrement保证多线程下计数正确
    return InterlockedIncrement(&refCount);
}

ULONG __stdcall CompositionHost::Release() {
    // 同样用线程安全的递减操作
    ULONG newCount = InterlockedDecrement(&refCount);
    // 引用计数归0时,自动销毁对象
    if (newCount == 0) {
        delete this;
    }
    return newCount;
}

HRESULT __stdcall CompositionHost::QueryInterface(REFIID iid, void** ppv) {
    if (ppv == nullptr) {
        return E_POINTER;
    }
    *ppv = nullptr;
    // 支持查询IUnknown和ICompositionHost接口
    if (iid == IID_IUnknown || iid == IID_ICompositionHost) {
        *ppv = static_cast<ICompositionHost*>(this);
        // 返回接口指针前必须调用AddRef
        AddRef();
        return S_OK;
    }
    // 不支持的接口返回E_NOINTERFACE
    return E_NOINTERFACE;
}

这里的关键细节:

  • 构造函数把refCount初始化为1,完全符合COM的创建规范
  • 用InterlockedIncrement/InterlockedDecrement保证多线程下计数安全
  • Release里引用计数归0时自动delete this,这是COM对象自我销毁的标准方式

3. 修正MainWindow里的调用方式

你当前调用CreateCompositionHost用的是&host,虽然WRL的ComPtr支持这种写法,但更规范的是用GetAddressOf(),而且要确保ComPtr在作用域结束时自动释放:

ComPtr<ICompositionHost> host;
// 用GetAddressOf()获取输出指针的地址
compositor->CreateCompositionHost(hwnd, host.GetAddressOf());

当host这个ComPtr离开CreateCompositionEffect的作用域时,它的析构函数会自动调用Release。如果此时CompositionHost的引用计数降到0,就会触发delete this,彻底释放内存,不会再泄漏。

4. 顺便优化FluentCompositor的引用计数

你的FluentCompositor已经实现了AddRef/Release,但直接用++ref和--ref不是线程安全的,改成原子操作更稳妥:

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

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

最后总结一下

内存泄漏的核心就是CompositionHost没实现正确的COM引用计数,导致ComPtr没法触发对象销毁。按照上面的步骤改完后,ComPtr会自动管理对象生命周期,引用计数归零时对象自动删除,泄漏问题就解决啦!

内容的提问来源于stack exchange,提问作者Selastin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 03:42:26