从0到1使用C++操作MSXML

1. 引言

MSXML(Microsoft XML Core Services)是微软提供的一套用于处理XML的COM组件库,广泛应用于Windows平台的XML解析、验证、转换等操作。本文将详细介绍如何从零开始,在C++中使用MSXML解析和操作XML文件,包含完整的代码示例及逐行注释,帮助初学者快速上手。


2. 准备工作

2.1 环境要求

  • Windows 操作系统(MSXML 是 Windows 特有组件)
  • Visual Studio(推荐 2015 或更高版本)
  • 基础C++知识(COM组件基本概念)

2.2 引入MSXML库

在代码中引入MSXML头文件,并链接对应的库:

cpp 复制代码
#include <msxml6.h>   // MSXML 6.0(最新稳定版)
#include <comdef.h>   // 用于COM智能指针(_bstr_t, _variant_t)
#pragma comment(lib, "msxml6.lib")  // 链接MSXML库

3. 初始化COM环境

MSXML基于COM(Component Object Model),使用前必须初始化COM库:

cpp 复制代码
#include <objbase.h>  // CoInitializeEx 所需头文件

int main() {
    // 初始化COM库(单线程模式)
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    if (FAILED(hr)) {
        printf("Failed to initialize COM. Error: %08X\n", hr);
        return 1;
    }

    // 后续代码...

    // 释放COM库
    CoUninitialize();
    return 0;
}

逐行解析:

  1. CoInitializeEx(NULL, COINIT_APARTMENTTHREADED):初始化COM,指定单线程单元(STA)模式。
  2. FAILED(hr):检查COM初始化是否成功。
  3. CoUninitialize():程序结束时释放COM资源。

4. 加载并解析XML文件

4.1 创建MSXML DOM对象

cpp 复制代码
IXMLDOMDocument2* pXMLDoc = NULL;
hr = CoCreateInstance(
    __uuidof(DOMDocument60),  // 使用MSXML 6.0
    NULL,
    CLSCTX_INPROC_SERVER,
    __uuidof(IXMLDOMDocument2),
    (void**)&pXMLDoc
);
if (FAILED(hr) || pXMLDoc == NULL) {
    printf("Failed to create XML DOM instance. Error: %08X\n", hr);
    CoUninitialize();
    return 1;
}

逐行解析:

  1. CoCreateInstance:创建MSXML DOM对象。
  2. __uuidof(DOMDocument60):指定使用MSXML 6.0。
  3. CLSCTX_INPROC_SERVER:组件运行在进程内。

4.2 加载XML文件

cpp 复制代码
VARIANT_BOOL bSuccess = VARIANT_FALSE;
hr = pXMLDoc->load(_variant_t(L"example.xml"), &bSuccess);
if (FAILED(hr) || bSuccess != VARIANT_TRUE) {
    printf("Failed to load XML file. Error: %08X\n", hr);
    pXMLDoc->Release();
    CoUninitialize();
    return 1;
}

逐行解析:

  1. pXMLDoc->load:加载XML文件(example.xml)。
  2. _variant_t(L"example.xml"):将文件名转换为COM兼容的VARIANT类型。
  3. bSuccess:返回加载是否成功。

5. 读取XML数据

5.1 获取XML根节点

