[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

microsoft.public.vb.general.discussion

Type Structure To Byte Array and Back

BeeJ

4/26/2012 1:42:00 AM

So why does one work and the other not?
Confused because if I send the first Type (sString As String) to an
Open Binary File and read it back it works fine. I just do a Put
#lFnbr , tHdrA1 both in the IDE and as .EXE

What are the gotchas?
Googling only found .Net stuff and even then not really related to
this.
Anyway, bottom line I need to convert various Type structures to a Byte
Arrays. How to?

Note: in one case the IDE crashes after several runs and in the other
case the .EXE crashes (these code parts were put into seperate button
clicks to call multiple times as desired).

Option Explicit

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

Private Type tHeaderAType
lLong As Long
ayByte(0 To 1) As Byte
sString As String
dDouble As Double
End Type

Private Type tHeaderBType
lLong As Long
ayByte(0 To 1) As Byte
ayString() As Byte
dDouble As Double
End Type
'

Private Sub Form_Load()
'
' Part A
Debug.Print "PartA Does Not Work In THe IDE But Seems To Work As
..EXE"

Dim tHdrA1 As tHeaderAType
Dim tHdrA2 As tHeaderAType

Dim tHdrB1 As tHeaderBType
Dim tHdrB2 As tHeaderBType

Dim ayByte() As Byte

tHdrA1.lLong = &H100
tHdrA1.ayByte(0) = Asc("A")
tHdrA1.ayByte(1) = Asc("Z")
tHdrA1.sString = "ABC123"
tHdrA1.dDouble = 1.00001

ReDim ayByte(0 To LenB(tHdrA1) - 1)

' Convert to byte array
CopyMemory ayByte(0), tHdrA1, LenB(tHdrA1)

' now try to get it back
CopyMemory tHdrA2, ayByte(0), UBound(ayByte) + 1

Debug.Print tHdrA2.lLong
Debug.Print Chr$(tHdrA2.ayByte(0))
Debug.Print Chr$(tHdrA2.ayByte(1))
Debug.Print tHdrA2.sString
Debug.Print tHdrA2.dDouble

' =========================================
' Part B
Debug.Print "PartB Seems To Work In IDE Once But Not as .EXE"

tHdrB1.lLong = &H100
tHdrB1.ayByte(0) = Asc("A")
tHdrB1.ayByte(1) = Asc("Z")
tHdrB1.ayString = "ABC123"
tHdrB1.dDouble = 1.00001

ReDim ayByte(0 To LenB(tHdrB1) - 1)

CopyMemory ayByte(0), tHdrB1, LenB(tHdrB1)

' now try to get it back
CopyMemory tHdrB2, ayByte(0), UBound(ayByte) + 1

Debug.Print tHdrB2.lLong
Debug.Print Chr$(tHdrB2.ayByte(0))
Debug.Print Chr$(tHdrB2.ayByte(1))
Debug.Print tHdrB2.ayString
Debug.Print tHdrB2.dDouble

End Sub '

===================
Debug.Print Results
===================
PartA Does Not Work
256
A
Z
Form1/Form1
1.00001

PartB Seems To Work
256
A
Z
ABC123
1.00001

--
So where are we?
Not the street address.
Not the city.
Not the country.
Not the Earth.
Not the Solar System.
Not the Galaxy.
Not the Universe.
Not the Brane.
So where is the Brane?
Where are we?

Life is but a dream!


58 Answers

Mike Williams

4/26/2012 11:44:00 AM

0

"BeeJ" <nospam@spamnot.com> wrote in message
news:jna94i$2sd$1@speranza.aioe.org...
> So why does one work and the other not?
> [lots of UDT swap test code snipped]

Not sure offhand what is going on there but your problem might be due to
your code using CopyMemory to dump raw data into a UDT containing a variable
length String (variable length Strings are represented by a four byte
pointer in the UDT data block rather than as the actual string data itself).
At first glance it appears that the code should work because the String
pointer should be copied correctly, but the fact that you are doing it using
CopyMemory (sort of "behind VB's back") might cause VB to mess things up if
it decides at some point to shuffle some strings about in memory (a bit like
the old fashioned kind of garbage collection). You should be able to fix it
by ditching the two Copymemory lines completely and by instead having VB
itself do the job for you, as in the following:

LSet tHdrA2 = tHdrA1

Mike


BeeJ

4/26/2012 2:07:00 PM

0

Yes I understand that the variable length string is problematic.

But if so, then why does the double come back correctly and the string not
since the double follows the string?
Seems that the string (lack of) information would clobber the double.

VB must be working "hard" behind the scenes to enable Open x For Binary As
#lFNbr and Put / Get #lFNbr , tUDT with a variable length string to work
properly.

The bottom line here is that I am hoping to easily convert a UDT with
variable length strings to a byte array..
Ultimate goal is to be able use ReadFile and WriteFile APIs to work with
UDTs containing variable length strings as does the Open x For Binary As
#lFNbr and Put / Get #lFNbr , tUDT so I can handle files >2GBytes.

Since the API declaration allows ANY, I though maybe I could work with the
LenB(UDT) and pass that and use to reconstruct the UDT on read but the same
problem exists as with just doing the ByteArray conversion.

Mike Williams

4/26/2012 2:59:00 PM

0

"BeeJ" <nospam@spamnot.com> wrote in message
news:jnbkq3$f4f$1@speranza.aioe.org...

> Yes I understand that the variable length string
> is problematic. But if so, then why does the double
> come back correctly and the string not since the
> double follows the string? Seems that the string
> (lack of) information would clobber the double.

No. That's not the way it works. If VB decides for some reason to move the
actual variable length string of UDT1 to some different location in memory
(after you have used CopyMemory to copy the raw data of UDT1 into UDT2) then
it will alter the four byte string pointer in UDT1 to point to that new
memory location. In such a case only those four bytes (bytes 9 - 12 of the
20 UDT data bytes in your specific example) would be altered and all the
other bytes, including the eight bytes which contain the actual value of the
Double (bytes 13 - 20), would remain intact. Following this alteration the
entire data of UDT1 would of course be valid and all UDT entries would be
read correctly. However, VB would /not/ change the four bytes (bytes 9 - 12)
in UDT2 because it does not know that UDT2 is supposed to point to the same
string in memory, and so those specific four bytes of UDT2 (which you copied
from UDT1 using CopyMemory) would still contain the old location of the
variable length string, which does /not/ now point to the same valid string
in memory, or even to any valid string. However, all the other bytes of UDT2
around those four string pointer bytes would still be valid, including the
Double, because those bytes contain the actual values of those variables
(the Long, the two Bytes and the Double). Does that make sense to you?

Mike



Mike Williams

4/26/2012 5:52:00 PM

0

"BeeJ" <nospam@spamnot.com> wrote in message
news:jnbkq3$f4f$1@speranza.aioe.org...
>
> VB must be working "hard" behind the scenes to enable
> Open x For Binary As #lFNbr and Put / Get #lFNbr ,
> tUDT with a variable length string to work properly.

Yes it does work hard behind the scenes. The VB Put statement doesn't just
do a raw dump of the UDT data. It manipulates the data of each entry,
depending on its type, in accordance with a strict set of rules. For
example, in the UDT data /as it sits in memory/ (using your own tHeaderAType
UDT as an example) the lLong element contains four bytes (because it is a
Long). The next part of the UDT (ayByte(0 To 1) As Byte) also occupies four
bytes of memory. The first two bytes of that four byte block contain the
data for ayByte(0) and ayByte(1) and the next two bytes of that four byte
block are padding bytes (usually containing zero). VB needs those two
padding bytes because it wants to start the next element of the UDT on a
four byte boundary, which would otherwise not be the case. Then there would
be four bytes containing the four byte pointer to the standard variable
length String (tHdrA1.sString = "ABC123"). So, in memory those three parts
would be as follows (twelve bytes in memory):

0 tHdrA1.lLong = &H100
1
0
0
65 tHdrA1.ayByte(0) = Asc("A")
90 tHdrA1.ayByte(1) = Asc("Z")
0 padding byte
0 padding byte
x (xxxx is a four byte String pointer)
x
x
x

When the VB Put statement writes those to disk it will write the Long in the
same way (as of course it must do) but it will write /only/ the actual data
for the two Byte element and it will /not/ write the two padding bytes to
disk, so only six bytes will be written to the disk file for those two
things. Then VB will write the actual string data itself to the disk file (a
pointer would of course be meaningless). However, because the string is a
standard variable length String then (in this specific case) VB will write
two bytes representing the length of the String as an integer value followed
by six bytes of data for the String itself (VB converts the standard "two
bytes per character" String into "one byte per character Ansi"). So, the
data written to disk (for the part of the UDT we have just mentioned) would
contain fourteen bytes on disk, as follows:

0 tHdrA1.lLong = &H100
1
0
0
65 tHdrA1.ayByte(0) = Asc("A")
90 tHdrA1.ayByte(1) = Asc("Z")
6 ) six character
0 ) string length
65 "A"
66 "B"
67 "C"
49 "1"
50 "2"
51 "3"

