Как использовать msxml с Visual Studio 2008 Express (без классов ATL), не сходя с ума? - PullRequest
6 голосов
/ 22 января 2009

Это не совсем вопрос, потому что я уже нашел решение. Это заняло у меня много времени, поэтому я хочу объяснить это здесь.

Msxml основан на COM, поэтому его не так просто использовать в C ++, даже если у вас есть полезные классы для решения вопросов выделения памяти. Но написание нового синтаксического анализатора XML было бы намного сложнее, поэтому я хотел использовать msxml.

Проблема:

Мне удалось найти достаточно примеров в Интернете, чтобы использовать msxml с помощью CComPtr (умный указатель, чтобы не вызывать Release () для каждого IXMLDOMNode вручную), CComBSTR (для преобразования строк C ++ в Формат COM для строк) и CComVariant. Эти 3 полезных класса являются классами ATL и требуют #include <atlbase.h>.

Проблема: Visual Studio 2008 Express (бесплатная версия) не включает ATL.

Решение:

Используйте comutil.h и comdef.h, которые включают несколько простых вспомогательных классов:

  • _bstr_t заменяет более или менее CComBSTR
  • _variant_t заменяет более или менее CComVariant
  • _com_ptr_t заменяет косвенно CComPtr с помощью _COM_SMARTPTR_TYPEDEF

Маленький пример:

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

Ответы [ 5 ]

7 голосов
/ 22 января 2009

Возможно, попробуйте использовать оператор #import.

Я использовал его в проекте VS6, который у меня есть, вы делаете что-то вроде этого (только в иллюстративных целях; это работает для меня, но я не претендую на защиту от ошибок):

#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....
1 голос
/ 23 января 2009

Я рад, что опубликовал свой вопрос, хотя у меня уже было решение, потому что я получил несколько альтернативных решений. Спасибо за все ваши ответы.

Использование другого парсера, такого как eXpat или, возможно, меньшего размера (не такого мощного, но достаточного для моих нужд) TinyXML может быть хорошей идеей (и упростить портирование программа для другой операционной системы).

Использование директивы #import, по-видимому, специфического для Microsoft расширения для упрощения использования COM, также интересно и привело меня на следующую веб-страницу MSXML in C++ but as elegant as in C#, на которой объясните, как максимально упростить использование msxml.

1 голос
/ 22 января 2009

Вы можете использовать TinyXML. Это открытый исходный код и более независимый от платформы.

1 голос
/ 22 января 2009

Другой вариант - использовать уже созданный анализатор XML, , например, eXpat . Это избавляет от необходимости использовать ATL и сложности COM, и это намного проще, чем реализация собственного. Я предлагаю это только потому, что вы заявили, что причина, по которой вы смотрите msxml, заключается в том, что вы не хотите реализовывать свой собственный анализатор.

0 голосов
/ 22 января 2009

Почему бы вам не использовать какую-нибудь оболочку MSXML, которая защитит вас от COM, например, Arabica ?

...