cpp 复制代码
IXMLDOMElement* pRoot = NULL;
hr = pXMLDoc->get_documentElement(&pRoot);
if (FAILED(hr) {
    printf("Failed to get root element. Error: %08X\n", hr);
    pXMLDoc->Release();
    CoUninitialize();
    return 1;
}

逐行解析:

  1. get_documentElement:获取XML的根节点。

5.2 遍历子节点

cpp 复制代码
IXMLDOMNodeList* pNodes = NULL;
hr = pRoot->get_childNodes(&pNodes);
if (FAILED(hr)) {
    printf("Failed to get child nodes. Error: %08X\n", hr);
    pRoot->Release();
    pXMLDoc->Release();
    CoUninitialize();
    return 1;
}

long length = 0;
pNodes->get_length(&length);  // 获取子节点数量

for (long i = 0; i < length; i++) {
    IXMLDOMNode* pNode = NULL;
    pNodes->get_item(i, &pNode);  // 获取第i个节点

    BSTR nodeName;
    pNode->get_nodeName(&nodeName);  // 获取节点名称
    printf("Node %d: %S\n", i, nodeName);

    SysFreeString(nodeName);  // 释放BSTR内存
    pNode->Release();  // 释放节点
}
pNodes->Release();

逐行解析:

  1. get_childNodes:获取所有子节点。
  2. get_length:获取子节点数量。
  3. get_item(i, &pNode):获取第i个节点。
  4. get_nodeName:获取节点名称(返回BSTR,需手动释放)。

6. 修改XML数据

6.1 修改节点属性

cpp 复制代码
IXMLDOMElement* pFirstChild = NULL;
pNodes->get_item(0, (IXMLDOMNode**)&pFirstChild);  // 获取第一个子节点

// 设置属性
hr = pFirstChild->setAttribute(
    _bstr_t(L"newAttr"),  // 属性名
    _variant_t(L"newValue")  // 属性值
);
if (FAILED(hr)) {
    printf("Failed to set attribute. Error: %08X\n", hr);
}
pFirstChild->Release();

逐行解析:

  1. setAttribute:设置节点属性,使用_bstr_t_variant_t封装字符串。

6.2 保存修改后的XML

cpp 复制代码
hr = pXMLDoc->save(_variant_t(L"modified.xml"));
if (FAILED(hr)) {
    printf("Failed to save XML. Error: %08X\n", hr);
}

逐行解析:

  1. save:保存XML到新文件。

7. 完整代码示例

演示1

cpp 复制代码
#include <msxml6.h>
#include <comdef.h>
#include <objbase.h>
#include <stdio.h>

int main() {
    // 初始化COM
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    if (FAILED(hr)) {
        printf("COM init failed. Error: %08X\n", hr);
        return 1;
    }

    // 创建DOM对象
    IXMLDOMDocument2* pXMLDoc = NULL;
    hr = CoCreateInstance(
        __uuidof(DOMDocument60),
        NULL,
        CLSCTX_INPROC_SERVER,
        __uuidof(IXMLDOMDocument2),
        (void**)&pXMLDoc
    );
    if (FAILED(hr) || !pXMLDoc) {
        printf("Failed to create DOM. Error: %08X\n", hr);
        CoUninitialize();
        return 1;
    }

    // 加载XML
    VARIANT_BOOL bSuccess = VARIANT_FALSE;
    hr = pXMLDoc->load(_variant_t(L"example.xml"), &bSuccess);
    if (FAILED(hr) || bSuccess != VARIANT_TRUE) {
        printf("Failed to load XML. Error: %08X\n", hr);
        pXMLDoc->Release();
        CoUninitialize();
        return 1;
    }

    // 获取根节点
    IXMLDOMElement* pRoot = NULL;
    hr = pXMLDoc->get_documentElement(&pRoot);
    if (FAILED(hr)) {
        printf("Failed to get root. Error: %08X\n", hr);
        pXMLDoc->Release();
        CoUninitialize();
        return 1;
    }

    // 遍历子节点
    IXMLDOMNodeList* pNodes = NULL;
    hr = pRoot->get_childNodes(&pNodes);
    if (FAILED(hr)) {
        printf("Failed to get child nodes. Error: %08X\n", hr);
        pRoot->Release();
        pXMLDoc->Release();
        CoUninitialize();
        return 1;
    }

    long length = 0;
    pNodes->get_length(&length);
    for (long i = 0; i < length; i++) {
        IXMLDOMNode* pNode = NULL;
        pNodes->get_item(i, &pNode);

        BSTR nodeName;
        pNode->get_nodeName(&nodeName);
        printf("Node %d: %S\n", i, nodeName);

        SysFreeString(nodeName);
        pNode->Release();
    }

    // 修改节点属性
    IXMLDOMElement* pFirstChild = NULL;
    pNodes->get_item(0, (IXMLDOMNode**)&pFirstChild);
    hr = pFirstChild->setAttribute(_bstr_t(L"newAttr"), _variant_t(L"newValue"));
    if (FAILED(hr)) {
        printf("Failed to set attribute. Error: %08X\n", hr);
    }

    // 保存XML
    hr = pXMLDoc->save(_variant_t(L"modified.xml"));
    if (FAILED(hr)) {
        printf("Failed to save XML. Error: %08X\n", hr);
    }

    // 释放资源
    pFirstChild->Release();
    pNodes->Release();
    pRoot->Release();
    pXMLDoc->Release();
    CoUninitialize();

    return 0;
}

演示2

cpp 复制代码
// Windows 头文件,提供基础API支持
#include <windows.h>

// MSXML 6.0 接口定义头文件
#include <msxml6.h>

// COM 基础功能头文件,包含CoInitialize等函数
#include <objbase.h>

// 标准输入输出头文件
#include <stdio.h>

// 添加以下头文件
#include <comutil.h>

// 添加以下链接库
#pragma comment(lib, "comsuppw.lib")

// 链接器指令:自动链接必要的库文件
#pragma comment(lib, "msxml6.lib")  // MSXML 6.0 库
#pragma comment(lib, "ole32.lib")   // COM 基础库

int main() {
    /*****************************************************************
    * 第一部分:COM库初始化和XML文档对象创建
    *****************************************************************/
    
    // 初始化COM库,所有使用COM技术的程序都必须先调用此函数
    // 参数NULL表示使用默认的线程模型(单线程公寓STA)
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr)) {
        printf("错误:COM库初始化失败。错误代码 = 0x%08x\n", hr);
        return -1;
    }

    // 创建XML文档对象指针,初始化为NULL
    IXMLDOMDocument2* pXMLDoc = NULL;
    
    // 使用CoCreateInstance创建MSXML DOM文档对象
    // 参数说明:
    // CLSID_DOMDocument60 - MSXML 6.0 DOM文档的类ID
    // NULL - 不进行聚合
    // CLSCTX_INPROC_SERVER - 创建进程内服务器
    // IID_IXMLDOMDocument2 - 请求的接口ID
    // (void**)&pXMLDoc - 接收接口指针的地址
    hr = CoCreateInstance(
        CLSID_DOMDocument60,
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_IXMLDOMDocument2,
        (void**)&pXMLDoc);
    
    // 检查创建是否成功
    if (FAILED(hr) || pXMLDoc == NULL) {
        printf("错误:创建DOM文档对象失败。错误代码 = 0x%08x\n", hr);
        CoUninitialize();  // 释放COM库
        return -1;
    }

    /*****************************************************************
    * 第二部分:设置文档属性
    *****************************************************************/
    
    // 设置异步加载为FALSE(同步加载)
    // VARIANT_FALSE表示禁用异步加载
    hr = pXMLDoc->put_async(VARIANT_FALSE);
    
    // 设置验证文档结构(如果文档有DTD或Schema)
    // VARIANT_TRUE表示开启验证
    hr = pXMLDoc->put_validateOnParse(VARIANT_TRUE);
    
    // 设置不解析外部资源(如外部DTD)
    // VARIANT_FALSE表示不解析外部资源
    hr = pXMLDoc->put_resolveExternals(VARIANT_FALSE);
    
    // 设置保留空白字符
    // VARIANT_TRUE表示保留空白字符(如缩进、换行等)
    hr = pXMLDoc->put_preserveWhiteSpace(VARIANT_TRUE);

    /*****************************************************************
    * 第三部分:创建和加载XML文档
    *****************************************************************/
    
    // 创建一个包含XML内容的BSTR字符串
    // BSTR是COM中使用的字符串类型,以长度前缀和null结尾
    BSTR bstrXML = SysAllocString(
        L"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        L"<bookstore>\n"
        L"  <book category=\"cooking\">\n"
        L"    <title lang=\"en\">Everyday Italian</title>\n"
        L"    <author>Giada De Laurentiis</author>\n"
        L"    <year>2005</year>\n"
        L"    <price>30.00</price>\n"
        L"  </book>\n"
        L"  <book category=\"children\">\n"
        L"    <title lang=\"en\">Harry Potter</title>\n"
        L"    <author>J.K. Rowling</author>\n"
        L"    <year>2005</year>\n"
        L"    <price>29.99</price>\n"
        L"  </book>\n"
        L"  <book category=\"web\">\n"
        L"    <title lang=\"en\">Learning XML</title>\n"
        L"    <author>Erik T. Ray</author>\n"
        L"    <year>2003</year>\n"
        L"    <price>39.95</price>\n"
        L"  </book>\n"
        L"</bookstore>"
    );

    // 用于接收加载是否成功的变量
    VARIANT_BOOL vbIsSuccessful;
    
    // 将XML字符串加载到文档对象中
    hr = pXMLDoc->loadXML(bstrXML, &vbIsSuccessful);
    
    // 释放BSTR内存(COM编程中必须手动释放分配的内存)
    SysFreeString(bstrXML);

    // 检查XML加载是否成功
    if (FAILED(hr) || vbIsSuccessful != VARIANT_TRUE) {
        printf("错误:加载XML字符串失败。\n");
        
        // 获取解析错误对象
        IXMLDOMParseError* pXMLError = NULL;
        BSTR bstrError = NULL;
        
        // 获取解析错误信息
        hr = pXMLDoc->get_parseError(&pXMLError);
        if (SUCCEEDED(hr)) {
            // 获取错误原因描述
            pXMLError->get_reason(&bstrError);
            printf("错误原因: %S\n", bstrError);
            
            // 释放错误描述字符串
            SysFreeString(bstrError);
            
            // 释放错误对象
            pXMLError->Release();
        }
        
        // 清理资源
        pXMLDoc->Release();
        CoUninitialize();
        return -1;
    }

    /*****************************************************************
    * 第四部分:演示从文件加载XML(补充功能)
    *****************************************************************/
    
    // 创建另一个XML文档对象用于演示文件加载
    IXMLDOMDocument2* pXMLDocFromFile = NULL;
    hr = CoCreateInstance(CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER, 
                         IID_IXMLDOMDocument2, (void**)&pXMLDocFromFile);
    
    if (SUCCEEDED(hr) && pXMLDocFromFile != NULL) {
        // 设置文档属性(同上)
        pXMLDocFromFile->put_async(VARIANT_FALSE);
        pXMLDocFromFile->put_validateOnParse(VARIANT_TRUE);
        
        // 创建VARIANT变量来指定文件名
        VARIANT varFileName;
        VariantInit(&varFileName);  // 初始化VARIANT
        varFileName.vt = VT_BSTR;   // 设置类型为BSTR
        
        // 分配文件名(假设当前目录下有books.xml文件)
        varFileName.bstrVal = SysAllocString(L"books.xml");
        
        // 从文件加载XML
        hr = pXMLDocFromFile->load(varFileName, &vbIsSuccessful);
        
        // 清理VARIANT
        VariantClear(&varFileName);
        
        if (SUCCEEDED(hr) && vbIsSuccessful == VARIANT_TRUE) {
            printf("成功从文件加载XML文档。\n");
            
            // 这里可以添加对加载文档的操作...
            
        } else {
            printf("警告:从文件加载XML失败,可能文件不存在。\n");
        }
        
        // 释放文档对象
        pXMLDocFromFile->Release();
    }

    /*****************************************************************
    * 第五部分:遍历XML文档
    *****************************************************************/
    
    // 获取文档根元素
    IXMLDOMElement* pRootElem = NULL;
    hr = pXMLDoc->get_documentElement(&pRootElem);
    
    if (SUCCEEDED(hr) && pRootElem != NULL) {
        // 获取根元素名称
        BSTR bstrRootName;
        hr = pRootElem->get_nodeName(&bstrRootName);
        if (SUCCEEDED(hr)) {
            printf("根元素名称: %S\n", bstrRootName);
            SysFreeString(bstrRootName);
        }

        /*****************************************************************
        * 演示创建处理指令(补充功能)
        *****************************************************************/
        
        // 创建处理指令节点
        IXMLDOMProcessingInstruction* pPI = NULL;
        BSTR bstrTarget = SysAllocString(L"xml-stylesheet");
        BSTR bstrData = SysAllocString(L"type=\"text/xsl\" href=\"style.xsl\"");
        
        hr = pXMLDoc->createProcessingInstruction(bstrTarget, bstrData, &pPI);
        
        if (SUCCEEDED(hr) && pPI != NULL) {
            // 将处理指令插入到文档开头
            IXMLDOMNode* pInsertedNode = NULL;
            hr = pXMLDoc->insertBefore(pPI, _variant_t(pRootElem), &pInsertedNode);
            
            if (SUCCEEDED(hr)) {
                printf("成功添加处理指令。\n");
                pInsertedNode->Release();
            }
            
            pPI->Release();
        }
        
        SysFreeString(bstrTarget);
        SysFreeString(bstrData);
        
        /*****************************************************************
        * 演示创建注释(补充功能)
        *****************************************************************/
        
        // 创建注释节点
        IXMLDOMComment* pComment = NULL;
        BSTR bstrComment = SysAllocString(L"这是一个示例XML文档");
        
        hr = pXMLDoc->createComment(bstrComment, &pComment);
        
        if (SUCCEEDED(hr) && pComment != NULL) {
            // 将注释插入到根元素之前
            IXMLDOMNode* pInsertedComment = NULL;
            hr = pXMLDoc->insertBefore(pComment, _variant_t(pRootElem), &pInsertedComment);
            
            if (SUCCEEDED(hr)) {
                printf("成功添加注释节点。\n");
                pInsertedComment->Release();
            }
            
            pComment->Release();
        }
        
        SysFreeString(bstrComment);
        
        /*****************************************************************
        * 遍历book节点
        *****************************************************************/
        
        // 获取所有book元素
        IXMLDOMNodeList* pBooks = NULL;
        BSTR bstrTagName = SysAllocString(L"book");
        hr = pRootElem->getElementsByTagName(bstrTagName, &pBooks);
        SysFreeString(bstrTagName);

        if (SUCCEEDED(hr) && pBooks != NULL) {
            // 获取book元素数量
            long bookCount;
            pBooks->get_length(&bookCount);
            printf("找到 %d 本书\n", bookCount);

            // 遍历每本书
            for (long i = 0; i < bookCount; i++) {
                IXMLDOMNode* pBook = NULL;
                hr = pBooks->get_item(i, &pBook);
                
                if (SUCCEEDED(hr) && pBook != NULL) {
                    // 获取整本书的XML内容
                    BSTR bstrBookXML;
                    pBook->get_xml(&bstrBookXML);
                    printf("\n第 %d 本书:\n%S\n", i + 1, bstrBookXML);
                    SysFreeString(bstrBookXML);

                    // 获取book元素的属性
                    IXMLDOMNamedNodeMap* pAttrs = NULL;
                    pBook->get_attributes(&pAttrs);
                    
                    if (pAttrs != NULL) {
                        // 获取category属性
                        IXMLDOMNode* pCategoryAttr = NULL;
                        BSTR bstrAttrName = SysAllocString(L"category");
                        hr = pAttrs->getNamedItem(bstrAttrName, &pCategoryAttr);
                        SysFreeString(bstrAttrName);

                        if (SUCCEEDED(hr) && pCategoryAttr != NULL) {
                            VARIANT varValue;
                            VariantInit(&varValue);
                            
                            // 获取属性值
                            pCategoryAttr->get_nodeValue(&varValue);
                            printf("  类别: %S\n", varValue.bstrVal);
                            
                            // 清理VARIANT
                            VariantClear(&varValue);
                            pCategoryAttr->Release();
                        }
                        pAttrs->Release();
                    }

                    /*****************************************************************
                    * 演示克隆节点(补充功能)
                    *****************************************************************/
                    
                    // 克隆当前book节点(深拷贝,包括所有子节点)
                    IXMLDOMNode* pClonedBook = NULL;
                    hr = pBook->cloneNode(VARIANT_TRUE, &pClonedBook);
                    
                    if (SUCCEEDED(hr) && pClonedBook != NULL) {
                        // 可以在此处操作克隆的节点...
                        printf("  成功克隆本书节点。\n");
                        pClonedBook->Release();
                    }
                    
                    /*****************************************************************
                    * 遍历子节点
                    *****************************************************************/
                    
                    IXMLDOMNodeList* pChildNodes = NULL;
                    pBook->get_childNodes(&pChildNodes);
                    
                    if (pChildNodes != NULL) {
                        long childCount;
                        pChildNodes->get_length(&childCount);

                        for (long j = 0; j < childCount; j++) {
                            IXMLDOMNode* pChild = NULL;
                            hr = pChildNodes->get_item(j, &pChild);
                            
                            if (SUCCEEDED(hr) && pChild != NULL) {
                                // 获取子节点名称
                                BSTR bstrChildName;
                                pChild->get_nodeName(&bstrChildName);

                                // 特别处理price节点
                                if (wcscmp(bstrChildName, L"price") == 0) {
                                    BSTR bstrPrice;
                                    pChild->get_text(&bstrPrice);
                                    printf("  价格: %S\n", bstrPrice);
                                    SysFreeString(bstrPrice);
                                }

                                SysFreeString(bstrChildName);
                                pChild->Release();
                            }
                        }
                        pChildNodes->Release();
                    }

                    pBook->Release();
                }
            }
            pBooks->Release();
        }
        
        /*****************************************************************
        * 演示删除节点(补充功能)
        *****************************************************************/
        
        // 查找要删除的节点(例如第一本书)
        IXMLDOMNode* pBookToRemove = NULL;
        BSTR bstrXPathRemove = SysAllocString(L"//book[1]");
        hr = pXMLDoc->selectSingleNode(bstrXPathRemove, &pBookToRemove);
        SysFreeString(bstrXPathRemove);
        
        if (SUCCEEDED(hr) && pBookToRemove != NULL) {
            // 从父节点中删除子节点
            IXMLDOMNode* pRemovedNode = NULL;
            hr = pRootElem->removeChild(pBookToRemove, &pRemovedNode);
            
            if (SUCCEEDED(hr)) {
                printf("\n成功删除第一本书。\n");
                pRemovedNode->Release();
            }
            
            pBookToRemove->Release();
        }
        
        pRootElem->Release();
    }

    /*****************************************************************
    * 第六部分:执行XPath查询
    *****************************************************************/
    
    // 查找价格大于35的书
    BSTR bstrXPath = SysAllocString(L"//book[price>35.00]");
    IXMLDOMNodeList* pExpensiveBooks = NULL;
    hr = pXMLDoc->selectNodes(bstrXPath, &pExpensiveBooks);
    SysFreeString(bstrXPath);

    if (SUCCEEDED(hr) && pExpensiveBooks != NULL) {
        long expensiveCount;
        pExpensiveBooks->get_length(&expensiveCount);
        printf("\n找到 %d 本价格高于35.00的书\n", expensiveCount);

        // 遍历查询结果
        for (long i = 0; i < expensiveCount; i++) {
            IXMLDOMNode* pBook = NULL;
            hr = pExpensiveBooks->get_item(i, &pBook);
            
            if (SUCCEEDED(hr) && pBook != NULL) {
                // 在每本书中查找title节点
                BSTR bstrTitleXPath = SysAllocString(L"title");
                IXMLDOMNode* pTitle = NULL;
                
                // 注意:这里需要将pBook转换为IXMLDOMElement接口
                hr = ((IXMLDOMElement*)pBook)->selectSingleNode(bstrTitleXPath, &pTitle);
                SysFreeString(bstrTitleXPath);

                if (SUCCEEDED(hr) && pTitle != NULL) {
                    BSTR bstrTitle;
                    pTitle->get_text(&bstrTitle);
                    printf("  高价书标题: %S\n", bstrTitle);
                    SysFreeString(bstrTitle);
                    pTitle->Release();
                }
                pBook->Release();
            }
        }
        pExpensiveBooks->Release();
    }

    /*****************************************************************
    * 第七部分:修改XML内容
    *****************************************************************/
    
    // 查找所有book节点
    BSTR bstrAllBooksXPath = SysAllocString(L"//book");
    IXMLDOMNodeList* pAllBooks = NULL;
    hr = pXMLDoc->selectNodes(bstrAllBooksXPath, &pAllBooks);
    SysFreeString(bstrAllBooksXPath);

    if (SUCCEEDED(hr) && pAllBooks != NULL) {
        long bookCount;
        pAllBooks->get_length(&bookCount);

        for (long i = 0; i < bookCount; i++) {
            IXMLDOMNode* pBook = NULL;
            hr = pAllBooks->get_item(i, &pBook);
            
            if (SUCCEEDED(hr) && pBook != NULL) {
                // 创建discount元素
                IXMLDOMElement* pDiscountElem = NULL;
                BSTR bstrDiscountTag = SysAllocString(L"discount");
                hr = pXMLDoc->createElement(bstrDiscountTag, &pDiscountElem);
                SysFreeString(bstrDiscountTag);

                if (SUCCEEDED(hr) && pDiscountElem != NULL) {
                    // 设置discount元素的文本内容
                    BSTR bstrDiscountValue = SysAllocString(L"10%");
                    hr = pDiscountElem->put_text(bstrDiscountValue);
                    SysFreeString(bstrDiscountValue);

                    // 将新元素添加到book节点
                    IXMLDOMNode* pInsertedNode = NULL;
                    hr = pBook->appendChild(pDiscountElem, &pInsertedNode);
                    
                    if (SUCCEEDED(hr) && pInsertedNode != NULL) {
                        pInsertedNode->Release();
                    }
                    pDiscountElem->Release();
                }
                
                /*****************************************************************
                * 演示替换节点(补充功能)
                *****************************************************************/
                
                // 查找year节点
                BSTR bstrYearPath = SysAllocString(L"year");
                IXMLDOMNode* pYearNode = NULL;
                hr = ((IXMLDOMElement*)pBook)->selectSingleNode(bstrYearPath, &pYearNode);
                SysFreeString(bstrYearPath);
                
                if (SUCCEEDED(hr) && pYearNode != NULL) {
                    // 创建新的year节点
                    IXMLDOMElement* pNewYearElem = NULL;
                    BSTR bstrYearTag = SysAllocString(L"year");
                    hr = pXMLDoc->createElement(bstrYearTag, &pNewYearElem);
                    SysFreeString(bstrYearTag);
                    
                    if (SUCCEEDED(hr) && pNewYearElem != NULL) {
                        // 设置新的年份值(原年份+1)
                        BSTR bstrOldYear;
                        pYearNode->get_text(&bstrOldYear);
                        
                        int year = _wtoi(bstrOldYear) + 1;
                        wchar_t newYear[10];
                        swprintf(newYear, 10, L"%d", year);
                        
                        BSTR bstrNewYear = SysAllocString(newYear);
                        pNewYearElem->put_text(bstrNewYear);
                        SysFreeString(bstrNewYear);
                        SysFreeString(bstrOldYear);
                        
                        // 替换节点
                        IXMLDOMNode* pReplacedNode = NULL;
                        hr = pBook->replaceChild(pNewYearElem, pYearNode, &pReplacedNode);
                        
                        if (SUCCEEDED(hr)) {
                            printf("  成功更新第%d本书的出版年份。\n", i+1);
                            pReplacedNode->Release();
                        }
                        
                        pNewYearElem->Release();
                    }
                    pYearNode->Release();
                }
                
                pBook->Release();
            }
        }
        pAllBooks->Release();
    }

    /*****************************************************************
    * 第八部分:保存修改后的XML
    *****************************************************************/
    
    // 创建VARIANT变量来指定输出文件名
    VARIANT varOutputFile;
    VariantInit(&varOutputFile);  // 初始化VARIANT
    varOutputFile.vt = VT_BSTR;   // 设置类型为BSTR
    varOutputFile.bstrVal = SysAllocString(L"modified_books.xml");

    // 保存XML文档到文件
    hr = pXMLDoc->save(varOutputFile);
    if (FAILED(hr)) {
        printf("错误:保存修改后的XML文件失败。\n");
    } else {
        printf("\n修改后的XML已保存到 modified_books.xml\n");
    }

    // 清理VARIANT变量
    VariantClear(&varOutputFile);

    /*****************************************************************
    * 第九部分:清理资源
    *****************************************************************/
    
    // 释放XML文档对象
    pXMLDoc->Release();
    
    // 反初始化COM库
    CoUninitialize();

    // 暂停控制台窗口(方便查看输出)
    system("pause");

    return 0;
}

