INTELLIGENT WORK FORUMS FOR COMPUTER PROFESSIONALS
Come Join Us!
Are you a Computer / IT professional? Join Tek-Tips now!
- Talk With Other Members
- Be Notified Of Responses
To Your Posts
- Keyword Search
- One-Click Access To Your
Favorite Forums
- Automated Signatures
On Your Posts
- Best Of All, It's Free!
*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.
Partner With Us!
"Best Of Breed" Forums Add Stickiness To Your Site

(Download This Button Today!)
Feedback
"...On your site I feel quite confident that the contacts and feedback will make my life a little less hectic..."
Geography
Where in the world do Tek-Tips members come from?
|
CopyMemory - Strange behavior
|
|
VB6 on XP Hi everyone. After trying for an hour to figure out why I couldn't create a bitmap from my byte data, I finally worked out that it was the CopyMemory API that was the problem. Rather than go into lengthy code and descriptions, I wrote a little test code that you can try yourselves. To test my theory, I created a new VB6 program with the following code in Form1: CODEOption Explicit
Private Type BITMAPFILEHEADER bfType As Integer bfSize As Long bfReserved1 As Integer bfReserved2 As Integer bfOffBits As Long End Type
Private Type BITMAPINFOHEADER biSize As Long biWidth As Long biHeight As Long biPlanes As Integer biBitCount As Integer biCompression As Long biSizeImage As Long biXPelsPerMeter As Long biYPelsPerMeter As Long biClrUsed As Long biClrImportant As Long End Type
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal dwLength As Long)
Private mBmpFileHeader As BITMAPFILEHEADER Private mBmpInfoHeader As BITMAPINFOHEADER
Private Sub Command1_Click() Dim bTmp3() As Byte Dim bTmp4() As Byte ' Fill mBmpFileHeader with easy to read bytes from h00 to h13... With mBmpFileHeader .bfType = &H100& .bfSize = &H5040302 .bfReserved1 = &H706& .bfReserved2 = &H908& .bfOffBits = &H13121110 End With ' Fill mBmpInfoHeader with easy to read bytes from h00 to h39... With mBmpInfoHeader .biSize = &H3020100 .biWidth = &H7060504 .biHeight = &H11100908 .biPlanes = &H1312 .biBitCount = &H1514 .biCompression = &H19181716 .biSizeImage = &H23222120 .biXPelsPerMeter = &H27262524 .biYPelsPerMeter = &H31302928 .biClrUsed = &H35343332 .biClrImportant = &H39383736 End With ' Delete any existing test files... If Len(Dir$(App.Path & "\*.tmp")) Then Kill App.Path & "\*.tmp" End If ' Write the mBmpInfoHeader UDT directly to a file... Open App.Path & "\1_mBmpInfoHeader_UDT.tmp" For Binary As #1 Put #1, , mBmpInfoHeader Close #1 ' Write the mBmpFileHeader UDT directly to a file... Open App.Path & "\2_mBmpFileHeader_UDT.tmp" For Binary As #2 Put #2, , mBmpFileHeader Close #2 ' Copy the mBmpInfoHeader UDT to a byte array, ' and write the byte array to a file... ReDim bTmp3(Len(mBmpInfoHeader) - 1) CopyMemory bTmp3(0), ByVal mBmpInfoHeader, Len(mBmpInfoHeader) Open App.Path & "\3_mBmpInfoHeader_Bytes.tmp" For Binary As #3 Put #3, , bTmp3 Close #3 ' Copy the mBmpFileHeader UDT to a byte array, ' and write the byte array to a file... ReDim bTmp4(Len(mBmpFileHeader) - 1) CopyMemory bTmp4(0), ByVal mBmpFileHeader, Len(mBmpFileHeader) Open App.Path & "\4_mBmpFileHeader_Bytes.tmp" For Binary As #4 Put #4, , bTmp4 Close #4 ' Quit. Unload Me End Sub Run the program, click the command button, then open the 4 files in a Hex editor. I'll use the following key to illustrate the obvious: CODE00 00 = Correct bytes 00 00 = Unknown bytes - otherwise known as WTF bytes ;) 00 00 = Bytes at wrong offset 1_mBmpInfoHeader_UDT.tmp CODE00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 2_mBmpFileHeader_UDT.tmp CODE00 01 02 03 04 05 06 07 08 09 10 11 12 13 3_mBmpInfoHeader_Bytes.tmp CODE00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 4_mBmpFileHeader_Bytes.tmp CODE00 01 00 00 02 03 04 05 06 07 08 09 10 11 One UDT copies fine, and the other doesn't. Anyone have any idea in the world about what's going on here? Don't worry, your not crazy. If you think that what's happening here is impossible you're not alone. Try it yourself and see if you get the same results. Cheers. Heaven doesn't want me, and Hell's afraid I'll take over! |
|
Did you try calling it like;
CopyMemory ByVal VarPtr(bTmp4(0)), ByVal VarPtr(mBmpFileHeader), Len(mBmpFileHeader)
|
|
AND use LenB(mBmpFileHeader) instead of Len(mBmpFileHeader)
|
|
However I think what you are seeing is byte padding in the udt as it is stored in memory. When you copy it to the byte array from memory you include the padding. When you write the udt direct to disk however vb automatically strips out the padding. For some discussion see thread222-693110: Memory usage by a Type |
|
Thanks Hugh, you are correct about the alignment. The thread you pointed out explains the problem. In relation to ByRef, ByVal and VarPtr, I tested each of the following and all had the same effect when copying from a UDT to a byte array: CODECopyMemory Dest, UDT, Length CopyMemory Dest, ByVal UDT, Length CopyMemory Dest, VarPtr(UDT), Length CopyMemory Dest, ByVal VarPtr(UDT), Length The solution is therefore to copy UDT items which are not memory aligned seperately from items which are memory aligned. For example, with the BITMAPFILEHEADER structure, I only need to worry about the first 2 values: CODEbfType As Integer <= 16bit aligned in both UDT and memory.
bfSize As Long <= 16bit aligned in UDT, 32bit aligned in memory. This is why we get the two 00 00 padding bytes before it. In memory, the remaining items are all correctly aligned because of the alignment of bfSize. This means bfType can be copied in one call, and the remaining items copied seperately as a "chunk" as follows: CODEOption Explicit
Private Type BITMAPFILEHEADER bfType As Integer bfSize As Long bfReserved1 As Integer bfReserved2 As Integer bfOffBits As Long End Type
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal dwLength As Long)
Private Sub Command1_Click() Dim bTmp() As Byte
' Fill mBmpFileHeader with easy to read bytes from h00 to h13... With mBmpFileHeader .bfType = &H100& .bfSize = &H5040302 .bfReserved1 = &H706& .bfReserved2 = &H908& .bfOffBits = &H13121110 End With
' Delete any existing test files... If Len(Dir$(App.Path & "\*.tmp")) Then Kill App.Path & "\*.tmp" End If
ReDim bTmp(Len(mBmpFileHeader) - 1) ' Copy just the first item. ' This is an Integer, so only copy 2 bytes... CopyMemory bTmp(0), mBmpFileHeader, 2 ' Copy the rest of the UDT. ' The source location is the pointer of the second item (bfSize). ' The number of bytes to copy is Len(mBmpFileHeader) minus the ' length of bfSize which is 2... CopyMemory bTmp(2), mBmpFileHeader.bfSize, Len(mBmpFileHeader) - 2 ' Write the byte array to a file... Open App.Path & "\mBmpFileHeader_Corrected.tmp" For Binary As #1 Put #1, , bTmp Close #1
' Quit... Unload Me End Sub This gives the following result in the file: CODE00 01 02 03 04 05 06 07 08 09 10 11 12 13 ...which means that the UDT copied correctly to the byte array. This process will become much more complex with larger UDT's where items drop in and out of "memory matched alignment", but you should be able to avoid copying each UDT item individually and break it up into "chunks". Thanks again to Hugh. Heaven doesn't want me, and Hell's afraid I'll take over! |
|
Good. Be aware of the difference between Len(udt) and LenB(udt), the latter includes the padding chars; because that should affect the DIM used to setup the byte array. |
|
Good tip Hugh. I didn't fully understand the difference between Len and LenB until I came across this UDT problem. After reading your 2nd post above and doing some testing, the difference became clear. Heaven doesn't want me, and Hell's afraid I'll take over! |
|
Turns out that Hugh was correct on all counts. While sending the UDT ByRef worked in the IDE, it fails in the compiled executable. CopyMemory needs a pointer so the correct syntax is: CODECopyMemory Dest, ByVal VarPtr(UDT), Length Same applies for individual items or chuncks - provide the pointer of the first item to copy: CODECopyMemory Dest, ByVal VarPtr(UDT.Item2), Length Cheers. Heaven doesn't want me, and Hell's afraid I'll take over! |
|
|
 |
|