Pregunta ¿Cómo usar msxml con Visual Studio 2008 Express (sin clases de ATL) sin volverse loco?


No es realmente una pregunta porque ya he encontrado una solución. Me tomó mucho tiempo, por eso quiero explicarlo aquí.

Msxml se basa en COM, por lo que no es realmente fácil de usar en C ++, incluso cuando tiene clases útiles para tratar los problemas de asignación de memoria. Pero escribir un nuevo analizador XML sería mucho más difícil, así que quise usar msxml.

El problema:

Pude encontrar suficientes ejemplos en Internet para usar msxml con la ayuda de CComPtr (puntero inteligente para evitar tener que llamar a Release () para cada IXMLDOMNode manualmente), CComBSTR (para convertir cadenas C ++ al formato COM para cadenas) y CComVariant. Estas 3 clases útiles son clases ATL y necesitan una #include <atlbase.h>.

Problema: Visual Studio 2008 Express (la versión gratuita) no incluye ATL.

Solución:

Utilizar comutil.h y comdef.h, que incluyen algunas clases de ayuda simples:

  • _bstr_t reemplaza más o menos CComBSTR
  • _variant_t reemplaza más o menos CComVariant
  • _com_ptr_t reemplaza indirectamente CComPtr mediante el uso de _COM_SMARTPTR_TYPEDEF

Pequeño ejemplo:

#include <msxml.h>
#include <comdef.h>
#include <comutil.h>

// Define some smart pointers for MSXML
_COM_SMARTPTR_TYPEDEF(IXMLDOMDocument,     __uuidof(IXMLDOMDocument));     // IXMLDOMDocumentPtr
_COM_SMARTPTR_TYPEDEF(IXMLDOMElement,      __uuidof(IXMLDOMElement));      // IXMLDOMElementPtr
_COM_SMARTPTR_TYPEDEF(IXMLDOMNodeList,     __uuidof(IXMLDOMNodeList));     // IXMLDOMNodeListPtr
_COM_SMARTPTR_TYPEDEF(IXMLDOMNamedNodeMap, __uuidof(IXMLDOMNamedNodeMap)); // IXMLDOMNamedNodeMapPtr
_COM_SMARTPTR_TYPEDEF(IXMLDOMNode,         __uuidof(IXMLDOMNode));         // IXMLDOMNodePtr

void test_msxml()
{
    // This program will use COM
    CoInitializeEx(NULL, COINIT_MULTITHREADED);

    {
        // Create parser
        IXMLDOMDocumentPtr pXMLDoc;
        HRESULT hr = CoCreateInstance(__uuidof (DOMDocument), NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void**)&pXMLDoc);
        pXMLDoc->put_validateOnParse(VARIANT_FALSE);
        pXMLDoc->put_resolveExternals(VARIANT_FALSE);
        pXMLDoc->put_preserveWhiteSpace(VARIANT_FALSE);

        // Open file
        VARIANT_BOOL bLoadOk;
        std::wstring sfilename = L"testfile.xml";
        hr = pXMLDoc->load(_variant_t(sfilename.c_str()), &bLoadOk);

        // Search for node <testtag>
        IXMLDOMNodePtr pNode;
        hr = pXMLDoc->selectSingleNode(_bstr_t(L"testtag"), &pNode);

        // Read something
        _bstr_t bstrText;
        hr = pNode->get_text(bstrText.GetAddress());
        std::string sSomething = bstrText;
    }

    // I'm finished with COM
    // (Don't call before all IXMLDOMNodePtr are out of scope)
    CoUninitialize();
}

6
2018-01-21 22:25


origen


Respuestas:


Tal vez intente usar el #import declaración.

Lo he usado en un proyecto VS6 que tengo por ahí, haces algo como esto (solo con fines ilustrativos; esto funcionó para mí, pero no pretendo ser a prueba de errores):

#import  "msxml6.dll"

  ...

MSXML2::IXMLDOMDocument2Ptr pdoc;
HRESULT hr = pdoc.CreateInstance(__uuidof(MSXML2::DOMDocument60));
if (!SUCCEEDED(hr)) return hr;

MSXML2::IXMLDOMDocument2Ptr pschema;
HRESULT hr = pschema.CreateInstance(__uuidof(MSXML2::DOMDocument60));
if (!SUCCEEDED(hr)) return hr;

pschema->async=VARIANT_FALSE;
VARIANT_BOOL b;
b = pschema->loadXML(_bstr_t( /* your schema XML here */ ));

MSXML2::IXMLDOMSchemaCollection2Ptr pSchemaCache;
hr = pSchemaCache.CreateInstance(__uuidof(MSXML2::XMLSchemaCache60));
if (!SUCCEEDED(hr)) return hr;

_variant_t vp=pschema.GetInterfacePtr();
pSchemaCache->add(_bstr_t( /* your namespace here */ ),vp); 

pdoc->async=VARIANT_FALSE;
pdoc->schemas = pSchemaCache.GetInterfacePtr();
pdoc->validateOnParse=VARIANT_TRUE;
if (how == e_filename)
    b = pdoc->load(v);
else
    b = pdoc->loadXML(bxmldoc);

pXMLError = pdoc->parseError;
if (pXMLError->errorCode != 0)
    return E_FAIL; // an unhelpful return code, sigh....

7
2018-01-21 22:40



Otra opción sería usar otro analizador XML que ya está hecho, como eXpat. Evita tener que usar ATL y las complejidades de COM, y es mucho más fácil que implementar el suyo. Sugiero esto solo porque has dicho que la razón por la que estás viendo msxml es porque no quieres implementar tu propio analizador.


1
2018-01-21 22:36



Puedes usar TinyXML. Es de código abierto y más independiente de la plataforma.


1
2018-01-22 01:17



Estoy feliz de haber publicado mi pregunta, aunque ya tenía una solución porque obtuve varias soluciones alternativas. Gracias por todas sus respuestas.

Usando otro analizador como eXpat o quizás el más pequeño (no tan poderoso pero suficiente para mis necesidades) TinyXML en realidad podría ser una buena idea (y facilitar el transporte del programa a otro sistema operativo).

Usando un #import La directiva, aparentemente una extensión específica de Microsoft para simplificar el uso de COM, también es interesante y me llevó a la siguiente página web MSXML in C++ but as elegant as in C#, que explica cómo simplificar el uso de msxml tanto como sea posible.


1
2018-01-23 19:28



¿Por qué no usa algún contenedor MSXML que lo proteja de COM, como Arábica?


0
2018-01-21 23:00