Dicas Importantes ao Usar MSXML2 em Aplicações C++

O MSXML2 é uma biblioteca robusta para manipulação de arquivos XML, mas exige cuidados específicos para evitar falhas. Abaixo, apresentamos práticas recomendadas com exemplos de código refatorados.

1. Inicialização e Liberação Correta do Componente

Sempre inicialize o COM com CoInitialize e finalize com CoUninitialize. O objeto DOM deve ser liberado após o uso.

IXMLDOMDocumentPtr pDoc = nullptr;
if (caminhoArquivo == nullptr) return nullptr;

::CoInitialize(nullptr);
HRESULT hr = pDoc.CreateInstance(__uuidof(DOMDocument60));
if (FAILED(hr)) {
    ::CoUninitialize();
    return nullptr;
}

if (pDoc->load(caminhoArquivo) == VARIANT_FALSE) {
    pDoc.Release();
    ::CoUninitialize();
    return nullptr;
}
// ... processamento ...
pDoc.Release();
::CoUninitialize();

2. Criação de um Novo Arquivo XML

Ao gerar um XML do zero, defina preserveWhiteSpace como VARIANT_TRUE para garantir formatação.

CString nomeArquivo;
nomeArquivo.Format("./dados/%s.xml", nome);

HANDLE hArquivo = CreateFile(nomeArquivo, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (hArquivo == INVALID_HANDLE_VALUE) {
    ::CoInitialize(nullptr);
    IXMLDOMDocumentPtr pDoc;
    pDoc.CreateInstance(__uuidof(DOMDocument60));

    pDoc->preserveWhiteSpace = VARIANT_TRUE;
    IXMLDOMProcessingInstructionPtr pInstrucao = pDoc->createProcessingInstruction(
        (_bstr_t)"xml", (_bstr_t)"version=\"1.0\" encoding=\"utf-8\"");
    pDoc->appendChild(pInstrucao);

    IXMLDOMElementPtr pRaiz = pDoc->createElement((_bstr_t)"db");
    pDoc->PutRefdocumentElement(pRaiz);
    pDoc->documentElement->appendChild(pDoc->createTextNode("\n\t"));

    IXMLDOMCommentPtr pComentario = pDoc->createComment((_bstr_t)"Banco de dados");
    pRaiz->appendChild(pComentario);

    pDoc->save((LPSTR)(LPCSTR)nomeArquivo);
    pDoc.Release();
    // CoUninitialize(); // Descomente se não houver outras chamadas COM
}
CloseHandle(hArquivo);

3. Verificação de Existência de Nós

Use selectNodes para obter uma lista e verifique se não é nula ou vazia.

IXMLDOMNodeListPtr listaPessoas = pDoc->selectNodes("register/person");
if (listaPessoas == nullptr || listaPessoas->Getlength() == 0) {
    // Tratamento para nós ausentes
} else {
    // Processamento da lista
}

4. Lietura de Atributos com Tratamento de Nulos

Ao obter atributos opcionais, valide o tipo VARIANT para evitar falhas.

for (long i = 0; i < listaPessoas->Getlength(); i++) {
    IXMLDOMNodePtr item = listaPessoas->Getitem(i);
    _variant_t varNome  = ((IXMLDOMElementPtr)item)->getAttribute("name");
    _variant_t varArquivo = ((IXMLDOMElementPtr)item)->getAttribute("file");
    _variant_t varData  = ((IXMLDOMElementPtr)item)->getAttribute("date");
    _variant_t varScore = ((IXMLDOMElementPtr)item)->getAttribute("score");

    pessoa.nome = _tcsdup(CString(varNome.bstrVal));
    TCHAR buffer[1024] = {0};
    _tcscpy(buffer, CString(varArquivo.bstrVal));
    pessoa.arquivo = _tcsdup(buffer);
    pessoa.data = _ttoi((LPCTSTR)(_bstr_t)varData);

    if (varScore.vt != VT_NULL) {
        pessoa.score = _ttoi((LPCTSTR)(_bstr_t)varScore);
    } else {
        pessoa.score = -1;
    }
    listaPessoas->AddTail(&pessoa);
}

5. Exclusão de um Nó Específico

Selecione o nó alvo com selectSingleNode e remova-o pelo pai.

CString caminhoXML;
caminhoXML.Format("./config/%s.xml", nome);
::CoInitialize(nullptr);
IXMLDOMDocumentPtr pDoc;
pDoc.CreateInstance(__uuidof(DOMDocument60));
if (pDoc->load((LPSTR)(LPCSTR)caminhoXML) == VARIANT_FALSE) {
    pDoc.Release();
    ::CoUninitialize();
    return 2;
}

CString consulta;
consulta.Format("db/item[@name='%s']", nome);
IXMLDOMNodePtr noAlvo = pDoc->selectSingleNode(_bstr_t(consulta));
if (noAlvo != nullptr) {
    noAlvo->GetparentNode()->removeChild(noAlvo);
}
pDoc->save((LPSTR)(LPCSTR)caminhoXML);
pDoc.Release();
::CoUninitialize();

6. Atualização de um Nó (Remover Filhos e Adicionar Novos)

Para alterar dados de um nó, remova todos os filhos e reinsira os novos valores.

CString consulta;
consulta.Format("db/item[@name='%s']/features/feature", nome);
IXMLDOMNodePtr noFeature = pDoc->selectSingleNode(_bstr_t(consulta));
if (noFeature != nullptr) {
    ((IXMLDOMElementPtr)noFeature)->setAttribute("date", tempoAtual);

    IXMLDOMNodeList* pListaFilhos = nullptr;
    noFeature->get_childNodes(&pListaFilhos);
    if (pListaFilhos != nullptr) {
        // Percorre de trás para frente para evitar problemas de indexação
        for (long i = pListaFilhos->Getlength() - 1; i >= 0; i--) {
            IXMLDOMNodePtr filho;
            pListaFilhos->get_item(i, &filho);
            noFeature->removeChild(filho);
        }
        pListaFilhos->Release();
    }

    for (int j = 0; j < tamanhoVetor; j++) {
        IXMLDOMNodePtr noFloat = pDoc->createElement("f");
        CString valor;
        valor.Format("%.8f", vetorFeatures[j]);
        noFloat->put_text(_bstr_t(valor));
        noFeature->appendChild(noFloat);
    }
}
pDoc->save((LPSTR)(LPCSTR)caminhoXML);

7. Tratamento de Erros no Salvamento

Se o save falhar, certifique-se de que não há referências pendentes ao documento. Use blocos try/catch para depuração.

try {
    pDoc->save((LPSTR)(LPCSTR)caminhoXML);
} catch (_com_error& erro) {
    // Log: HRESULT = %d, Descrição: %s
    Debug_TraceA("Falha ao salvar XML: HRESULT = %d, Descricao = %s\n", erro.Error(), (LPCTSTR)erro.Description());
}

Atenção: Cuidado com Iteração e Remoção Simultânea

Ao remover filhos de um nó enquanto percorre a lista, nunca use um loop crescente (for i = 0; i < tamanho; i++) porque a lista se modifica a cada remoção. Sempre itere de trás para frente ou use um método que não dependa do índice após a remoção.

// Correto: percorrer de trás para frente
for (long i = lista->Getlength() - 1; i >= 0; i--) {
    IXMLDOMNodePtr no;
    lista->get_item(i, &no);
    pai->removeChild(no);
}

Tags: MSXML2 COM VC++ XML DOMDocument60

Publicado em 6-17 02:23