Solved

UDT Array to String -- URGENT!!!!

Posted on 2001-06-06
28
407 Views
Last Modified: 2008-02-01
Hi all,

I need to convert a array of UDT's to a string.
I already know how to convert a string to an array, using the CopyMemory API.
But when I want to do it reversed, VB generates a fatal exception :(

Please help with this, I need this *really* fast!!

Thanks in advance,

D. Walsarie

this was my test:

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

Public Type udtITEM
  ItemID As Long
  ItemValue As Byte
End Type

Public myArray() As udtITEM
Public myString As String

Public Sub ConvertArrayToString()

  ReDim myArray(0 To 2)

  myArray(0).ItemID = 100
  myArray(0).ItemValue = 3

  myArray(1).ItemID = 200
  myArray(1).ItemValue = 6

  myArray(2).ItemID = 300
  myArray(2).ItemValue = 9

  CopyMemory ByVal myString, myArray(0), 3

End Sub
0
Comment
Question by:dwalsarie
  • 7
  • 5
  • 5
  • +5
28 Comments
 
LVL 2

Author Comment

by:dwalsarie
Comment Utility
wOops..

CopyMemory ByVal myString, myArray(0), 3

should be

CopyMemory ByVal myString, myArray(0), 3 * 5

the value 5 is the length of the UDT :)
however, it still don't work
0
 
LVL 1

Expert Comment

by:zzconsumer
Comment Utility
Mhh, I'm not sure what is a UDT, but maybe this can help you a bit:

Strig to Array:
dim s as String
dim a() as String

a = split(s,"")

Array to String:
s = join(a,"")

If this is not what you wanted, please exlain UDT to me. I'm always searching for something new. ;-)
0
 
LVL 1

Expert Comment

by:morgan_peat
Comment Utility
   Dim myArray() As udtITEM
    Dim myString As String
   
    ReDim myArray(0 To 2)
       
    myArray(0).ItemID = 100
    myArray(0).ItemValue = 3
       
    myArray(1).ItemID = 200
    myArray(1).ItemValue = 6
   
    myArray(2).ItemID = 300
    myArray(2).ItemValue = 9
   
   
   
    ' Create some room in the string
    ' 3 items to be stored, and UDT is
    ' 8 bytes in length (alligned on DWord boundaries)
    ' Therefore needs to be 24 bytes long -> 12 chars
    myString = Space$(12)
   
    ' Copy the 24 bytes into the string
    CopyMemory ByVal StrPtr(myString), ByVal VarPtr(myArray(0).ItemID), 24
   
   
    ' Wipe the array, and re-instate as proof.
    ReDim myArray(0 To 2)
    CopyMemory ByVal VarPtr(myArray(0).ItemID), ByVal StrPtr(myString), 24


When using CopyMemory with UDT's, you need to be aware of the byte length.  Items are always aligned on DWords (Long) boundaries - ie. 4 bytes each.  Therefore, this UDT is 8 bytes in length.

To copy data into a string, you need to allocate memory first.  You didn't, so you were trying to copy data into a piece of memory you did not own - hence the exception.
0
 
LVL 1

Expert Comment

by:morgan_peat
Comment Utility
I generally find it's better to 'pad' the UDT, so you remember where the DWORD boundaries are.  ie.:

Public Type MyType
    ItemID as Long
0
 
LVL 1

Expert Comment

by:morgan_peat
Comment Utility
Oops - as I was saying...

I generally find it's better to 'pad' the UDT, so you remember where the DWORD boundaries are.  ie.:

Public Type MyType
   ItemID As Long
   ItemValue As Byte
   ' Dummy(2) As Byte
End Type
0
 
LVL 10

Expert Comment

by:caraf_g
Comment Utility
Option Explicit
Private Type udt1B
    ccc As Long
    bbb As Byte
End Type
Private Declare Sub CopyMemoryLong _
                Lib "kernel32" _
              Alias "RtlMoveMemory" (ByVal Destination As Long, _
                                     ByVal Source As Long, _
                                     ByVal Length As Long)



Private Sub Command1_Click()

Dim arrudt1Bs(1 To 3) As udt1B
Dim arrudt2Bs(1 To 3) As udt1B
Dim strX As String
Dim lngVP As Long

arrudt1Bs(1).ccc = 1
arrudt1Bs(1).bbb = 1
arrudt1Bs(2).ccc = 2
arrudt1Bs(2).bbb = 2
arrudt1Bs(3).ccc = 3
arrudt1Bs(3).bbb = 3


strX = Space(LenB(arrudt1Bs(1)) * 3 / 2)

'from array to string
CopyMemoryLong VarPtr(lngVP), VarPtr(strX), 4

CopyMemoryLong lngVP, VarPtr(arrudt1Bs(1)), LenB(arrudt1Bs(1)) * 3


'from string back to array
CopyMemoryLong VarPtr(arrudt2Bs(1)), lngVP, LenB(arrudt1Bs(1)) * 3

