MSXMLを使う

VC++からMSXMLを利用する方法です。
意外とあっさり出来ました。(参考資料のサイトを見ながら行ったら簡単に出来た)

まずはmsxml.dllから

MSXMLを利用できるようにするには#inportを利用してMSXML.DLLをインポートする必要があります。
詳しい解説は参考資料で紹介しているサイトを見てください(自分が良く分かっていない)
とりあえず、自分がインポートした時は、

#import "msxml.dll" named_guids raw_interfaces_only
using namespace MSXML;


とヘッダに記述しました。

この状態でビルドすると、debugフォルダにmsxml.tlhとmsxml3.tlhが生成されます。
これらが何かは良く分かりませんが、MSXMLを利用するために必要だということは分かります。

とにかくXMLを読み込む

参考資料のサイトのソース+αを使って解説。
サンプル
void GetRootTagName(){
    //COMの初期化
    if(FAILED(CoInitialize(NULL))){
        // エラー処理
        return;
    }
    IXMLDOMDocumentPtr pDoc;
    HRESULT hr = pDoc.CreateInstance(CLSID_DOMDocument);
    if(FAILED(hr)){
        // エラー処理
        return;
    }
    pDoc->put_async(VARIANT_FALSE); // load メソッドがロードを完了するまで待つようにする。
    VARIANT_BOOL f;
    hr = pDoc->load(_variant_t(L"http://majissuka.com/NewInfo.xml"), &f);// アドレスは架空ですのであしからず;
    if((hr != S_OK) || !f){
        // エラー処理
        return;
    }
    IXMLDOMElementPtr pElemRoot;
    pDoc->get_documentElement(&pElemRoot);
    if(NULL == pElemRoot){
        // エラー処理
        return;
    }
    CComBSTR bstrText;
    pElemRoot->get_tagName(&bstrText);
    MessageBoxW(NULL, bstrText, L"テストアプリケーション", MB_OK);
}

まず 2行目の宣言です。
これは、IXMLDOMDocumentPtrの変数を宣言しています。
このIXMLDOMDocumentPtrは、XMLそのものです。
3行目で、pDocのインスタンスを作成しています。
8行目で、10行目で利用するloadメソッドを呼び出したときに、ロードが完了するまで待つ設定を行います。
10行目で、XMLの読み込みを行っています。
15行目は、XMLのエレメントを指す変数を宣言しています。
16行目で、get_documentElement関数を利用し、ルートのエレメントを取得しています。
22行目で、ルートのエレメントの名前を取得しています。
このとき。CComBSTRの変数を利用して取得を行います。
これは、MSXMLから取得される文字列がUNICODEのためだと思われます。

子のノードにアクセスする

子のノードにアクセスするためには、get_childNodes関数を利用します。

IXMLDOMNodeListPtr nodeList;
nodeItem->get_childNodes(&nodeList);

の様に利用します。
IXMLDOMNodeListPtrは、ノードをリスト化したものです。
get_chilNodes関数は、呼び出したノードの子のノードのリストを取得する関数です。
ここで取得した子のノードのリストを利用することで子のノードにアクセスすることが可能になります。

IXMLDOMNodeListPtrの個々のノードにアクセスする場合は、get_item関数を利用します。

・・・・
LONG lLen;
IXMLDOMNodePtr nodeAttrItem;
nodeList->get_length(&lLen);
for(int i = 0; i < lLen; i++){
    nodeList->get_item(i, &nodeItem);
・・・
}


といった具合に行います。

ノードの情報を取得する

ノードの情報を取得する場合は、IXMLDOMNodePtrのメンバ変数を利用することで取得することが可能です。
関数解説
get_nodeName(CComBSTR bstrText) ノードの名前を取得します
get_attributes(IXMLDOMNamedNodeMapPtr nodeAttr) 属性を取得します
get_nodeValue(VARIANT val) 値を取得します
get_dataType(VARIANT type) タイプを取得します
とりあえずこれらの関数でXMLにアクセスが可能になります

半角スペースが省略されてしまう

テキストの部分の半角スペースが省略される場合があります。
これは、MSXMLの設定で半角スペースを省略する設定になっているからです(きっと)
半角スペースも取得したい場合は以下の方法があります。
1番目の方法は、XMLそのものを変えてしまう方法です。
この方法を行なうと、XML全体、またはタグごとに半角スペースを有効にするかどうかを決定することが可能です。

2番目の方法は、XMLを扱う側、つまりプログラムから指定する方法です。
(ただし、msxml3.dllのバージョンによっても動作が違うようで、preserveWhiteSpaceを変更しなくても取得できてしまったりします。)
preserveWhiteSpacepプロパティをVARIANT_TRUEにすることで、半角スペースを省略せず扱うことが可能です。
    IXMLDOMDocumentPtr pDoc;
    pDoc.CreateInstance( __uuidof(DOMDocument) ) ;
    pDoc->preserveWhiteSpace = VARIANT_TRUE;

ただし、この方法はテキストの部分をノードとして扱う為、データの構造が変わってきます。
また、xml:spaceが宣言されている場合に動作も変わってきます。
場合によっては既存の処理を結構修正する必要が出てきます。
これに関してはMicrosoftのpreserveWhiteSpace のMSの説明(FAQ)を読むと良いでしょう。
既存の処理を利用して半角スペースを有効にしたい場合は、1番目の方法が良いかと思います。

メモリリークへの対処

MSXMLを利用すると、メモリリークに遭遇することがある。
Release()関数を利用して解放することもできるが、NULLを代入することで、operator = 内で解放処理が動作するので、 NULLを代入するほうが良い。
Release関数の場合、やり方によってはアプリエラーが発生することがある。

経験上、こうしたほうがいいと言う例を挙げておく
    IXMLDOMNode* pRoot;
    ・・・・
    IXMLDOMNode* pNode;
    pRoot->get_nextSbling(pNode);
これは
    IXMLDOMNodePtr pRoot;
    ・・・・
    IXMLDOMNodePtr pNode = pRoot->GetnextSbling();
とするほうが良い。
また、ループをまわすような
    IXMLDOMNode* pNode;
    while(pNode->get_nextSbling(pNode)){
        ・・・
    }
と言う処理は
    IXMLDOMNode* pNode;
    IXMLDOMNodeList pNodeList = pRoot->GetchildNodes();
    LONG lLen = pNodeList->Getlength();
    for(LONG lLoop = 0; lLoop < lLen; lLoop++){
        pNode = pNodeList->Getitem(lLoop);
        ・・・
        pNode = NULL;
    }
    pNodeList = NULL;
とするほうが良い。
最初の例で、IXMLDOMNodePtrを使ったほうがよいというのは、ループを想定してのこと。

参考資料

デベロッパーズコーナー:DOMプログラミング講座 第1回:C++ アプリケーションから MSXML を使う
デベロッパーズコーナー:DOMプログラミング講座 第2回:C++ アプリケーションから MSXML を使う(その2)
preserveWhiteSpace のMSの説明(FAQ)(アーカイブコンテンツなのでなくなっている可能性があります)