The VB Get statement of course performs the same sort of analysis in
reverse. If you want to look into this further then type "Put" into your VB
code window and hit the F1 key.

Mike



BeeJ

4/26/2012 8:13:00 PM

0

Mike Williams explained on 4/26/2012 :
> "BeeJ" <nospam@spamnot.com> wrote in message
> news:jnbkq3$f4f$1@speranza.aioe.org...
>>
>> VB must be working "hard" behind the scenes to enable
>> Open x For Binary As #lFNbr and Put / Get #lFNbr ,
>> tUDT with a variable length string to work properly.
>
> Yes it does work hard behind the scenes. The VB Put statement doesn't just do
> a raw dump of the UDT data. It manipulates the data of each entry, depending
> on its type, in accordance with a strict set of rules. For example, in the
> UDT data /as it sits in memory/ (using your own tHeaderAType UDT as an
> example) the lLong element contains four bytes (because it is a Long). The
> next part of the UDT (ayByte(0 To 1) As Byte) also occupies four bytes of
> memory. The first two bytes of that four byte block contain the data for
> ayByte(0) and ayByte(1) and the next two bytes of that four byte block are
> padding bytes (usually containing zero). VB needs those two padding bytes
> because it wants to start the next element of the UDT on a four byte
> boundary, which would otherwise not be the case. Then there would be four
> bytes containing the four byte pointer to the standard variable length String
> (tHdrA1.sString = "ABC123"). So, in memory those three parts would be as
> follows (twelve bytes in memory):
>
> 0 tHdrA1.lLong = &H100
> 1
> 0
> 0
> 65 tHdrA1.ayByte(0) = Asc("A")
> 90 tHdrA1.ayByte(1) = Asc("Z")
> 0 padding byte
> 0 padding byte
> x (xxxx is a four byte String pointer)
> x
> x
> x
>
> When the VB Put statement writes those to disk it will write the Long in the
> same way (as of course it must do) but it will write /only/ the actual data
> for the two Byte element and it will /not/ write the two padding bytes to
> disk, so only six bytes will be written to the disk file for those two
> things. Then VB will write the actual string data itself to the disk file (a
> pointer would of course be meaningless). However, because the string is a
> standard variable length String then (in this specific case) VB will write
> two bytes representing the length of the String as an integer value followed
> by six bytes of data for the String itself (VB converts the standard "two
> bytes per character" String into "one byte per character Ansi"). So, the data
> written to disk (for the part of the UDT we have just mentioned) would
> contain fourteen bytes on disk, as follows:
>
> 0 tHdrA1.lLong = &H100
> 1
> 0
> 0
> 65 tHdrA1.ayByte(0) = Asc("A")
> 90 tHdrA1.ayByte(1) = Asc("Z")
> 6 ) six character
> 0 ) string length
> 65 "A"
> 66 "B"
> 67 "C"
> 49 "1"
> 50 "2"
> 51 "3"
>
> The VB Get statement of course performs the same sort of analysis in reverse.
> If you want to look into this further then type "Put" into your VB code
> window and hit the F1 key.
>
> Mike

