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

Inno Setup中Variant类型(MSXML)正确用法及二次调用崩溃问题

问题:Inno Setup中使用MSXML Variant类型二次调用触发Access Violation异常

在Inno Setup中使用MSXML的MSXML2.DOMDocument.6.0时,遇到Variant类型处理问题:第二个创建元素的函数被调用两次会触发Access Violation异常,而第一个函数可正常运行。

工作函数示例

function CreateElementWorking(xmlDocument: Variant; name : String) : Variant;
var
    xmlNewElement : Variant;
begin
    Result := xmlDocument.createElement(name);
    xmlNewElement := Result;

    // 对新创建的元素进行操作
    AddAttributes(xmlDocument, xmlNewElement);
end;

崩溃函数示例

function CreateElementFailing(xmlDocument: Variant; name : String) : Variant;
var
    xmlNewElement : Variant;
begin
    xmlNewElement := xmlDocument.createElement(name);
    
    // 对新创建的元素进行操作
    AddAttributes(xmlDocument, xmlNewElement);
    
    // 第二次调用时,Result保留了首次调用的旧值,赋值时触发Access Violation异常
    Result := xmlNewElement;
end;

二次调用崩溃的推测原因是:Inno Setup的Pascal脚本会保留函数Result的旧值,未在每次调用时初始化。第一个函数可正常工作,但不清楚原理。想知道是否可以在赋值Result := xmlNewElement前重置Result,或者当前的Variant类型处理方式是否完全错误?

崩溃最小复现示例

function AddAttributes(xmlDocument, xmlElement: Variant) : Variant;
begin
end;

function DoTest() : Boolean;
var
    xmlDocument    : Variant;
    xmlElement     : Variant;
    xmlRootElement : Variant;
begin
    xmlDocument       := CreateOleObject('MSXML2.DOMDocument.6.0');
    xmlDocument.async := False;
    xmlDocument.setProperty('SelectionLanguage', 'XPath');

    xmlRootElement := xmlDocument.selectSingleNode('/');

    xmlElement := CreateElementFailing(xmlDocument, 'Test');
    xmlRootElement.appendChild(xmlElement);
    xmlRootElement := xmlDocument.selectSingleNode('/Test');

    xmlElement := CreateElementFailing(xmlDocument, 'Test');  // 此处崩溃
    xmlRootElement.appendChild(xmlElement);
    
    // 预期生成XML:
    // <Test><Test/></Test>
end;    
原因分析与解决方案

核心原因

Inno Setup的Pascal脚本引擎中,函数返回值Result(尤其是Variant类型)不会在每次函数调用时自动初始化或释放前一次的引用。当第二次调用CreateElementFailing时,Result还持有第一次调用返回的COM对象引用,直接赋值新的Variant会导致旧COM对象的引用计数处理异常,进而触发Access Violation。

而CreateElementWorking能正常运行的原因是:函数一开始就给Result赋值了新的COM对象,覆盖了之前的旧引用,避免了旧引用未释放导致的冲突。

解决方案

有两种可靠的处理方式:

1. 提前初始化Result为Unassigned

在函数开头先将Result设置为Unassigned,清空旧的引用,再进行后续操作:

function CreateElementFixed(xmlDocument: Variant; name : String) : Variant;
var
    xmlNewElement : Variant;
begin
    Result := Unassigned; // 重置Result,释放旧引用
    xmlNewElement := xmlDocument.createElement(name);
    
    AddAttributes(xmlDocument, xmlNewElement);
    Result := xmlNewElement;
end;

2. 直接在开头给Result赋值(参考工作函数的写法)

和CreateElementWorking一样,先将新创建的元素赋值给Result,后续操作再用临时变量引用Result:

function CreateElementFixed(xmlDocument: Variant; name : String) : Variant;
var
    xmlNewElement : Variant;
begin
    Result := xmlDocument.createElement(name);
    xmlNewElement := Result;
    
    AddAttributes(xmlDocument, xmlNewElement);
end;

关键注意事项

  • 对于返回Variant类型的函数,必须确保每次调用时Result的旧引用被正确释放,避免COM对象引用计数混乱。
  • 不要依赖脚本引擎自动初始化Result,显式初始化或提前赋值是更安全的做法。

内容的提问来源于stack exchange,提问作者TomB

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 17:03:12