I try to use TXMLDocument in Delphi to enumerate a XML file. But whenever I exit the ProcessFile procedure, there will be an "Access violation" exception.
I declare XMLFile as an interface object IXMLDocument, instead of TXMLDocument, and do not call free it manually.
Below is the code:
// Invoke ProcessFile in the main app
ProcessFile('C:\Myfile.xlf', True);
// Process the node recursively
procedure ProcessNode1(Node: IXMLNode);
var
Index: Integer;
begin
if (Node.NodeName = 'trans-unit') then
begin
// Process '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
// Initialize the COM, otherwise we cannot use MSXML
CoInitialize(nil);
// Open the XML document
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');
// Output the information
// If removing the following statement, then everything will be OK.
Writeln(Format('Call TranslateFile. OriginFile: %s. SrcFile: %s. SrcLang: %s. DstLang: %s. FixMode: %s.',
[OriginalFileName, ExtractFileName(SrcFileName), SrcLang, DstLang, BoolToStr(FixMode, True)]));
// Enumerate the DOM tree
ProcessNode1(FileNode);
finally
CoUninitialize;
end;
end;
Blow is the XML document
<?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>
You are calling CoUninitialize()
while the XmlFile
MainNode
, and FileNode
objects are still active, so the RTL can't release them properly when they go out of scope when ProcessFile()
exits, hence the Access Violation.
You need to clear those references before unloading the COM library, eg:
procedure ProcessFile(const SrcFileName: string; const FixMode: Boolean);
var
XmlFile: IXMLDocument;
MainNode, FileNode: IXMLNode;
...
begin
// Initialize the COM, otherwise we cannot use MSXML
CoInitialize(nil);
try
// Open the XML document
XmlFile := LoadXMLDocument(SrcFileName);
try
MainNode := XmlFile.DocumentElement;
FileNode := MainNode.ChildNodes['file'];
...
finally
// ADD THIS!
FileNode := nil;
MainNode := nil;
XmlFile := nil;
end;
finally
CoUninitialize;
end;
end;
Alternatively, separate the loading/unloading of the COM library from your tree processing code, eg:
procedure DoProcessFile(const SrcFileName: string; const FixMode: Boolean);
var
XmlFile: IXMLDocument;
MainNode, FileNode: IXMLNode;
...
begin
// Open the XML document
XmlFile := LoadXMLDocument(SrcFileName);
MainNode := XmlFile.DocumentElement;
FileNode := MainNode.ChildNodes['file'];
...
end;
procedure ProcessFile(const SrcFileName: string; const FixMode: Boolean);
begin
// Initialize the COM, otherwise we cannot use MSXML
CoInitialize(nil);
try
DoProcessFile(SrcFileName, FixMode);
finally
CoUninitialize;
end;
end;