Smart questions
Smart answers
Smart people
INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS

Member Login




Remember Me
Forgot Password?
Join Us!

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!

Join Tek-Tips
*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.

Donate Today!

Do you enjoy these
technical forums?
Donate Today! Click Here

Posting Guidelines

Promoting, selling, recruiting, coursework and thesis posting is forbidden.
Jobs from Indeed

Link To This Forum!

Partner Button
Add Stickiness To Your Site By Linking To This Professionally Managed Technical Forum.
Just copy and paste the
code below into your site.

Unscruffed (Programmer)
15 Nov 11 20:00

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:

CODE

Option 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:

CODE

00 00 = Correct bytes
00 00 = Unknown bytes - otherwise known as WTF bytes ;)
00 00 = Bytes at wrong offset

1_mBmpInfoHeader_UDT.tmp

CODE

00 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

CODE

00 01 02 03 04 05 06 07 08 09 10 11 12 13

3_mBmpInfoHeader_Bytes.tmp

CODE

00 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

CODE

00 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!

HughLerwill (Programmer)
16 Nov 11 11:41
Did you try calling it like;

CopyMemory ByVal VarPtr(bTmp4(0)), ByVal VarPtr(mBmpFileHeader), Len(mBmpFileHeader)




 
HughLerwill (Programmer)
16 Nov 11 11:50
AND use LenB(mBmpFileHeader) instead of Len(mBmpFileHeader)



 
Helpful Member!  HughLerwill (Programmer)
16 Nov 11 12:42
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
Unscruffed (Programmer)
16 Nov 11 16:54

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:

CODE

CopyMemory 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:

CODE

bfType 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:

CODE

Option 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:

CODE

00 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!

HughLerwill (Programmer)
16 Nov 11 17:06
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.  
Unscruffed (Programmer)
16 Nov 11 18:22

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!

Unscruffed (Programmer)
17 Nov 11 17:57
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:

CODE

CopyMemory Dest, ByVal VarPtr(UDT), Length

Same applies for individual items or chuncks - provide the pointer of the first item to copy:

CODE

CopyMemory Dest, ByVal VarPtr(UDT.Item2), Length

Cheers.
 

Heaven doesn't want me, and Hell's afraid I'll take over!

Reply To This Thread

Posting in the Tek-Tips forums is a member-only feature.

Click Here to join Tek-Tips and talk with other members!

Close Box

Join Tek-Tips® Today!

Join your peers on the Internet's largest technical computer professional community.
It's easy to join and it's free.

Here's Why Members Love Tek-Tips Forums:

Register now while it's still free!

Already a member? Close this window and log in.

Join Us             Close