-
Feb 10th, 2023, 09:57 PM
#1
Thread Starter
Frenzied Member
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).
-
Feb 11th, 2023, 08:25 AM
#2
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
-
Feb 11th, 2023, 02:05 PM
#3
Thread Starter
Frenzied Member
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
-
May 27th, 2023, 11:01 AM
#4
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))
-
May 27th, 2023, 11:06 AM
#5
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?
-
May 27th, 2023, 01:18 PM
#6
Thread Starter
Frenzied Member
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.
-
May 27th, 2023, 07:15 PM
#7
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.
-
May 27th, 2023, 10:30 PM
#8
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
-
Nov 23rd, 2023, 12:31 AM
#9
Addicted Member
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
-
Nov 23rd, 2023, 02:52 AM
#10
Re: Passing array in Variant by reference
Originally Posted by quickbbbb
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
-
Nov 23rd, 2023, 03:40 AM
#11
Addicted Member
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.
-
Nov 23rd, 2023, 03:51 AM
#12
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>
Last edited by wqweto; Nov 25th, 2023 at 02:25 AM.
-
Nov 24th, 2023, 09:42 PM
#13
Fanatic Member
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|