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
相关产品推荐
相关产品推荐

