How to pass an array of ByRef arguments using CallByName?

I am currently using CallByName to dynamically call methods. There are several methods that I collect daily from a table on the server along with the arguments. For this reason, I am sending an array of arguments to CallByName, and not to the param array, since I do not know the number of arguments to execute. Given that CallByName expects a paramarray, I use the private declare function to bypass the VBA type definition.

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 

This works when I pass an object where the method changes its basic properties. However, there are some methods in which I pass an object and a method that changes the values ​​of the passed arguments. For example, I pass an array with the following arguments

  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 

When I pass this array, the method returns the array back with the same values, but I expect double type values ​​for Array (1), Array (2), Array (3). Any ideas?

+5
source share
1 answer

The first key to the answer is to declare a function for rtcCallByName (pulled vbe7.dll from the export table):

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

Note that Args declared as a pointer to SafeArray , but not declared as an out parameter. This means that the COM contract for the function basically means that if you pass ParamArray , the only guarantee that it does is that it will not change the pointer to ParamArray . ByRef in Declare Function only indicates that you are passing a pointer.

Regarding the values ​​inside ParamArray , there are actually not many Microsoft documentation that I can compute for VBA, but the version of the VB.NET documentation gives a second key:

The ParamArray parameter is always declared using ByVal (Visual Basic).

In the context of CallByName this makes sense. The rtcCallByName function rtcCallByName does not know (and cannot) which of the parameters for the called method is declared ByRef or ByVal , so it must assume that it cannot change them.

As for implementations, to get around this limitation, I would suggest either refactoring to exclude return values ​​that are passed by ByRef to the code called by CallByName , or wrapping the necessary functionality in the class.

+1
source

All Articles