MsgBox arrudt2Bs(1).bbb
MsgBox arrudt2Bs(2).bbb
MsgBox arrudt2Bs(3).bbb


End Sub
0
 
LVL 10

Expert Comment

by:caraf_g
Comment Utility
PS - like morgan sez..... :o)
0
 
LVL 6

Expert Comment

by:VK
Comment Utility
PS: To get the size of a type

Len(myArray(0))=5
0
 
LVL 2

Accepted Solution

by:
WalterM earned 300 total points
Comment Utility
You have to reserve sufficient string space first before copying the array elements into it. If you don't, you will crash VB - as you discovered already ;-) - because you are writing to memory that very likely isn't yours to write to!

I've written a small example to demonstrate the correct technique. Add a new module to you project and paste this code:

--- code starts here ---

Option Explicit

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

Public Type udtITEM
    ItemID As Long
    ItemValue As Byte
End Type

Public Sub Test()
    Dim SrcArray() As udtITEM
    Dim S As String
    Dim DstArray() As udtITEM
    Dim I As Long

    ' Initialize the source array
    ReDim SrcArray(0 To 2)
   
    SrcArray(0).ItemID = 100
    SrcArray(0).ItemValue = 3
   
    SrcArray(1).ItemID = 200
    SrcArray(1).ItemValue = 6
   
    SrcArray(2).ItemID = 300
    SrcArray(2).ItemValue = 9
   
    ' Show the array
    For I = LBound(SrcArray) To UBound(SrcArray)
        With SrcArray(I)
            Debug.Print I, .ItemID, .ItemValue
        End With
    Next I
   
    ' Copy the array to a string
    Array2String SrcArray(), S
    ' Show the result
    Debug.Print "'"; S; "'"
   
    ' Copy the string back to an array
   
    String2Array S, DstArray
   
    ' Show the result
    For I = LBound(DstArray) To UBound(DstArray)
        With DstArray(I)
            Debug.Print I, .ItemID, .ItemValue
        End With
    Next I

End Sub

Public Sub Array2String(Source() As udtITEM, Dest As String)
    Dim LB As Long
    Dim ItemSize As Long
    Dim ItemCount As Long
    Dim ByteCount As Long

    ' Determine the array's start index
    LB = LBound(Source())
    ' Determine the number of items in the array
    ItemCount = (UBound(Source()) - LB + 1)
    If ItemCount > 0 Then
        ' Determine the size of one array element
        ItemSize = LenB(Source(LB))
        ' Determine the total number of bytes to copy
        ByteCount = ItemCount * ItemSize
        ' Reserve sufficient string space
        ' Note: since VB strings are Unicode, i.e. two
        ' bytes per character, we only need to initialize
        ' the string to half that number of characters
        Dest = String$(ByteCount \ 2, vbNullChar)
        ' Copy the source array into the destination string
        ' Use StrPtr to prevent the usual ANSI/Unicode string conversion
        CopyMemory ByVal StrPtr(Dest), Source(LB), ByteCount
    End If

End Sub

Public Sub String2Array(ByVal Source As String, Destination() As udtITEM, Optional ByVal StartIndex As Long = 0&)
    Dim LB As Long
    Dim ItemSize As Long
    Dim ItemCount As Long
    Dim ByteCount As Long

    ' Determine the number of source bytes
    ByteCount = LenB(Source)
    If ByteCount > 0 Then
        ' Determine the size of one array element
        ReDim Destination(StartIndex To StartIndex)
        ItemSize = LenB(Destination(StartIndex))
        ' Checl if the bytecount is an integer multiple of the element size
        If ((ByteCount Mod ItemSize) <> 0) Then Err.Raise 5, "Invalid string length"
        ' Determine the number of elements
        ItemCount = ByteCount \ ItemSize
        ' Reserve array space
        ReDim Destination(StartIndex To StartIndex + ItemCount - 1)
        ' Copy the source string into the destination array
        ' Use StrPtr to prevent the usual ANSI/Unicode string conversion
        CopyMemory Destination(StartIndex), ByVal StrPtr(Source), ByteCount
    End If
   
End Sub

--- code ends here ---

Michel
0
 
LVL 15

Expert Comment

by:ameba
Comment Utility
Uh, uh, too many posts to check  :-)

>       Dest = String$(ByteCount \ 2, vbNullChar)

should be:
       Dest = String$((ByteCount + 1) \ 2, vbNullChar)

Ref.:
http://www.vb2themax.com/Item.asp?PageID=TipBank&ID=325
0
 
LVL 16

Expert Comment

by:Richie_Simonetti
Comment Utility
interesting...
0
 
LVL 15

Expert Comment

