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

时间:2016-05-25 08:59:33

标签: 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

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

 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

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

1 个答案:

答案 0 :(得分:1)

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

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

请注意,Args被声明为指向SafeArray的指针,但被声明为out参数。这意味着该函数的COM契约基本上是说如果你传递ParamArray它唯一的保证是它不会改变指向ParamArray本身的指针。 ByRef中的Declare Function仅表示您正在传递指针。

对于里面的值 ParamArray,我真的没有太多的微软文档我可以专门针对VBA,但VB.NET version of the documentation给出了第二个线索:

  

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

CallByName的背景下,这是完全合理的。 rtcCallByName函数本身不会(也不能)知道被调用方法的哪些参数是自己声明为ByRefByVal,所以它必须假设它无法改变它们。

就解决此限制的实现而言,我建议重构以消除在ByRef调用的代码中传递CallByName的返回值,或者在类中包含所需的功能。