ね、ハマるでしょ?
環境
いつも通り、サポート切れのXE5。
基本構文
xmlファイルを新規作成します。
var
XML : TXMLDocument;
RootNode, ChildNode : IXMLNode;
begin
XML := TXMLDocument.Create(nil);
XML.Active := True;
RootNode := XML.AddChild('Root');
ChildNode := RootNode.AddChild('Child');
ChildNode.Text := 'ガキ';
XML.SaveToFile('D:\Doc.xml');
XML.Active := False;
FreeAndNil(FResultXML);
end;
ここでハマったよその1 別スレッドで上記を操作
こんなエラーが出ました。
「DOMException Microsoft MSXML がインストールされていません」
Microsoft MSXMLはTXMLDocumentでデフォルトで使用するものです。
当初、このエラーを素直に読み取ったので、
「あれ、コンポーネント配置してない(IDEのポトペタで作ってない)から、デフォルト値がうまく読み込めてないのかな?」
などと思い、
XML.DOMVendor := MSXML_DOM;
を追記しました。
が、それでもエラーが解消されません。
「ということは、ポトペタ配置と他にもプロパティが違う場所があるのか、おまじないが必要なのか」
と探すも、それらしいものが見つからず。
結論:仕様です
下記の「エラーハンドリングおかしくね?」というページを発見。
さらにこんなページたちも。
原因と解決策
・エラーメッセージが微妙に意図しているところと違う。
・GUI経由、つまりはVCLフォームを表示しているメインスレッドならCOMは普通に呼び出せるが、
別スレッドの場合はCoInitialize 、CoUninitialize を呼び出してやらなくてはならない。
ここでハマったよその2 TXMLDocumentインスタンスが勝手に破棄される
実際にプログラミングしたのは、上記基本構文よりもちょっとだけ複雑で、
Nodeを作るところ・そのためのデータをとってくるところや、SaveToFileするクラスは別に作っています。
そんなこんなで、SaveToFileしようとすると、「Activeでないよ」というエラーが発生するように。
あれ?
もしかして勝手にクローズしちゃうのかな?
と思って直前でActiveを活性化させても同じ。
なんでやねん、と思って追ってみるとTXMLDocumentインスタンスが死んでいる……
ウソみたいだろ、Freeしてないのに解放されているんだぜ。
結論:仕様です
「作成時に Owner が設定されていない TXMLDocument は、インターフェイス オブジェクトと同様の動作をします。 つまり、そのインターフェイスに対する参照がすべて解放されると、TXMLDocument インスタンスは自動的に解放されます。」
なんでそんな動作なんだよ(怒)
「インターフェイス オブジェクトと同様の動作」なんてさせるくらいなら最初からインターフェースで返せよ!分からんだろうが!
解決策
というわけで、Owner を設定してやれば解決します。
適当なTComponentインスタンスを生成してCreateの引数に渡してやりましょう。
総括:今回ハマったところを全部のせるとこんな感じ。
var
XML : TXMLDocument;
RootNode, ChildNode : IXMLNode;
OwnerComponent : TComponent;
begin
CoInitialize(nil);
XML := TXMLDocument.Create(OwnerComponent);
XML.Active := True;
RootNode := XML.AddChild('Root');
ChildNode := RootNode.AddChild('Child');
ChildNode.Text := 'ガキ';
XML.SaveToFile('D:\Doc.xml');
XML.Active := False;
FreeAndNil(FResultXML);
FreeAndNil(OwnerComponent);
CoUninitialize;
end;