Delphi中使用TXMLDocument枚举XML树触发访问违例问题求助
Delphi中使用IXMLDocument枚举XML时退出过程触发访问违例的解决方法
在Delphi中使用IXMLDocument接口操作XML文件,每次退出ProcessFile过程时都会触发Access violation异常。已将XmlFile声明为接口类型而非TXMLDocument类,且未手动调用Free方法。相关代码和XML文档如下:
相关代码
// 主程序中调用ProcessFile ProcessFile('C:\Myfile.xlf', True); // 递归处理节点 procedure ProcessNode1(Node: IXMLNode); var Index: Integer; begin if (Node.NodeName = 'trans-unit') then begin // 处理'trans-unit'节点 end else for Index := 0 to Node.ChildNodes.Count - 1 do ProcessNode1(Node.ChildNodes[Index]); end; procedure ProcessFile(const SrcFileName: string; const FixMode: Boolean); var XmlFile: IXMLDocument; MainNode, FileNode: IXMLNode; OriginalFileName, SrcLang, DstLang: string; begin // 初始化COM,否则无法使用MSXML CoInitialize(nil); // 打开XML文档 XmlFile := TXMLDocument.Create(nil); try XmlFile.LoadFromFile(SrcFileName); // XmlFile.Active := True; MainNode := XmlFile.DocumentElement; FileNode := MainNode.ChildNodes['file']; OriginalFileName := FileNode.GetAttribute('original'); SrcLang := FileNode.GetAttribute('source-language'); DstLang := FileNode.GetAttribute('target-language'); // 输出信息 // 移除下面这行代码的话,程序就能正常运行 Writeln(Format('调用TranslateFile. 源文件: %s. 输入文件: %s. 源语言: %s. 目标语言: %s. 修复模式: %s.', [OriginalFileName, ExtractFileName(SrcFileName), SrcLang, DstLang, BoolToStr(FixMode, True)])); // 遍历DOM树 ProcessNode1(FileNode); finally CoUninitialize; end; end;
XML文档
<?xml version="1.0" encoding="utf-16"?> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd" xmlns:soluling="http://www.soluling.com"> <file original="a.rc" source-language="en-US" target-language="bg-BG" datatype="plaintext"> <body> <group id="String" datatype="winres"> <group id="IDS_TEST"> <trans-unit id="String.IDS_TEST1"> <source>a</source> <target state="translated" state-qualifier="fuzzy-match">b</target> <soluling:datatype>string</soluling:datatype> <soluling:rowstatus>rsInUse</soluling:rowstatus> </trans-unit> </group> </group> </body> </file> </xliff>
问题根源
- COM环境生命周期错误:
CoInitialize(nil)和CoUninitialize在ProcessFile内部调用,而接口对象(IXMLDocument、IXMLNode)的引用释放可能延迟到CoUninitialize之后,此时COM环境已关闭,触发访问违例。 - 命名空间未处理:XML使用了默认命名空间
urn:oasis:names:tc:xliff:document:1.2,直接用ChildNodes['file']查找会返回空节点,后续操作引发隐性内存问题。
修复步骤
1. 调整COM初始化时机
将COM初始化移到程序入口,确保整个程序生命周期内COM环境有效:
program XmlApp; begin CoInitialize(nil); try // 主程序逻辑,调用ProcessFile ProcessFile('C:\Myfile.xlf', True); finally CoUninitialize; end; end.
同时删除ProcessFile内部的CoInitialize和CoUninitialize代码。
2. 正确处理XML命名空间
使用命名空间管理器查找节点:
procedure ProcessFile(const SrcFileName: string; const FixMode: Boolean); var XmlFile: IXMLDocument; MainNode, FileNode: IXMLNode; OriginalFileName, SrcLang, DstLang: string; NSManager: TXmlNamespaceManager; begin XmlFile := TXMLDocument.Create(nil); try XmlFile.LoadFromFile(SrcFileName); XmlFile.Active := True; // 显式激活文档 MainNode := XmlFile.DocumentElement; NSManager := TXmlNamespaceManager.Create(MainNode.OwnerDocument); try NSManager.AddNamespace('xlf', 'urn:oasis:names:tc:xliff:document:1.2'); FileNode := MainNode.SelectSingleNode('xlf:file', NSManager); finally NSManager.Free; end; if Assigned(FileNode) then begin OriginalFileName := FileNode.GetAttribute('original'); SrcLang := FileNode.GetAttribute('source-language'); DstLang := FileNode.GetAttribute('target-language'); Writeln(Format('调用TranslateFile. 源文件: %s. 输入文件: %s. 源语言: %s. 目标语言: %s. 修复模式: %s.', [OriginalFileName, ExtractFileName(SrcFileName), SrcLang, DstLang, BoolToStr(FixMode, True)])); ProcessNode1(FileNode); end; finally // 接口对象无需手动释放,引用计数归零自动清理 end; end;
3. 递归节点处理增加空指针检查
procedure ProcessNode1(Node: IXMLNode); var Index: Integer; begin if not Assigned(Node) then Exit; // 用LocalName避免命名空间前缀干扰 if (Node.LocalName = 'trans-unit') then begin // 处理'trans-unit'节点 end else for Index := 0 to Node.ChildNodes.Count - 1 do ProcessNode1(Node.ChildNodes[Index]); end;
内容的提问来源于stack exchange,提问作者alancc
相关产品推荐
相关产品推荐