8. 总结

本文详细介绍了如何在C++中使用MSXML进行XML解析、遍历、修改和保存,并提供了完整的代码示例和逐行注释。关键点包括:

  1. 初始化COM环境CoInitializeEx)。
  2. 创建MSXML DOM对象CoCreateInstance)。
  3. 加载XML文件load)。
  4. 遍历和修改节点get_childNodes, setAttribute)。
  5. 保存XMLsave)。

通过本教程,可以快速掌握MSXML的基本用法,并应用于实际项目中。

相关推荐
2401_8582861136 分钟前
CD27.【C++ Dev】类和对象(18)友元和内部类
开发语言·c++·类和对象
(王子变青蛙)38 分钟前
C++初始
开发语言·c++·程序人生
莫有杯子的龙潭峡谷38 分钟前
4.15 代码随想录第四十四天打卡
c++·算法
灋✘逞_兇1 小时前
快速幂+公共父节点
数据结构·c++·算法·leetcode
姜行运1 小时前
每日算法(双指针算法)(Day 1)
c++·算法·c#
十五年专注C++开发2 小时前
面试题:请描述一下你在项目中是如何进行性能优化的?针对哪些方面进行了优化,采取了哪些具体的措施?
开发语言·数据结构·c++·qt·设计模式·性能优化
爱奥尼欧2 小时前
C++基础语法(1)
c语言·c++
pixel32 小时前
C++ STL常见容器
c++
wuqingshun3141592 小时前
经典算法 判断一个图是不是树
数据结构·c++·算法·蓝桥杯·深度优先
别来无恙2022 小时前
数据结构(4)
数据结构·c++