I have used the Get and Put with UDTs for years with great success.
And I do know that VB does move strings around at its whim.

What you are saying is what I suspected but was hoping that there was
some mechanism unfamiliar to me that would allow me to do what I need.

Unless I have missed something you have said, I am going to have to
uniquely break apart the UDT and do ReadFile and Write file on the
components.

Any clues on handling variable length Strings with ReadFile and
WriteFile. Right now I am considering and have coded up
convert the string to a byte array vbFromUniCode (data is raw binary)
write the length of the byte array (UBOUND(yA) +1)
write the byte array

Then on Read
read the byte array length
ReDim yA(length)
read into the byte array
convert to string

This all works but requires a lot more code since the UDT is large.

Fortunately there are no Variants in the UDT so I don't have to figure
that out.

--
So where are we?
Not the street address.
Not the city.
Not the country.
Not the Earth.
Not the Solar System.
Not the Galaxy.
Not the Universe.
Not the Brane.
So where is the Brane?
Where are we?

Life is but a dream!


ralph

4/26/2012 9:01:00 PM

0

On Wed, 25 Apr 2012 18:41:36 -0700, BeeJ <nospam@spamnot.com> wrote:

>So why does one work and the other not?
>Confused because if I send the first Type (sString As String) to an
>Open Binary File and read it back it works fine. I just do a Put
>#lFnbr , tHdrA1 both in the IDE and as .EXE
>
>What are the gotchas?
>Googling only found .Net stuff and even then not really related to
>this.
>Anyway, bottom line I need to convert various Type structures to a Byte
>Arrays. How to?
>

