Results 1 to 9 of 9

Thread: CopyMemory and UDT's (Joacim??)

  1. #1

    Thread Starter
    Lively Member
    Join Date
    Aug 2000
    Location
    Australia
    Posts
    82
    In a previous post, Joacim Andersson advised me to use the CopyMemory API routine to copy a UDT to a ByteArray in order to send the data via WinSock. This works great.

    However, it appears that when a UDT is declared as a mixed set of data types, the CopyMemory routine doesn't keep the integrity. For example
    Code:
    'In Module
    
    Declare Sub CopyMemory _
    Lib "kernel32" Alias "RtlMoveMemory" ( _
    Destination As Any, _
    Source As Any, _
    ByVal Length As Long)
    
    Type typeInMsg
        Size as Long
        Function As Byte
        TID As Long
        Seq As Long
        Msg(255) as Byte
    End Type
    
    'In form
    Dim InMsg as typeInMsg
    Dim bArr() as byte
    Dim Buf() as byte
    
    'Load up the OutMsg UDT with values.
    
    InMsg.Function = 5
    InMsg.TID = 20562
    InMsg.Seq = 1
    Buf() = StrConv("Test", vbFromUnicode)
    For i = 0 To UBound(Buf())
        InMsg.Msg(i) = Buf(i)
    Next i
    
    InMsg.Size = Len(InMsg)
    
    'Copy UDT to Byte Array
    ReDim Preserve bArr(1 To Len(InMsg)) As Byte    
    CopyMemory bArr(1), InMsg, Len(InMsg)
    If tcpClient.State = sckConnected Then tcpClient.SendData bArr
    
    'The Byte string output should be
    'Size     F TID       Seq
    '13 1 0 0 5 82 80 0 0 1 0 0 0 {Start of Msg text}
    
    'However, my Byte Array reads
    'Size     Func    TID       Seq
    '13 1 0 0 5 0 0 0 82 80 0 0 1 0 0 0 {Start of Msg text}
    It appears that CopyMemory has turned my Byte into a 4 byte Long Integer which of course throws out my byte array alignment.

    Does anyone know why this happens? If so, what's the way around it?

    Appreciate any advice
    Regards
    Adrian

  2. #2
    Guru Yonatan's Avatar
    Join Date
    Apr 1999
    Location
    Israel
    Posts
    892

    Cool There are other CopyMemory fans out there!

    For some unknown super-weird reason, putting a Long in a UDT messes up a Byte in the same UDT.
    So, don't use regular Longs!
    Put this in the form:
    Code:
    Option Explicit
    
    Private Type YonaLong
        btLoByte As Byte
        btByte2 As Byte
        btByte3 As Byte
        btHiByte As Byte
    End Type
    
    Private Type typeInMsg
        Size As YonaLong
        Function As Byte
        TID As YonaLong
        Seq As YonaLong
        Msg(255) As Byte
    End Type
    
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal cbSrc As Long)
    
    Private Property Get YonaLongThing(dwLong As Long) As YonaLong
        Call CopyMemory(YonaLongThing, dwLong, 4)
    End Property
    
    Private Property Let YonaLongThing(dwLong As Long, tYonaLong As YonaLong)
        Call CopyMemory(dwLong, tYonaLong, 4)
    End Property
    Here's how you can use it:
    Code:
    ' Long->YonaLong
    ' Instead of:
    InMsg.TID = 20562
    ' Use this:
    InMsg.TID = YonaLongThing(20562)
    
    ' YonaLong->Long
    ' Instead of:
    SomeVariable = InMsg.TID
    ' Use this:
    YonaLongThing(SomeVariable) = InMsg.TID
    YonaLong does not mess up any Bytes... Because it's made of Bytes itself.
    So, using CopyMemory to convert the UDT to a Byte array, or using it to convert the Byte array to a UDT, should work with no problems now.

  3. #3
    transcendental analytic kedaman's Avatar
    Join Date
    Mar 2000
    Location
    0x002F2EA8
    Posts
    7,221
    I think i know what the odd reason is. Sam once told me if you store a misaligned udt, it will consume a lot more memory, therefore i usually try to put same type of variables after each other, here's one example:
    Code:
    '16 Bytes
    Type UDTBad
        a as Byte 'byte 1
        b as long 'byte 5-8
        c as byte 'byte 9
        d as long 'byte 13-16
    End type
    
    '8 bytes
    Type UDTGood
        a as byte 'byte 1
        b as byte 'byte 2
        c as byte 'byte 3
        d as byte 'byte 4
        e as long 'byte 5-8
    End type
    So try to store a udt with a variables that either starts at each 4'th byte or come after another of the same type. Like 4 bytes, 2 integers and 1 long.
    Now what happened with Adrians UDT is exactly this:
    Code:
    Type typeInMsg
        Size as Long ' 1-4
        Function As Byte '5
        TID As Long '9!!! not 6
        Seq As Long '13 not 10
        Msg(255) as Byte '17-272 not 14-269
    End Type
    It should be better if you do:
    Code:
    Type typeInMsg
        Size as Long
        TID As Long
        Seq As Long
        Msg(255) as Byte
        Function As Byte
    End Type
    None of the longs are now missplaced
    Use
    writing software in C++ is like driving rivets into steel beam with a toothpick.
    writing haskell makes your life easier:
    reverse (p (6*9)) where p x|x==0=""|True=chr (48+z): p y where (y,z)=divMod x 13
    To throw away OOP for low level languages is myopia, to keep OOP is hyperopia. To throw away OOP for a high level language is insight.

  4. #4
    Guru Yonatan's Avatar
    Join Date
    Apr 1999
    Location
    Israel
    Posts
    892
    You said you're doing this to send the data via Winsock.
    • If you are building the client and the server:
      It's probably easier to just change the UDT (on the client and on the server) to a non-misplaced-byte-thing one, the way kedaman explained.
    • If you are building just the client and can't change the way the server checks the incoming data:
      Time to switch to YonaLong!

  5. #5

    Thread Starter
    Lively Member
    Join Date
    Aug 2000
    Location
    Australia
    Posts
    82

    Thanks Kedaman and Yonatan

    Thanks guys, great tips.

    As I have no control over the Server, and the byte stream has to be as per my original post, then I guess it's time to switch to YonaLong!

    I just wanted to make sure that I wasn't going nuts.

    Cheers
    Adrian.

  6. #6
    Frenzied Member
    Join Date
    Aug 2000
    Location
    O!
    Posts
    1,177
    When defining UDTs, you have to be aware of how various data types are stored in memory (i.e. the required starting address).

    In most languages, Longs must start on a double word boundry. If you declare a Long after an Integer, there will be 2 bytes between the 2 variables (only if the Integer is on a word boundry, but not a double word boundry). If the Long follows a Byte, there will be 3 bytes between them.

    UDTs inside a UDT will also be on a double word boundry.

    As someone else suggested, try to group all like variables together. It also doesn't hurt to place dummy filler variables of the correct size between real variables to show the displacement. For example:
    Code:
    Private Type dclExampleRec
                 bArray(3)   As Byte       ' * needs 5 bytes
                 Fill        As Byte 
                 iByteCnt    As Integer
            End Type
    * an array of bytes is defined as vbArray + vbByte

    This alignment is true for other languages that I have used including C, Pascal, COBOL, Fortran and PL/1.

    BTW, AdrianH, you should use LenB instead of Len in the CopyMemory statement. See HOWTO: Send and Receive UDT's Using the Winsock Control for more info.

  7. #7

    Thread Starter
    Lively Member
    Join Date
    Aug 2000
    Location
    Australia
    Posts
    82
    Thanks for the feedback ccoder. I wasn't aware of the MS KB article.

    Regards
    Adrian

  8. #8
    Hyperactive Member tumblingdown's Avatar
    Join Date
    Mar 2000
    Posts
    362

    How about objects?

    Does CopyMem work for Objects? How about if these objects contain other objects?

    If it did, it would be a superb way of marshalling objects across processes ;-)



    td.
    "One logical slip and an entire scientific edifice comes tumbling down." - Robert M. Pirsig


    [email protected]

    "but if Einstein is right and God is in the details, reality requires that we sometimes get religion." - Scott Meyers.

  9. #9
    Guru Yonatan's Avatar
    Join Date
    Apr 1999
    Location
    Israel
    Posts
    892
    You can get the memory address of the object using the undocumented ObjPtr function.
    However, there is no way to retrieve the size of the object. And usually the object contains procedures, which makes it impossible to calculate.

    You'll get over it.

Posting Permissions

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



Click Here to Expand Forum to Full Width