by:ameba
Comment Utility
Public Sub ConvertArrayToString()
    ReDim myArray(0 To 2)
   
    myArray(0).ItemID = 100
    myArray(0).ItemValue = 3
   
    myArray(1).ItemID = 200
    myArray(1).ItemValue = 6
   
    myArray(2).ItemID = 300
    myArray(2).ItemValue = 9
   
    Dim bytes As Long
    bytes = 3 * LenB(myArray(0))
    myString = Space((bytes + 1) / 2)
    CopyMemory ByVal StrPtr(myString), ByVal VarPtr(myArray(0)), bytes
   
    ' back to array
    ReDim myArray(0 To 2)
    CopyMemory ByVal VarPtr(myArray(0)), ByVal StrPtr(myString), bytes
End Sub
0
 
LVL 1

Expert Comment

by:morgan_peat
Comment Utility
Any reason why there's an extra 1 added?
0
 
LVL 15

Expert Comment

by:ameba
Comment Utility
>Any reason why there's an extra 1 added?
Maybe not for this UDT, but, imagine there is only one byte in UDT:
Public Type udtITEM
 ' ItemID As Long  <- commented
 ItemValue As Byte
End Type

To save 3 bytes you'll need 1.5 characters string.

3/2 will give 1 for length of string.
(3+1)/2 will give correct value for length (2).

Ref.:
http://www.vb2themax.com/Item.asp?PageID=TipBank&ID=350
"byte elements aren't aligned at all"
0
What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 2

Author Comment

by:dwalsarie
Comment Utility
pfffrrrtt... I am going to try all these methods now :)
many many response on this question .. yipeeh! :D
0
 
LVL 2

Expert Comment

by:WalterM
Comment Utility
Indeed, reserving an extra byte will make the code more robust, and it can hardly be considered a waste of memory in case it isn't required.

Thanx for pointing this out, ameba!

And happy programming to you, dwalsarie ;-)

Michel
0
 
LVL 15

Expert Comment

by:ameba
Comment Utility
Thanks, WalterM
Actually, it's not always 'extra byte', but only when needed:  5 \ 2  gives 2
0
 
LVL 2

Expert Comment

by:WalterM
Comment Utility
Correct again, ameba.

Haven't slept that much - apparently - and it's late in the afternoon.

I guess I should shut up and go home ;-)

Michel
0
 
LVL 15

Expert Comment

by:ameba
Comment Utility
>Haven't slept that much
All Dutch programmers are a bit weird, I think  ;-)

dwalsarie,
sorry for extra notifications, and don't worry - there is no additional code for testing  :-)
0
 
LVL 10

Expert Comment

by:caraf_g
Comment Utility
:o)
0
 
LVL 2

Expert Comment

by:WalterM
Comment Utility
>All Dutch programmers are a bit weird, I think  ;-)

You could make that either

   All programmers are a bit weird

or

   All the Dutch are a bit weird

and still be right.

Michel
0
 
LVL 10

Expert Comment

by:caraf_g
Comment Utility
but wouldn't that make all Dutch programmers VERY weird?
0
 
LVL 2

Expert Comment

by:WalterM
Comment Utility
No comment.
0
 
LVL 2

Expert Comment

by:WalterM
Comment Utility
frantic laughing...
0
 
LVL 10

Expert Comment

by:caraf_g
Comment Utility
Ik ben niet gek, ik ben een vliegtuig

(and for those of you who don't understand *that*, "I'm not mad, I'm an aeroplane")
0
 
LVL 2

Author Comment

by:dwalsarie
Comment Utility
I thank you all very much for helping me with this problem.
I awarded the points to WalterM because I he was the first person who provided a stable and clean solution which seem to work perfectly. However, all of you guys deserve a big *hug* anyway :p

>All Dutch programmers are a bit weird, I think  ;-)

idd :-) and that's what make us the dutch :)
0
 
LVL 2

Expert Comment

by:WalterM
Comment Utility
Thanx for the points, dwalsarie!

Glad I could be of help (feeling a bit less weird now).

Michel
0
 
LVL 2

Author Comment

by:dwalsarie
Comment Utility
Yikes! I was to fast with accepting the answer :(
It still doesn't work.

However, my situation has changed a little:

Public ByteArray() As Byte
Public OutputString As String

I need to convert the ByteArray() to OutputString and back

When I use the code from WalterM and fill all the bytes in the array with value 65 (which is the ASCII code for 'A') it returns garbage only, instead of a sequence of A's..

Can someone fix this for me? I will have another 50 points waiting for the person who fixes this problem
0

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

I was working on a PowerPoint add-in the other day and a client asked me "can you implement a feature which processes a chart when it's pasted into a slide from another deck?". It got me wondering how to hook into built-in ribbon events in Office.
Since upgrading to Office 2013 or higher installing the Smart Indenter addin will fail. This article will explain how to install it so it will work regardless of the Office version installed.
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
Show developers how to use a criteria form to limit the data that appears on an Access report. It is a common requirement that users can specify the criteria for a report at runtime. The easiest way to accomplish this is using a criteria form that a…

772 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now