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);
}