Delphi调用IActiveScript IDispatch.Invoke传递多参数异常问题
出错原因
问题根源是悬空指针和不规范的COM变体操作,具体两点:
- 临时对象生命周期问题:
VarToWideStr(v[i])返回的是临时WideString值,将其内部字符指针赋值给Arg[n].bstrVal后,临时对象会在当前语句执行结束、或进入下一次循环迭代时被Delphi自动释放,bstrVal随即成为指向已回收内存的野指针。这就是参数值重复、运行结果随机、32/64位表现不一致的直接原因——野指针指向的内存内容不受控,会随着内存复用随时变化。单参数场景下临时对象释放后内存暂时未被覆写,所以看起来运行正常,参数数量增加后内存被反复复用,问题就会稳定复现。 - 不符合COM VARIANT操作规范:
TVariantArg是COM标准VARIANTARG结构的Delphi映射,直接手动修改vt和bstrVal字段,既没有正确初始化结构的保留字段,也没有按照COM要求为BSTR分配独立内存、维护引用计数。你注释里手动赋值常量字符串能运行只是巧合:常量字符串存储在程序静态只读段,生命周期和程序一致,不会被释放,本质依然是不规范写法,传入动态字符串必然出错。
另外你代码里@Arg报错是正常的:Delphi动态数组本身是指向数组首元素的指针,@Arg取到的是动态数组变量自身的地址,不是数组内容的首地址,@Arg[0]的写法才是正确的。
修复方法
不要手动操作TVariantArg的底层字段,使用Delphi内置的变体操作函数处理参数,自动完成类型转换、内存分配和释放,修复后的核心代码如下:
procedure TForm1.Button14Click(Sender: TObject); var v: OleVariant; Disp: IDispatch; Arg: TArray<TVariantArg>; Res: OleVariant; DispParams: TDispParams; i, n: Integer; s: WideString; dispid: TDispID; begin v := VarArrayOf(['Wrong...', 'huh', 'OK!']); s := 'TestParams'; OleCheck(MyScriptingHost1.FScript.GetScriptDispatch(nil, Disp)); OleCheck(Disp.GetIDsOfNames(GUID_NULL, @s, 1, 1033, @dispid)); // 按实际参数个数初始化参数数组 SetLength(Arg, VarArrayHighBound(v, 1) - VarArrayLowBound(v, 1) + 1); // COM规范要求Invoke的参数按逆序存入rgvarg数组 for i := VarArrayLowBound(v, 1) to VarArrayHighBound(v, 1) do begin n := High(Arg) - i; // 初始化变体结构,直接赋值OleVariant自动处理内存和类型 VariantInit(TVarData(Arg[n])); OleVariant(Arg[n]) := v[i]; end; DispParams.rgvarg := @Arg[0]; DispParams.rgdispidNamedArgs := nil; DispParams.cArgs := Length(Arg); DispParams.cNamedArgs := 0; OleCheck(Disp.Invoke(dispid, GUID_NULL, 1033, DISPATCH_METHOD, DispParams, @Res, nil, nil)); // 调用完成后释放参数中的变体资源,避免内存泄漏 for i := 0 to High(Arg) do VariantClear(TVarData(Arg[i])); Memo2.Lines.Text := VarToStr(Res); end;
修复后传参逻辑在32位、64位环境下表现一致,支持任意数量、任意自动化兼容类型的参数传递,不会出现随机值问题。
内容的提问来源于stack exchange,提问作者Ronald Ruijs
相关产品推荐
相关产品推荐

