6

这不是一个真正的问题,因为我已经找到了解决方案。我花了很多时间,这就是为什么我想在这里解释一下。

Msxml 基于 COM,因此在 C++ 中使用它并不容易,即使您有有用的类来处理内存分配问题。但是编写一个新的 XML 解析器要困难得多,所以我想使用 msxml。

问题:

我能够在 Internet 上找到足够的示例来使用 msxml,借助CComPtr(智能指针避免必须手动为每个 IXMLDOMNode 调用 Release())、CComBSTR(将 C++ 字符串转换为字符串的 COM 格式)和CComVariant. 这 3 个有用的类是 ATL 类,需要一个#include <atlbase.h>.

问题:Visual Studio 2008 Express(免费版)不包括 ATL。

解决方案:

使用comutil.hand comdef.h,其中包括一些简单的辅助类:

  • _bstr_t或多或少地替换CComBSTR
  • _variant_t或多或少地替换CComVariant
  • _com_ptr_tCComPtr通过使用间接替换_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();
}
4

5 回答 5

7

也许尝试使用该#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....
于 2009-01-21T22:40:37.280 回答
1

另一种选择是使用另一个已经完成的 XML 解析器,例如 eXpat。它避免了必须使用 ATL 和 COM 的复杂性,并且比实现自己的要容易得多。我建议这样做只是因为您已经说过您查看 msxml 的原因是因为您不想实现自己的解析器。

于 2009-01-21T22:36:55.730 回答
1

您可以使用 TinyXML。它是开源的,并且更独立于平台。

于 2009-01-22T01:17:05.640 回答
1

我很高兴我发布了我的问题,尽管我已经有了一个解决方案,因为我有几个替代解决方案。感谢您的所有回答。

使用另一个解析器,如eXpat或可能更小的(不是那么强大,但足以满足我的需要)TinyXML实际上可能是一个好主意(并且可以更容易地将程序移植到另一个操作系统)。

使用#import指令,显然是微软特定的扩展来简化 COM 的使用,也很有趣,并把我带到了下面的网页MSXML in C++ but as elegant as in C#,它解释了如何尽可能简化 msxml 的使用。

于 2009-01-23T19:28:33.190 回答
0

为什么不使用一些 MSXML 包装器来保护您形成 COM,例如Arabica

于 2009-01-21T23:00:09.503 回答