You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

问题根源

  1. COM环境生命周期错误:CoInitialize(nil)和CoUninitialize在ProcessFile内部调用,而接口对象(IXMLDocument、IXMLNode)的引用释放可能延迟到CoUninitialize之后,此时COM环境已关闭,触发访问违例。
  2. 命名空间未处理: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 13:22:31