Wednesday, February 17, 2021

String-style operations by wrapping a Byte array


People seem to get tangled up in their underwear a lot trying to fiddle with binary data in String variables. Often they run into nightmares where they convert Unicode "to Unicode" and then back later, in the vain hope of avoiding data corruption. And then some locale boundary gets crossed and it all falls down. Hard.

From what I've seen the bulk of this comes from the desire to use String operations on binary data. But most of these are fairly trivial to synthesize, especially with the help of CopyMemory.

The Bytarr Class wraps a dynamic Byte array along with several properties and methods to make this easier.

You can use the Class for lots of applications, or when you only need one or two operations it can server as a template for inline code when you don't want the Class.

Bytarr (biter?) is bundled with a test program in the attachment. This also includes my Dump Class, which I find handy for debugging and testing.



Source:

Sunday, February 14, 2021

Working with pointers - VB6 (by Krivous Anatoly Anatolevich)

Often there are situations when you need to get data having only the address (for example, in WndProc, HookProc). Usually, simply copy the data via CopyMemory the structure after changing data and copy it back. If the structure is large, it will be a waste of resources to copy into structure and back. In languages such as C ++ is all done easily with the help of pointers, written something like newpos = (WINDOWPOS *) lparam. Nominally VB6 does not work with pointers, but there are a few workarounds.

Public Declare Function GetMem4 Lib "msvbvm60" (src As Any, Dst As Any) As Long
Public Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (src() As Any) As Long
For a start I will give the necessary declarations:
Code:

Private Type Vector
    X As Single
    Y As Single
End Type
Private Type TestRec
    Name As String
    Value As Long
    Position As Vector
    Money As Double
End Type
 
Private Sub Form_Load()
    Dim tr As TestRec
    Test tr
End Sub
 
Private Function Test(Pointer As TestRec, Optional ByVal nu As Long)
    Dim q As TestRec, z As TestRec
 
    q.Name = "The trick"
    q.Position.X = 5: q.Position.Y = 15
    q.Value = 12345: q.Money = 3.14
 
    z.Name = "Visual Basic 6.0"
    z.Position.X = 99: z.Position.Y = 105
    z.Value = 7643: z.Money = 36.6
 
    GetMem4 VarPtr(q), ByVal VarPtr(nu) - 4    ' Set pointer to q (Pointer = &q)
 
    PrintRec Pointer
 
    GetMem4 VarPtr(z), ByVal VarPtr(nu) - 4    ' Set pointer to z (Pointer = &z)
 
    PrintRec Pointer
 
End Function
 
Private Sub PrintRec(Pt As TestRec)
    Debug.Print "----------------"
    Debug.Print "Name = " & Pt.Name
    Debug.Print "Value = " & Pt.Value
    Debug.Print "Money = " & Pt.Money
    Debug.Print "Position.X = " & Pt.Position.X
    Debug.Print "Position.Y = " & Pt.Position.Y
End Sub
You can also create a pointer by using arrays. The idea is to create 2 arrays one element each, which will store the address of a variable, and the other will refer to the data. The first will always be Long, a second type of data desired. This is useful for example if you want to pass on lists, etc. It's no secret that the array in VB is simply an SafeArray. In the data structure of this array contains a lot of useful information, and a pointer to the data. What we do, we create two arrays:


  • 1st (with address) refers to a pointer to the second data array. As a result, changing the values in the first array, 2nd automatically refer to the desired data.*
  • 2nd is directly the data pointed to by the first.*


Also, after all the manipulations necessary to return all the pointers back to VB properly clear the memory.* For all manipulations I created auxiliary functions and structure for data recovery.* Address SafeArray is available through Not Not Arr, but IDE after such manipulations are glitches with floating point:
Code:

Public Type PtDat
    Prv1 As Long
    Prv2 As Long
End Type
 
' Create the pointer. 1st param is pointer, 2nd address.
Public Function PtGet(Pointer() As Long, ByVal VarAddr As Long) As PtDat
    Dim i As Long
    i = GetSA(ArrPtr(Pointer)) + &HC
    GetMem4 ByVal i, PtGet.Prv1
    GetMem4 VarAddr + &HC, ByVal i
    PtGet.Prv2 = Pointer(0)
End Function
' Release pointer
Public Sub PtRelease(Pointer() As Long, prev As PtDat)
    Pointer(0) = prev.Prv2
    GetMem4 prev.Prv1, ByVal GetSA(ArrPtr(Pointer)) + &HC
End Sub
' Obtaint address of SafeArray (same Not Not)
Public Function GetSA(ByVal addr As Long) As Long
    GetMem4 ByVal addr, GetSA
End Function
Example of use:
Code:

Private Sub Form_Load()
    Dim pt() As Long, var() As TestRec, prev As PtDat      ' Pointer, references data, release data.
    Dim q As TestRec, z As TestRec                          ' The structures, which we refer
 
    ReDim pt(0): ReDim var(0)
 
    q.Name = "The trick"
    q.Position.X = 5: q.Position.Y = 15
    q.Value = 12345: q.Money = 3.14
 
    z.Name = "Visual Basic 6.0"
    z.Position.X = 99: z.Position.Y = 105
    z.Value = 7643: z.Money = 36.6
 
    prev = PtGet(pt, GetSA(ArrPtr(var)))                    ' Create "pointer"
 
    pt(0) = VarPtr(q)                                      ' Refer to q (pt = &q)
    PrintRec var(0)
    pt(0) = VarPtr(z)                                      ' Refer to z (pt = &z)
    PrintRec var(0)
 
    PtRelease pt, prev                                      ' Release
 
End Sub

Source:
http://earlier189.rssing.com/browser.php?indx=6373759&item=376