Results 1 to 13 of 13

Thread: Passing array in Variant by reference

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    May 2014
    Location
    Kallithea Attikis, Greece
    Posts
    1,289

    Passing array in Variant by reference

    This is the Class1, which have sub alfa and a parameter an array of type long.
    Code:
    Sub alfa(b() As Long)
    Debug.Print "ok"
    b(0) = 100
    End Sub
    It is impossible to have an array of type long in variant and pass it by reference. Except we can use invoke from iDispatch interface.

    just add a reference to OLEEXP

    Code:
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
        lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)
    Sub main()
    Dim k(20) As Long
    Dim z, c As New Class1, arg() As Variant, Clid As Long, dispid As Long
    Dim Disp As oleexp.IDispatch, ret As Long, riid As oleexp.UUID, lngArgErr As Long
    Dim varDISPID() As Long, VarRet As Long, params As oleexp.DISPPARAMS, Excep As oleexp.EXCEPINFO
    ReDim varDISPID(0 To 0)
    Set Disp = c
    ret = Disp.GetIDsOfNames(riid, "alfa", 1&, Clid, varDISPID(0))
    If ret <> 0 Then Exit Sub
    dispid = varDISPID(0)
    z = k()
    ' we can't do this: c.alfa z
    ReDim arg(0)
    VarByRef VarPtr(arg(0)), z
    With params
    .cArgs = 1
    .rgPointerToVariantArray = VarPtr(arg(0))
    End With
    ret = Disp.Invoke(dispid, riid, 0, 1, params, VarRet, Excep, lngArgErr)
    VarByRefCleanRef VarPtr(arg(0))
    Debug.Print z(0) = 100
    End Sub
    
    
    
    
    Sub VarByRef(ByVal a As Long, ByRef b As Variant)
    Dim t(0 To 3) As Long
       CopyMemory t(0), ByVal VarPtr(b), 16
       t(0) = t(0) Or &H4000
       t(2) = VarPtr(b) + 8
       CopyMemory ByVal a, t(0), 16
    End Sub
    Sub VarByRefCleanRef(ByRef a As Long)
    Dim t(0 To 3) As Long
       CopyMemory t(0), ByVal VarPtr(a), 2
       t(0) = t(0) And &HFFFFBFFF
       CopyMemory ByVal a, t(0), 2
    End Sub


    I have to say that OLEEXP has two faults on IDISPATCH:
    One is from GetIDsOfNames, the rgsznames have to be Long. Using it as String is easy for one String, but we can't pass more strings and take with one call all the DISPID (and that happen if we use named parameters, so the first string is the name of function and the following strings are the name of parameters).

    The second one is the return value from invoke, the VarRet. The right one has VARIANT type. The OLEEXP has this value as long type. I have check it and using as variant we can get UDT (from ActiveX dll).

  2. #2
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: Passing array in Variant by reference

    Hmm... the following seems a lot easier to me ...(and it does not require any typelibs)

    Class1:
    Code:
    Option Explicit
    
    Public Sub Alfa(LArr() As Long)
      Debug.Print "inside Alfa"
      LArr(0) = 100
    End Sub
    
    Public Sub Beta(L As Long)
      Debug.Print "inside Beta"
      L = L + 100
    End Sub
    Form1:
    Code:
    Option Explicit
    
    Private Declare Sub RtlMoveMemory Lib "kernel32" (Dst As Any, Src As Any, ByVal BLen&)
     
    Private Sub Form_Load()
      Dim V1, V2, C1 As New Class1, LArr(20) As Long
        
      V1 = LArr
      CallByName C1, "Alfa", VbMethod, PassByRef(V1)
      
      V2 = -58&
      CallByName C1, "Beta", VbMethod, PassByRef(V2)
     
      Debug.Print V1(0), V2 '<<- should print 100, 42
    End Sub
     
    Function PassByRef(V)
      PassByRef = VarPtr(V) + 8
      RtlMoveMemory ByVal VarPtr(PassByRef), VarType(V) Or &H4000, 2
    End Function
    Olaf

  3. #3

    Thread Starter
    Frenzied Member
    Join Date
    May 2014
    Location
    Kallithea Attikis, Greece
    Posts
    1,289

    Re: Passing array in Variant by reference

    Good thinking (you do the same with VarByRef() sub above but with one copymemory)!


    The problem with CallByName is that we can't place named arguments, which we can with invoke (if we didn't use OLEEXP, because with OLEEXP simply we can't get dispid from the arguments, because we can't pass an array of long (where each long is poniter to string) to GetIDsOfNames, you see it set to get a string for name - which in array of names this is the first value and is the name of the method/function).

    These are from a tweak typelib from Eduardo Moricillo for iDispacth interface:
    In OLEEXP cNames is String type

    Function GetIDsOfNames(riid As IID, rgszNames As Long, cNames As Long, lcid As Long, rgDispId As Long) As Long
    Member of IDispatch.IDispatchM2000

    Here pVarResultis variant type, so can return a variant with UDT (it is Long type in OLEEXP).


    Function Invoke(dispIdMember As Long, riid As IID, lcid As Long, wFlags As Integer, pDispParams As DISPPARAMS, pVarResult, pExcepInfo As EXCEPINFO, puArgErr As Long) As Long
    Member of IDispatch.IDispatchM2000

  4. #4
    PowerPoster
    Join Date
    Jan 2020
    Posts
    3,746

    Re: Passing array in Variant by reference

    Property AsLong(Ptr As Any) As Long
    VBVM6Lib.VBVM6 的成员
    Returns or sets the value referenced by the source parameter cast to a DWord Long.
    Code:
    Dim a As Long, b As Long
    Dim c As Long
    a = 11
    b = 22
    c = o.Sum(AsLong(a), AsLong(b))

  5. #5
    PowerPoster
    Join Date
    Jan 2020
    Posts
    3,746

    Re: Passing array in Variant by reference

    Code:
    Private Declare Function VariantChangeTypeEx Lib "oleaut32" (ByRef pvargDest As Variant, ByRef pvarSrc As Variant, ByVal lcid As Long, ByVal wFlags As Integer, ByVal VT As Integer) As Long
    Private Declare Function GetMem4 Lib "msvbvm60" (Src As Any, Dst As Any) As Long
    Private Declare Function GetMem2 Lib "msvbvm60" (Src As Any, Dst As Any) As Long
    
     Const vbLongLong As Integer = &H14
    Private Const vbTypeMask As Integer = &HFFF
    Private Const vbByRef As Integer = &H4000
    
    Public Property Get VarType(VarName) As VbVarType
        GetMem2 VarName, VarType
        VarType = VarType And vbTypeMask
    End Property
    
    Public Property Let VarType(VarName, ByVal Value As VbVarType)
        Dim VT As Integer
        GetMem2 VarName, VT
        VT = (VT And (Not vbTypeMask)) Or (Value And vbTypeMask)
        GetMem2 VT, VarName
    End Property
    Code:
    Dim v1
    v1 = 3344
     
    
    VarType(v1) = vbLong
    does any way to change Variant for byref or byval?

  6. #6

    Thread Starter
    Frenzied Member
    Join Date
    May 2014
    Location
    Kallithea Attikis, Greece
    Posts
    1,289

    Re: Passing array in Variant by reference

    There is a good library here: https://github.com/cristianbuse/VBA-MemoryTools
    which have CloneParamArray - copies a param array to another array of Variants while preserving ByRef elements.

    A byref variant is a variant type variable vbVariant Or &H4000, which at offset +8 has a pointer to an variant of any type. So when you pass a variant say vblong, as variant by reference you have to make a new variant, as type vbVariant Or &H4000, and pass the pointer to original varianat After the call the reference may change to another variant, say type vbString, so the pointer point to the new variant of type vbString. This is different for a pass by reference a long value, which converted to a variant vbLong Or &H4000 and the pointer (at offset +8) point to the four byte long. At the return of this call you have either the change of value of that long (at the placed address) or a new address to another long.

    So if you wish to have a variant which can be anything after a call, you have to pass it by reference at the call, you don't have to hold as a pointer to variant. Although if you wish to do that, you may use long type variables as pointers and synthesize the type as vbvariant Or &H4000 type variant and at +8 place that pointer, but you have to use invoke. You have to clear the variant before exit a sub if you handle the variant by copymemory actions, to nor allow automatic control at exit.

  7. #7
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,652

    Re: Passing array in Variant by reference

    I added the version of IDispatch you requested; IDispatchM2000 in oleexpimp.tlb, since they conflict if in the same.

  8. #8
    PowerPoster
    Join Date
    Jan 2020
    Posts
    3,746

    Re: Passing array in Variant by reference

    call to variant dll like
    dim a as string,b as string
    a="abc"
    b="xyz"
    call stradd(a,varptr(b))
    the second arg will be change


    in vc++ dll,freebasic
    function stradd(v as variant ptr) as variant
    dim vv as variant
    vv=v[1] + v[0]

    v[1] ="new str" 'how to write this code?
    return vv

    Code:
    Function StrAdd(ByVal v As VARIANT Ptr) As VARIANT 
      
       Dim CB As CObject 
        'MsgBox CObject(*v[0].pbstrVal ).ToString
        CB = CObject(v[1]) & CObject( *v[0].pbstrVal)
        '*v[0].pbstrVal=????
        Return CB
    End Function

  9. #9
    Addicted Member
    Join Date
    Jun 2017
    Posts
    236

    Re: Passing array in Variant by reference

    Hi Schmidt

    How to success when use "Dim V1 As Long"

    Private Sub Form_Load()
    Dim out
    Dim V1 As Long
    Dim V2 As Variant

    V1 = 12345
    V2 = 6677

    out = PassByRef(V1): Debug.Print out, "__A" ' not success

    out = PassByRef(V2): Debug.Print out, "__B" ' success

    End Sub

  10. #10
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: Passing array in Variant by reference

    Quote Originally Posted by quickbbbb View Post
    Hi Schmidt

    How to success when use "Dim V1 As Long"

    Private Sub Form_Load()
    Dim out
    Dim V1 As Long
    Dim V2 As Variant

    V1 = 12345
    V2 = 6677

    out = PassByRef(V1): Debug.Print out, "__A" ' not success

    out = PassByRef(V2): Debug.Print out, "__B" ' success

    End Sub
    The mechanism I've posted in #2, requires "a prior copy into a Variant"
    (before the ByRef, LateBound-Class-Method-Call, where this Variant-Variable gets indirectly passed).

    It's useful for Script-Interpreters where you don't have specific Typing...

    Not sure what you want to achieve... what's your goal in the end?

    Olaf

  11. #11
    Addicted Member
    Join Date
    Jun 2017
    Posts
    236

    Re: Passing array in Variant by reference

    Ok, I see
    As for you ask me "what's your goal in the end"
    I want to reduce type conversion
    But I found that the "Timer - tt" was very small.
    So don't trouble you to help me the answer

    thanks

    Private Sub Form_Load()
    Dim w As Long, tt


    dim var_w : var_w = PassByRef(w)
    tt = Timer
    For w = 1 To 100000000 ' 0.28 second
    Call T_var(w)
    ''''' Call T_var(var_w)
    Next
    MsgBox Timer - tt


    tt = Timer
    For w = 1 To 100000000 ' 0.25 second
    Call T_long(w)
    Next
    MsgBox Timer - tt

    End
    End Sub

    Private Sub T_var(v As Variant)
    End Sub

    Private Sub T_long(v As Long)
    End Sub
    Last edited by quickbbbb; Nov 23rd, 2023 at 03:46 AM.

  12. #12
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,121

    Re: Passing array in Variant by reference

    For anyone having trouble understanding what the problem in OP actually is, here is a *failing* snippet (CallByName fails with Type missmatch) and the workaround by Olaf

    Code:
    Option Explicit
    
    Private Declare Sub RtlMoveMemory Lib "kernel32" (Dst As Any, Src As Any, ByVal BLen&)
    
    Private Sub Form_Load()
        Dim C1          As New Class1
        Dim LArr(20)    As Long
        Dim VArr        As Variant
        
        CallByName C1, "alfa", VbMethod, LArr
        Debug.Print LArr(0)
        '-> ok
        '->  100
        
        VArr = LArr
    '    CallByName C1, "alfa", VbMethod, VArr '<--- fails with Type missmatch
        CallByName C1, "alfa", VbMethod, PassByRef(VArr)
        Debug.Print VArr(0)
        '-> ok
        '->  100
    End Sub
    
    Function PassByRef(V)
      PassByRef = VarPtr(V) + 8
      RtlMoveMemory ByVal VarPtr(PassByRef), VarType(V) Or &H4000, 2
    End Function
    
    CallByName works just fine with strongly typed LArr variable, (always) passing it ByRef as it's a SAFEARRAY, not like a SAFEARRAY wrapped in a Variant as in the second case with VArr failing call.

    cheers,
    </wqw>

  13. #13
    Fanatic Member
    Join Date
    Mar 2023
    Posts
    787

    Re: Passing array in Variant by reference

    Why would you pass a Array as Variant? As I have learned that Arrays only can be all the same.
    Better to declare a custom Type and pass that as an array if you need it.
    In that custom type you can have different sorts of declares....Long, String, Object, Collection, Integers, Bytes etc etc.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width