[RESOLVED] How to create (0 to -1) bounds byte array?-VBForums
Results 1 to 17 of 17

Thread: [RESOLVED] How to create (0 to -1) bounds byte array?

  1. #1

    Thread Starter
    Hyperactive Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    350

    Resolved [RESOLVED] How to create (0 to -1) bounds byte array?

    Hi, @all!
    Code:
    'some examples of (0 to -1)
    
    Private Sub Form_Load()
        
        Dim arr$(), v
        
        arr = Split("") 'String(0 to -1)
        
        v = Array() 'Variant(0 to -1)
        
        '??? 'Byte(0 to -1)
        
    End Sub
    In some cases I would like my function will return Byte(0 to -1), like 'vb Split' do, so array will be always initialized and further I can safely use Ubound() for checking or directly in 'for each' e.t.c., no matter, but I don't know how correctly create such "empty" byte array in a way to be sure not to get crash surprises when runtime decides to free memory of such "empty" array.

    Thanks for help.
    Alex.

  2. #2
    PowerPoster
    Join Date
    Jun 2013
    Posts
    3,107

    Re: How to create (0 to -1) bounds byte array?

    For a ByteArray it would simply be:
    Code:
    Dim B() As Byte
        B = "" 'or B = vbNullString
    Olaf

  3. #3

    Thread Starter
    Hyperactive Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    350

    Re: How to create (0 to -1) bounds byte array?



    Thank you.

  4. #4
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    3,043

    Re: How to create (0 to -1) bounds byte array?

    Hmmm, I never use these things, but this thread got me to thinking. It's interesting that you could do something like ...

    Code:
    
    Dim bb() As Byte
    bb = vbNullString
    
    Dim i As Long
    For i = LBound(bb) To UBound(bb)
        ' do something
    Next i
    
    ... and you wouldn't get into trouble.

    That got me wondering about exactly what's going on with the SafeArray, so I wrote this code:

    Code:
    
    Option Explicit
    '
    Private Type SAFEARRAY
        cDims As Integer
        fFeatures As Integer
        cbElements As Long
        cLocks As Long
        pvData As Long
    End Type
    '
    Private Type SAFEARRAYBOUND
        cElements As Long
        lLbound As Long
    End Type
    '
    
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
    
    Private Sub Form_Load()
        Dim bb() As Byte
        '
        Dim sa As SAFEARRAY
        Dim sab() As SAFEARRAYBOUND
        '
    
        bb = ""
        GetSafeArrayInfo bb, sa, sab()
    
    
        Debug.Print sab(0).cElements
        Debug.Print sab(0).lLbound
    
    
        Debug.Print "done"
        Unload Me
    End Sub
    
    Private Sub GetSafeArrayInfo(v As Variant, sa As SAFEARRAY, sab() As SAFEARRAYBOUND)
        Dim ppSA As Long
        Dim pSA As Long
        '
        CopyMemory ppSA, ByVal VarPtr(v) + 8, 4
        CopyMemory pSA, ByVal ppSA, 4 ' Dereference.
        '
        If pSA = 0 Then Exit Sub ' No array.
        '
        CopyMemory sa, ByVal pSA, LenB(sa)
        If sa.cDims = 0 Then Exit Sub ' No dimensions, not dimensioned.
        '
        ReDim sab(0 To sa.cDims - 1)
        '
        ' Now we can get the remainder of the SAFEARRAY structure.
        CopyMemory sab(0), ByVal (pSA + LenB(sa)), (sa.cDims * 8)
    End Sub
    
    It turns out that it is "sort of" dimensioned. In other words, SAFEARRAY.cDims has a non-zero value. However, when we look at the SAFEARRAYBOUND values, we see that the lLBound = 0 but the cElements also = 0. Obviously, the VB6 UBound function just does something like: UBound = sab.lLBound + (sab.cElements - 1)

    And that all makes perfect sense to me.

    And then, that got me thinking some more. We can certainly place any type of an array in a Variant. So why can't we place one of these funny "sort of" dimensioned array into a Variant and then typecast it? Along these lines, I wrote this code:

    Code:
    
    Option Explicit
    
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
    
    Private Sub Form_Load()
    
    
        Dim c() As Currency
        c = MakeZeroToNegOneArray(vbCurrency)
        MsgBox TypeName(c)
        MsgBox LBound(c) & " " & UBound(c)
    
    
        Debug.Print "done"
        Unload Me
    End Sub
    
    Private Function MakeZeroToNegOneArray(iType As VBA.VbVarType) As Variant
        Dim bb() As Byte
        Const VT_ARRAY = &H2000
        '
        bb = vbNullString                                       ' We'll make a Byte type and then force-typecast it.
        MakeZeroToNegOneArray = bb                              ' Put empty array into variant.
        CopyMemory MakeZeroToNegOneArray, iType Or VT_ARRAY, 2  ' Typecast, making sure it's still an array.
    End Function
    
    
    Given that no actual memory is allocated for the array data, I see no foul in doing this typecasting.

    It would appear that this is a workable method for creating one of these "funny" arrays for any type. It could probably be improved a bit with GetMem2, but the above certainly seems to work.

    Regards,
    Elroy

  5. #5
    PowerPoster
    Join Date
    Jun 2013
    Posts
    3,107

    Re: How to create (0 to -1) bounds byte array?

    Quote Originally Posted by Elroy View Post
    Code:
    
    Private Function MakeZeroToNegOneArray(iType As VBA.VbVarType) As Variant
        Dim bb() As Byte
        Const VT_ARRAY = &H2000
        '
        bb = vbNullString                                       ' We'll make a Byte type and then force-typecast it.
        MakeZeroToNegOneArray = bb                              ' Put empty array into variant.
        CopyMemory MakeZeroToNegOneArray, iType Or VT_ARRAY, 2  ' Typecast, making sure it's still an array.
    End Function
    
    Given that no actual memory is allocated for the array data, I see no foul in doing this typecasting.
    It would appear that this is a workable method for creating one of these "funny" arrays for any type. It could probably be improved a bit with GetMem2, but the above certainly seems to work.
    Nope, that's not going to fly, since you neglected to change the .cbElements Member
    of the (underlying) ByteArray you've choosen as a "template".

    One can remedy (and add/overwrite) that too, of course - but then the code would become longer -
    so why not use the OleAut-function which was desigend for that purpose instead (to save some lines of code)...

    Code:
    Option Explicit
    
    Private Declare Sub MemCopy Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal CB&)
    Private Declare Function SafeArrayCreateEx& Lib "oleaut32" (ByVal VarType&, ByVal Dims&, SAUB As Any, ByVal pvExtra&)
     
    Private Sub Form_Load()
    Dim C() As Currency
        C = CreateEmptyArray(vbCurrency)
        Debug.Print LBound(C), UBound(C), TypeName(C)
    End Sub
    
    Private Function CreateEmptyArray(ByVal ArrType As VbVarType, Optional ByVal Dims As Long = 1)
    Static SAUB(0 To 29) As Long 'enough statically allocated "zero-space" for max. 15 Dimensions
      MemCopy ByVal VarPtr(CreateEmptyArray) + 8, SafeArrayCreateEx(ArrType And Not vbArray, Dims, SAUB(0), 0), 4
      MemCopy ByVal VarPtr(CreateEmptyArray), ArrType Or vbArray, 2
    End Function
    Olaf

  6. #6
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    3,043

    Re: How to create (0 to -1) bounds byte array?

    Quote Originally Posted by Schmidt View Post
    Nope, that's not going to fly, since you neglected to change the .cbElements Member
    of the (underlying) ByteArray you've choosen as a "template".
    You're right, but I'm not sure it matters. In fact, using the above code, I executed the following ...

    Code:
    
    
    Private Sub Form_Load()
        Dim c() As Currency
    
        c = MakeZeroToNegOneArray(vbCurrency)
    
        ReDim c(1 To 1000)
    
        Dim sa As SAFEARRAY
        Dim sab() As SAFEARRAYBOUND
    
        GetSafeArrayInfo c(), sa, sab()
    
        MsgBox sa.cbElements
    
    End Sub
    
    .. and cbElements reports 8. It's obviously fixed on the Redim.

    Regards,
    Elroy

  7. #7
    PowerPoster
    Join Date
    Jun 2013
    Posts
    3,107

    Re: How to create (0 to -1) bounds byte array?

    Quote Originally Posted by Elroy View Post
    You're right, but I'm not sure it matters.
    Well, I'm sure it matters (otherwise wouldn't have bothered with posting a warning).

    Quote Originally Posted by Elroy View Post
    In fact, using the above code, I executed the following ...
    ...
    .. and cbElements reports 8. It's obviously fixed on the Redim.
    A Redim (without Preserve) is a completely new allocation - that's why it worked for you.

    But check out the following:
    Code:
    Option Explicit
     
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
    
    Private Sub Form_Load()
        Dim c() As Currency
        c = MakeZeroToNegOneArray(vbCurrency)
        ReDim Preserve c(1)
        Debug.Print c(1) 'should print 0, but on my machine gives: -807044173589455.4976
    End Sub
    
    Private Function MakeZeroToNegOneArray(iType As VBA.VbVarType) As Variant
        Dim bb() As Byte
        Const VT_ARRAY = &H2000
        '
        bb = vbNullString                                       ' We'll make a Byte type and then force-typecast it.
        MakeZeroToNegOneArray = bb                              ' Put empty array into variant.
        CopyMemory MakeZeroToNegOneArray, iType Or VT_ARRAY, 2  ' Typecast, making sure it's still an array.
    End Function
    BTW, vbArray has the same value as your VT_ARRAY-def.

    Olaf

  8. #8
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    3,043

    Re: How to create (0 to -1) bounds byte array?

    And just for grins, I went ahead and patched up cbElements, although it probably truly can't matter. And yeah, it did take a few more lines of code.

    Code:
    
    Option Explicit
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
    Private Type SAFEARRAY
        cDims As Integer
        fFeatures As Integer
        cbElements As Long
        cLocks As Long
        pvData As Long
    End Type
    '
    Private Type SAFEARRAYBOUND
        cElements As Long
        lLbound As Long
    End Type
    
    Private Function MakeZeroToNegOneArray(iType As VBA.VbVarType) As Variant
        ' Allowed: Boolean, Byte, Currency, Date, Double, Integer, Long, Single, String, Variant, Object
        If Not ((iType >= 2 And iType <= 9) Or iType = 11 Or iType = 12 Or iType = 17) Then Exit Function
        '
        Dim bb() As Byte
        Dim ppSA As Long
        Dim iBytes As Long
        '
        bb = vbNullString                                       ' We'll make a Byte type and then force-typecast it.
        MakeZeroToNegOneArray = bb                              ' Put empty array into variant.
        CopyMemory MakeZeroToNegOneArray, iType Or vbArray, 2&  ' Typecast, making sure it's still an array.
        '
        ' And now fix cbElements.
        CopyMemory ppSA, ByVal VarPtr(MakeZeroToNegOneArray) + 8&, 4&
        ppSA = ppSA + 4&                ' Offset pointer to cbElements.
        Select Case iType
        Case vbByte:                                  iBytes = 1&
        Case vbInteger, vbBoolean:                    iBytes = 2&
        Case vbString, vbSingle, vbLong, vbObject:    iBytes = 4&
        Case vbCurrency, vbDate, vbDouble:            iBytes = 8&
        Case vbVariant:                               iBytes = 16&
        End Select
        CopyMemory ByVal ppSA, iBytes, 4&
    End Function
    
    Private Sub Form_Load() '11 17 12
        Dim l() As Long
    
        l = MakeZeroToNegOneArray(vbLong)
    
    
        Dim sa As SAFEARRAY
        Dim sab() As SAFEARRAYBOUND
    
        GetSafeArrayInfo l(), sa, sab()
    
        MsgBox sa.cbElements
    
    End Sub
    Private Sub GetSafeArrayInfo(v As Variant, sa As SAFEARRAY, sab() As SAFEARRAYBOUND)
        Dim ppSA As Long
        Dim pSA As Long
        '
        CopyMemory ppSA, ByVal VarPtr(v) + 8, 4
        CopyMemory pSA, ByVal ppSA, 4 ' Dereference.
        '
        If pSA = 0 Then Exit Sub ' No array.
        '
        CopyMemory sa, ByVal pSA, LenB(sa)
        If sa.cDims = 0 Then Exit Sub ' No dimensions, not dimensioned.
        '
        ReDim sab(0 To sa.cDims - 1)
        '
        ' Now we can get the remainder of the SAFEARRAY structure.
        CopyMemory sab(0), ByVal (pSA + LenB(sa)), (sa.cDims * 8)
    End Sub
    
    
    Regards,
    Elroy

  9. #9
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    3,043

    Re: How to create (0 to -1) bounds byte array?

    Ahhh, I didn't see your post. Ok, it looks like it does matter. I'll admit that I was a bit nervous about it. But it's all fixed now, either way.

    EDIT1: Truth be told, I'm a bit surprised that a Redim Preserve works on these things, but I suppose that does make sense.

    EDIT2: Yeah, I went ahead and used vbArray.

  10. #10

    Thread Starter
    Hyperactive Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    350

    Re: [RESOLVED] How to create (0 to -1) bounds byte array?

    Are there a minimalistic code for Long() ? (like in 2-nd post)
    I need Long(0 to -1).

  11. #11
    Hyperactive Member
    Join Date
    May 2011
    Posts
    327

    Re: [RESOLVED] How to create (0 to -1) bounds byte array?

    You can use SafeArrayCreateVector like in this thread to create empty arrays of any type, including private UDTs or classes like this
    Code:
    Private Declare Function EmptyBiff12PartArray Lib "oleaut32" Alias "SafeArrayCreateVector" (Optional ByVal VarType As VbVarType = vbObject, Optional ByVal Low As Long = 0, Optional ByVal Count As Long = 0) As cBiff12Part()
    ...
    Dim aParts() As cBiff12Part
    aParts = EmptyBiff12PartArray
    cheers,
    </wqw>

  12. #12

    Thread Starter
    Hyperactive Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    350

    Re: [RESOLVED] How to create (0 to -1) bounds byte array?

    Nice. Thanks, wqweto.

  13. #13

    Thread Starter
    Hyperactive Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    350

    Re: [RESOLVED] How to create (0 to -1) bounds byte array?

    Sorry, wqweto. This is not a solution.

    According to MSDN, SafeArrayCreateVector creates fixed array (FADF_FIXEDSIZE). Such array (0 to -1) is useless in this case.
    After trying to 'redim' and save value, the program will crash.

  14. #14
    PowerPoster
    Join Date
    Jun 2013
    Posts
    3,107

    Re: [RESOLVED] How to create (0 to -1) bounds byte array?

    Why not use the code I've posted at the end of #5?

    Olaf

  15. #15
    Hyperactive Member
    Join Date
    May 2011
    Posts
    327

    Re: [RESOLVED] How to create (0 to -1) bounds byte array?

    Quote Originally Posted by Dragokas View Post
    Sorry, wqweto. This is not a solution.

    According to MSDN, SafeArrayCreateVector creates fixed array (FADF_FIXEDSIZE). Such array (0 to -1) is useless in this case.
    After trying to 'redim' and save value, the program will crash.
    I just test this snippet
    Code:
    Option Explicit
    
    Private Declare Function EmptyLongArray Lib "oleaut32" Alias "SafeArrayCreateVector" (Optional ByVal vt As VbVarType = vbLong, Optional ByVal lLow As Long = 0, Optional ByVal lCount As Long = 0) As Long()
    
    Private Sub Form_Load()
        Dim aTemp() As Long
        
        aTemp = EmptyLongArray
        ReDim Preserve aTemp(0 To 10) As Long
        MsgBox aTemp(0)
    End Sub
    and it's working in the IDE and compiled.

    Is this what you mean by 'redim' (with Preserve, like in 'resize')?

    cheers,
    </wqw>

  16. #16

    Thread Starter
    Hyperactive Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    350

    Re: [RESOLVED] How to create (0 to -1) bounds byte array?

    Your example with 'Long' doesn't cause crash (but I suspect that it can be unstable).
    UDT - crash:
    Code:
    Option Explicit
    
    Private Type cBiff12Part
        l As Long
    End Type
    
    Private Declare Function EmptyBiff12PartArray Lib "oleaut32" Alias "SafeArrayCreateVector" (Optional ByVal VarType As VbVarType = vbObject, Optional ByVal Low As Long = 0, Optional ByVal Count As Long = 0) As cBiff12Part()
    
    Private Sub Form_Load()
        Dim aParts() As cBiff12Part
        aParts = EmptyBiff12PartArray
        
        ReDim Preserve aParts(1) As cBiff12Part    
        aParts(1).l = 1
    End Sub
    Quote Originally Posted by Schmidt
    Why not use the code I've posted at the end of #5?
    Sorry, my inattentive, I forgot about it.

  17. #17
    Hyperactive Member
    Join Date
    May 2011
    Posts
    327

    Re: [RESOLVED] How to create (0 to -1) bounds byte array?

    @Dragokas: My sample `EmptyBiff12PartArray` was intended for `cBiff12Part` being a VB6 class. For an empty UDTs array as in your sample code, you have to tweak the signature to use `Optional ByVal VarType As VbVarType = vbUserDefinedType` to prevent crashes. Yes, mismatch on `VarType` param default value and API declare's retval type can certainly lead to catastrophic failures.

    The funny thing in your mismatched snippet is that `ReDim` does not AV but calling `Erase` on this franken-array actually does.

    cheers,
    </wqw>

Tags for this Thread

Posting Permissions

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



Featured


Click Here to Expand Forum to Full Width

Survey posted by VBForums.