如何使用CallByName传递参数ByRef数组?

mar*_*uja 6 vba dynamic marshalling pass-by-reference callbyname

我目前正在使用CallByName来动态调用方法.我每天从服务器中的表中提取几种方法以及参数.出于这个原因,我将一个参数数组发送到CallByName而不是一个param数组,因为我不知道运行时的参数数量.鉴于CallByName需要一个paramarray,我使用私有声明函数来绕过VBA类型定义.

Private Declare PtrSafe Function rtcCallByName Lib "VBE7.DLL" ( _
  ByVal Object As Object, _
  ByVal ProcName As LongPtr, _
  ByVal CallType As VbCallType, _
  ByRef Args() As Any, _
  Optional ByVal lcid As Long) As Variant

Public Function CallByNameMethod(Object As Object, ProcName As String, ByRef Args () As Variant)
  AssignResult CallByNameMethod, rtcCallByName(Object, StrPtr(ProcName), VbMethod, Args)
End Function 

 Private Sub AssignResult(target, Result)
  If VBA.IsObject(Result) Then Set target = Result Else target = Result
End Sub
Run Code Online (Sandbox Code Playgroud)

当我传递方法更改其基础属性的对象时,这种方法有效.但是,有一些方法可以传递一个对象和一个更改传递参数值的方法.例如,我传递一个带有以下参数的数组

 Dim Name as String, Value1 as double, Value2 as double, Value3 as double
 Dim Array(3) as Variant

  String = "Name"
  Value1 = 0
  Value2 = 0
  Value3 = 0

  Array(0) = Name
  Array(1) = Value1
  Array(2) = Value2
  Array(3) = Value3
Run Code Online (Sandbox Code Playgroud)

当我传递该数组时,该方法只返回具有相同值的数组,但我期望Array(1),Array(2),Array(3)的双重类型值.有任何想法吗?

Com*_*ern 2

答案的第一个线索在于 rtcCallByName 的函数声明(从 vbe7.dll 的导出表中提取):

function CallByName(Object: IDispatch; ProcName: BSTR; CallType: VbCallType; Args: ^SafeArray; out lcid: I4): Variant; stdcall;
Run Code Online (Sandbox Code Playgroud)

请注意,Args被声明为指向 a 的指针SafeArray,但未声明out参数。这意味着该函数的 COM 契约基本上是说,如果您传递 a ,ParamArray它所做的唯一保证是它不会更改指向其ParamArray自身的指针。only中ByRefDeclare Function表示您正在传递一个指针。

至于中的ParamArray,我确实没有太多专门针对 VBA 的 Microsoft 文档,但VB.NET 版本的文档给出了第二条线索:

ParamArray 参数始终使用 ByVal (Visual Basic) 进行声明。

在 的背景下CallByName,这是完全有道理的。函数rtcCallByName本身不(也不能)知道被调用方法的哪些参数本身是声明的ByRefByVal,因此它必须假设它无法更改它们。

至于解决此限制的实现,我建议要么进行重构以消除ByRef在调用的代码中传递的返回值CallByName,要么将所需的功能包装在类中。