MASATOの開発日記


前の開発日記 次の開発日記 一覧

2002/05/05

Windowsでは、COMオブジェクトとの文字列のやり取りは、BSTRが基本のようです。 BSTRは、ワイド文字列、即ちUnicodeの文字列のことです。 全世界からみた汎用性で考えると、これは正しい選択だと思いますが、 Shift_JISの世界からみると、毎回毎回変換の手間があって面倒です。

しかもBSTRは、SysAllocString等のAPIを用いてメモリを確保し、 SysFreeString等のAPIを用いてメモリを解放しなければならないので、これもまた結構面倒です。 しかも、newやmallocと違い、メモリリークを起こしても検出されません。 という訳で、VC++でCOMオブジェクトを扱うのは大変だというのが正直な思いです。 VBだと簡単なんですけれどねぇ・・・

XMLの文字コード

XML対応アプリケーションと言うためには、どんな文字コードで書かれた XMLを読み込めれば良いのだろうかと調べてみたところ、 UTF-8とUTF-16だけ読み込めれば問題無いようです。この2つが必須というわけです。 Shift_JIS等、他のローカルな文字コードで書かれたXMLを読み込めても問題無いのですが、 それはあくまでオプションであって、必須では無いようです。 もしかして時代は全ての文字がUNICODEで書かれる時代になってきたのでしょうか。 しかし、Shift_JISを切り捨てるのはまだ早すぎると思います。 WinNT系でなければ動作しないプログラムというのはまだちょっと人気が出ないでしょう。 しかし、Microsoft製のDocument Object Model(DOM)はしっかりとShift_JISまでサポートしてくれます。 これを使えば、自然とShift_JISにも対応したXML対応アプリケーションが作成できるわけです。 Shift_JISという文字コード自体もMicrosoft製なので、サポートするのはある意味当然なのですが、 Shift_JISにどっぷり漬かった私にとってShif_JISをサポートしてくれるというのは非常にありがたいものです。

Microsoft XML DOMの使い方

環境Visual C++ 6.0

Microsoft製のXML DOMの使い方を説明します。

IE5.0以上がインストールされていれば自動的にMicrosoft XML DOMもインストールされていますので、 下記のコードも動作するはずです。

VC++でこれを扱うのは少々面倒ですので、 前もってJScript等の簡単にCOMオブジェクトを扱える言語で 予習しておくと楽かと思います。

最初にXMLファイルを用意する必要があります。 test.xmlというファイル名で、以下のようなXMLファイルを用意しました。

<?xml version="1.0" encoding="Shift_JIS"?>
<Test>
  <Item>アイテム1</Item>
  <Item>アイテム2</Item>
  <Item>アイテム3</Item>
</Test>

このファイルから、アイテム1、アイテム2、アイテム3という文字列を読み込むのが目標です。

まずは、Microsoft XML DOMをインポートする必要があります。 ヘッダファイルをインクルードするのとほぼ同じ感覚です。 コードは次のようになります。

#import <msxml.dll> named_guids raw_interfaces_only
using namespace MSXML;

このディレクティブにより、msxml.tlhとmsxml.tliというファイルが作成されるはずです。 msxml.tlhがヘッダファイル、msxml.tliがソースファイルです。 #import文により、tliのコンパイルやtlhのインクルードは自動的に行われるようですので、 #import文以外に何かする必要はありません。

初期化用のコードは次の様になります。

// ●初期化●
::CoInitialize(NULL);
IXMLDOMDocument* lpXMLDoc;
::CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void**)&lpXMLDoc);

普通のCOMの初期化と同様です。CLSID_DOMDocumentとIID_IXMLDOMDocumentは、 msxml.tlhの中に定義されています。

test.xmlを読み込むコードは以下のようになります。

// ●ファイルから読み込む。●
lpXMLDoc->put_async(VARIANT_FALSE);
VARIANT FilePath;
::VariantInit(&FilePath);
FilePath.vt = VT_BSTR;
V_BSTR(&FilePath) = ::SysAllocString(L"test.xml");
VARIANT_BOOL result;
lpXMLDoc->load(FilePath, &result);
::VariantClear(&FilePath);

読み込み失敗等のエラーチェックはしていませんのでご注意下さい。

文字列を取得するコードは次の様になります。

// ●ノード検索●	
IXMLDOMNodeList* lpNodeList;
BSTR NodePath = ::SysAllocString(L"Test/Item");
lpXMLDoc->selectNodes(NodePath, &lpNodeList);
::SysFreeString(NodePath);
long lLength;
lpNodeList->get_length(&lLength);
for (int i = 0; i < lLength; i++){
  IXMLDOMNode* lpItem;
  lpNodeList->get_item(i, &lpItem);
  // ●テキスト取得●
  BSTR String1;
  lpItem->get_text(&String1);
  UINT iLength1 = SysStringLen(String1) + 1;
  // ●Shift_JISへ変換●
  int iLength2 = ::WideCharToMultiByte(CP_ACP, 0, String1, iLength1, 0, NULL, NULL, NULL);
  LPSTR String2 = new CHAR[iLength2];
  ::WideCharToMultiByte(CP_ACP, 0, String1, iLength1, String2, iLength2, NULL, NULL);
  // ●テキスト表示●
  printf("%s\n", String2);
  ::SysFreeString(String1);
  delete String2;
  lpItem->Release();
}
lpNodeList->Release();

Unicodeのまま表示できる環境ですと もっと単純なコードになりそうです。

最後に、解放処理をして終わりです。

// ●解放処理●
lpXMLDoc->Release();
::CoUninitialize();

単純なことをするだけのコードなのにだいぶ複雑になってしまいました。 とりあえずこれは、Microsoft XML DOMの最初の一歩なので、色々と応用してみて下さい。

リソースからXMLファイルを読み込む方法

環境Visual C++ 6.0

Microsoft XML DOMを用いて、ファイルからではなくリソースからXMLファイルを読み込むことも できます。

例えば、上記のtest.xmlを、リソースに追加したとします。 そのときのリソースタイプを"TXT"、リソースIDをIDR_TXT1としますと、 ファイルから読み込むコードを以下のコードに置き換えればリソースから読み込めます。

// ●リソースから読み込む。●
lpXMLDoc->put_async(VARIANT_FALSE);
HMODULE hModule = ::GetModuleHandle(NULL);
HRSRC hRsrc = ::FindResource(hModule, MAKEINTRESOURCE(IDR_TXT1), "TXT");
DWORD dwSize = ::SizeofResource(hModule, hRsrc);
HGLOBAL hGlobal = ::LoadResource(hModule, hRsrc);
LPSTR lpData = (LPSTR)::LockResource(hGlobal);
// ●Unicodeに変換する。●
int iResLength = ::MultiByteToWideChar(CP_ACP, 0, lpData, dwSize, NULL, 0);
BSTR ResString = ::SysAllocStringLen(NULL, iResLength);
::MultiByteToWideChar(CP_ACP, 0, lpData, dwSize, ResString, iResLength);
// ●文字列から読み込む。●
VARIANT_BOOL result;
lpXMLDoc->loadXML(ResString, &result);
::SysFreeString(ResString);

一言で言えば、loadXMLメソッドを使用する。ただそれだけです。

test.xmlがShift_JISでしたのでMultiByteToWideCharを用いてUnicodeに変換していますが、 test.xml自体をUnicodeにすれば、変換の必要なくそのままloadXMLに渡せるでしょう。 ただし、BOM(Byte Order Mark)が付いているとloadXMLが失敗しますので、 BOMを自力で外してloadXMLに文字列を渡す必要があります。

前の開発日記 次の開発日記 一覧