Why?

I suspect you don't need either UDTs or Byte Arrays.

Create a class and serialize it. Write, test, and forget it.

-ralph

Mike Williams

4/26/2012 9:26:00 PM

0

"BeeJ" <nospam@spamnot.com> wrote in message
news:jnca7m$c3u$1@speranza.aioe.org...
>
> What you are saying is what I suspected but . . .

Well actually that cannot be true because if you had already suspected what
I told you then you would not have come back with the question you did
regarding why the String could be corrupt whilst the Double following it in
the UDT was fine. You'll probably get your code working much easier if you
"tell it like it is".

> Unless I have missed something you have said, I am going
> to have to uniquely break apart the UDT and do ReadFile
> and Write file on the components.

Well, yes. If you want to save and then read the data yourself so that it
can be dumped into a UDT and so the UDT will understand what that data
represents then yes, you are going to need to do that. But you're still
going to need to be careful not to end up with your original problem when
you do so.

Also, regarding your remarks about writing the length of a UDT variable
length String to disk and then writing the byte data of the String itself,
and then reading it all back and dumping that raw data into the UDT, you are
going to have to be a bit careful with that. For example, you have said that
is what you are already doing and you have said that it works, but if that
is the case then you must not have tried it with a variable length String
greater than 65534 characters because I don't think (from what you have told
me so far) that you will have got the length data correct for such a String
because it is not quite as straight forward as I think you imagine.

What have you got against using Get and Put by the way? Is there a specific
reason why you cannot use those methods? I know you've used extremely large
files in the past for other things, but are you really expecting to deal
with UDTs or arrays of UDTs that exceed the size of file that VB can handle?

Mike



Karl E. Peterson

4/26/2012 10:19:00 PM

0

BeeJ laid this down on his screen :
> Yes I understand that the variable length string is problematic.
>
> But if so, then why does the double come back correctly and the string not
> since the double follows the string?
> Seems that the string (lack of) information would clobber the double.

Nope. The "string" is, as Mike said, just four bytes - always four
bytes - pointing to a location in memory where the string *data*
resides.

If you want to understand memory structures, I'd suggest you snag
http://vb.mvps.org/sampl... for a module that you can drop into
any project, which offers a number of useful routines. Including,
passing it a pointer and seeing what's in memory there, byte by byte.

> VB must be working "hard" behind the scenes to enable Open x For Binary As
> #lFNbr and Put / Get #lFNbr , tUDT with a variable length string to work
> properly.

It is. In lots of helpful and occassionally evil ways.

