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

使用MSXML与智能指针的代码存在内存泄漏问题求助

问题描述

使用MSXML智能指针编写的代码存在内存泄漏,循环运行时任务管理器、Process Explorer及GetProcessMemoryInfo获取的Commit Size持续增长。已确认parser指针不是泄漏源,通过手动调用.AddRef()和.Release()发现createProcessingInstruction返回对象的引用计数为2,手动调用.Release()会触发断言,且Debug构建无法进入引用计数逻辑。疑似泄漏点在替换XML处理指令的代码块中。

相关代码:

MSXML2::IXMLDOMDocument3Ptr parser;
HRESULT hr(parser.CreateInstance(__uuidof(MSXML2::DOMDocument60) ) );
if(FAILED(hr) )
{
    throw(XmlException("Failed to create instance of MSXML."));
}
parser->preserveWhiteSpace = VARIANT_TRUE;

...
// Various methods to populate the XML.  Omitted because this was determined to not leak
...

// Something within this section is leaking.
{
    const std::wstring wstrXmlDeclaration(L" version=\"1.0\" encoding=\"UTF-16\"");
    // TODO:  If this is expensive, maybe store an instance?
    IXMLDOMProcessingInstructionPtr pProcessingInstruction(
        parser->createProcessingInstruction(
            L"xml",
            wstrXmlDeclaration.c_str()
        )
    );
    if(!pProcessingInstruction)
    {
        throw(XmlException("Failed to create instruction for encoding.") );
    }
    DWORD ulRefCount = pProcessingInstruction->AddRef();
    ulRefCount = pProcessingInstruction->Release();

    // NOTE: ulRefCount is 2 here (??)

    MSXML2::IXMLDOMNodePtr pFirstChild(
        parser->GetfirstChild()
    );
    if(!pFirstChild)
    {
        throw(XmlException("Failed to get XML Declaration of XML Document.") );
    }

    const std::wstring wstrFirstChildName(pFirstChild->nodeName);
    assert(wstrFirstChildName == L"xml");
    if(wstrFirstChildName != L"xml")
    {
        throw(XmlException("Unexpected first element (XML Declaration) in XML."));
    }

    MSXML2::IXMLDOMNodePtr pOldChild(
        parser->replaceChild(
            static_cast<MSXML2::IXMLDOMNode*>(pProcessingNode),
            static_cast<MSXML2::IXMLDOMNode*>(pFirstChild)
        )
    );
    if(!pOldChild)
    {
        throw(XmlException("Failed to apply specified encoding.") );
    }
}

泄漏原因分析与修复方案

1. 致命变量名笔误

代码中replaceChild的第一个参数使用了未定义的pProcessingNode,但实际创建的处理指令变量是pProcessingInstruction。这个笔误会导致传入野指针或未初始化指针,MSXML在处理错误指针时,会出现资源未正确清理的情况,直接引发内存泄漏,甚至可能触发断言或崩溃。

修复:将pProcessingNode替换为pProcessingInstruction:

MSXML2::IXMLDOMNodePtr pOldChild(
    parser->replaceChild(
        static_cast<MSXML2::IXMLDOMNode*>(pProcessingInstruction),
        static_cast<MSXML2::IXMLDOMNode*>(pFirstChild)
    )
);

2. 手动操作引用计数破坏平衡

你手动调用.AddRef()和.Release()的行为会干扰智能指针的自动引用计数管理:

  • createProcessingInstruction返回的COM对象初始引用计数为1
  • 智能指针pProcessingInstruction构造时会自动调用.AddRef(),此时引用计数变为2
  • 手动调用.AddRef()后计数到3,.Release()回到2,这是正常的(智能指针持有1次,对象本身未被DOM树接管时的状态)
  • 手动修改引用计数会导致后续智能指针析构时的计数不平衡,要么泄漏,要么触发断言

修复:删除手动调用.AddRef()和.Release()的代码,完全依赖智能指针自动管理引用计数。

3. 旧节点的自动释放验证

replaceChild返回的旧节点由pOldChild智能指针持有,代码块结束时智能指针会自动调用.Release()释放旧节点。只要变量名笔误修复,这部分逻辑是正常的,不会导致泄漏。

额外建议

  • 使用MSXML的Debug版本(msxml6d.dll)配合Debug构建,能更精准地捕获COM对象的引用计数问题和内存泄漏,默认Release版本的MSXML会屏蔽部分调试逻辑。
  • 避免在智能指针上手动操作引用计数,智能指针的设计初衷就是自动管理COM对象的生命周期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 11:49:57