> The bottom line here is that I am hoping to easily convert a UDT with
> variable length strings to a byte array..

Then you'll need to become familiar with how they're stored in memory.
Or, write it out with Put to a file, and read it back in (to the byte
array) with Get.

> Ultimate goal is to be able use ReadFile and WriteFile APIs to work with UDTs
> containing variable length strings as does the Open x For Binary As #lFNbr
> and Put / Get #lFNbr , tUDT so I can handle files >2GBytes.

Most file i/o routines don't "like" variable length strings. You seem
to be on the verge of understanding why.

> Since the API declaration allows ANY, I though maybe I could work with the
> LenB(UDT) and pass that and use to reconstruct the UDT on read but the same
> problem exists as with just doing the ByteArray conversion.

GPF waiting to happen. <g>

--
..NET: It's About Trust!
http://vfre...


BeeJ

4/26/2012 10:35:00 PM

0

Mike Williams laid this down on his screen :
> "BeeJ" <nospam@spamnot.com> wrote in message
> news:jnca7m$c3u$1@speranza.aioe.org...
>>
>> What you are saying is what I suspected but . . .
>
> Well actually that cannot be true because if you had already suspected what I
> told you then you would not have come back with the question you did
> regarding why the String could be corrupt whilst the Double following it in
> the UDT was fine. You'll probably get your code working much easier if you
> "tell it like it is".
>
>> Unless I have missed something you have said, I am going
>> to have to uniquely break apart the UDT and do ReadFile
>> and Write file on the components.
>
> Well, yes. If you want to save and then read the data yourself so that it can
> be dumped into a UDT and so the UDT will understand what that data represents
> then yes, you are going to need to do that. But you're still going to need to
> be careful not to end up with your original problem when you do so.
>
> Also, regarding your remarks about writing the length of a UDT variable
> length String to disk and then writing the byte data of the String itself,
> and then reading it all back and dumping that raw data into the UDT, you are
> going to have to be a bit careful with that. For example, you have said that
> is what you are already doing and you have said that it works, but if that is
> the case then you must not have tried it with a variable length String
> greater than 65534 characters because I don't think (from what you have told
> me so far) that you will have got the length data correct for such a String
> because it is not quite as straight forward as I think you imagine.
>
> What have you got against using Get and Put by the way? Is there a specific
> reason why you cannot use those methods? I know you've used extremely large
> files in the past for other things, but are you really expecting to deal with
> UDTs or arrays of UDTs that exceed the size of file that VB can handle?
>
> Mike

The UDT is a header for very large files.
The UDT contains abundant information about the contents of the file.
So yes I am dealing with >2GBytes. Tpically 7GBytes.

I have nothing against Get and Put and have used them extensively when
appropriate.

--
So where are we?
Not the street address.
Not the city.
Not the country.
Not the Earth.
Not the Solar System.
Not the Galaxy.
Not the Universe.
Not the Brane.
So where is the Brane?
Where are we?

Life is but a dream!


BeeJ

4/26/2012 10:37:00 PM

0

ralph presented the following explanation :
> On Wed, 25 Apr 2012 18:41:36 -0700, BeeJ <nospam@spamnot.com> wrote:
>
>> So why does one work and the other not?
>> Confused because if I send the first Type (sString As String) to an
>> Open Binary File and read it back it works fine. I just do a Put
>> #lFnbr , tHdrA1 both in the IDE and as .EXE
>>
>> What are the gotchas?
>> Googling only found .Net stuff and even then not really related to
>> this.
>> Anyway, bottom line I need to convert various Type structures to a Byte
>> Arrays. How to?
>>
>
> Why?
>
> I suspect you don't need either UDTs or Byte Arrays.
>
> Create a class and serialize it. Write, test, and forget it.
>
> -ralph

Well 'cause the code already exists with UDTs and I am not changing
that. Previously files were considered to be <2GBytes but not now.

--
So where are we?
Not the street address.
Not the city.
Not the country.
Not the Earth.
Not the Solar System.
Not the Galaxy.
Not the Universe.
Not the Brane.
So where is the Brane?
Where are we?

Life is but a dream!