Valhalla Legends Forums Archive | Visual Basic Programming | Explanation of CopyMemory

AuthorMessageTime
FrostWraith
Let's say we have this function
[code]Function MakeDWORD(Value As Long) As String
    Dim Result As String * 4
    CopyMemory ByVal Result, Value, 4
    MakeDWORD = Result
End Function[/code]

Could someone help me with a clear step-by-step explanation of what CopyMemory does? I hate using code I dont completely understand.
August 22, 2006, 10:32 PM
Topaz
That function converts the given value into a signed(not sure if it's signed) long and returns it.
August 22, 2006, 11:57 PM
FrostWraith
Yes, but if this function had to be recoded w/o using copymemory, what would it look like?
August 23, 2006, 2:03 AM
Topaz
[quote author=FrostWraith link=topic=15573.msg157026#msg157026 date=1156298606]
Yes, but if this function had to be recoded w/o using copymemory, what would it look like?
[/quote]

As far as I know, CopyMemory is the only way you can convert between VB6 values and C structs.
August 23, 2006, 2:53 AM
TehUser
[quote author=Topaz link=topic=15573.msg157029#msg157029 date=1156301585]
As far as I know, CopyMemory is the only way you can convert between VB6 values and C structs.
[/quote]

Not at all.

[quote author=FrostWraith link=topic=15573.msg157003#msg157003 date=1156285973]
Could someone help me with a clear step-by-step explanation of what CopyMemory does? I hate using code I dont completely understand.
[/quote]
CopyMemory does exactly what it looks like it's doing.  It copies memory from one location to another.  However, in this case, it's being used to create a string value from a long integer.  Because if you used Visual Basic's CStr function (Convert to String), you'd end up with this:
[code]
Dim myNum as Long
Dim myStr as String
myNum = 12345678
myStr = CStr(myNum) ' myStr = "12345678"
[/code]

When you use CopyMemory on the other hand, the long value (4 bytes on most systems), is copied into the string.  So if we look at 12345678 in hex, it's equal to 0x00BC614E (or rather, in memory, it's going to be stored as 0x4E61BC00 because of the endian-ness of the PC.)  Thus, the string after CopyMemory holds the character 0x4E, 0x61, 0xBC, and 0x00.  This is significantly different from the original string.

Now, for the purpose: Visual Basic doesn't have pointers (generally speaking) and type-casting on a low level, so when we're sending binary data (technically, all data is binary, but the term seems to be used for data that contains non-printable characters) we have to convert it into a string format.  There's a huge difference between sending the string "2" (0x32) and the number 2 (0x02000000).  What CopyMemory allows us to do is load that binary data into a string in an easy fashion.  And you can see it in MakeDWORD where an input long integer is converted to a four byte string.

Lastly, if you wanted to write a replacement, it would look something like this:
[code]
Option Explicit

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As Any, _
    source As Any, ByVal bytes As Long)


Private Sub Form_Load()
    Debug.Print DebugOutput(MakeDWORDVB(12345678))
    Debug.Print DebugOutput(MakeDWORDCopyMemory(12345678))
End Sub

Public Function MakeDWORDVB(Value As Long) As String
    Dim tempStr As String
    ' Convert the value to its hexadecimal representation.
    ' In the case of 12345678, this is "BC614E"
    tempStr = CStr(Hex(Value))
    ' Pad the string with zeros.
    ' Use the Right$ function to remove all but the last 8
    '  characters.  This is because a 4-byte value needs 8
    '  representative bytes.  In the case of 12345678, these
    '  bytes are "00BC614E".
    tempStr = Right$("00000000" & tempStr, 8)
   
    ' Now, because of the endianness of the PC (has to do with
    '  byte order), we have to reverse the bytes for the DWORD
    '  to be read appropriately.  This little loop takes care
    '  of converting each set of two bytes ("00" "BC" "61" "4E")
    '  to their character format.  It also reverses the byte
    '  order so that the DWORD will be read appropriately by the
    '  receiving end.
    Dim i As Byte
    For i = 1 To 7 Step 2
        MakeDWORDVB = Chr$("&H" & Mid$(tempStr, i, 2)) & MakeDWORDVB
    Next i
End Function

Function MakeDWORDCopyMemory(Value As Long) As String
    Dim Result As String * 4
    CopyMemory ByVal Result, Value, 4
    MakeDWORDCopyMemory = Result
End Function
[/code]

Output:[code]0000:  4E 61 BC 00                                       Naź.............
0000:  4E 61 BC 00                                       Naź.............
[/code]
August 23, 2006, 3:24 AM
Networks
Why were you born? Show off! :P
August 23, 2006, 3:26 AM
TheMinistered
I don't much feel like reading TehUsers long overcomplicated explanation, so I'll explain it simply:

Function MakeDWORD(Value As Long) As String
    Dim Result As String * 4
    CopyMemory ByVal Result, Value, 4
    MakeDWORD = Result
End Function

This takes a 4byte DWORD and converts it to a 4 byte string.  There are no conversion operators for visual basic that work on this level.

Copymemory works by moving a section/block of memory from one location to another Src to Dest

In this case Src is Value, Dest is Result.
Src = Long, Dest = String

Thus offering effecient conversion from Long To String (CStr does literal conversion to an ascii string)
August 23, 2006, 5:39 AM
JoeTheOdd
Note, CopyMemory doesn't convert anything. It's given three values, in, out, and length. It copys, byte for byte, [tt]length[/tt] bytes worth of [tt]in[/tt] to the pointer specified in [tt]out[/tt]. We just happen to use that to write the contents of a Long to a String. If you ask me, that behavior shouldn't be used in good programming practice, because that doesn't sound like the intended use.

Excuse my Java/C#/whatever you call this, but this is what is, by my standards, a proper makeDword(int) function. Untested, of course, but you'll live (seriously, you will!):
[code]public String makeDword(int i)
{
    byte b0 = (byte)(i && 0xff000000) >> 24;
    byte b1 = (byte)(i && 0x00ff0000) >> 16;
    byte b2 = (byte)(i && 0x0000ff00) >> 8;
    byte b3 = (byte)(i && 0x000000ff);

    return b0.toString() + b1.toString() + b2.toSring() + b3.toString();
}[/code]
August 24, 2006, 6:18 AM
Myndfyr
Yeah, don't call that C#.
August 24, 2006, 11:03 AM
JoeTheOdd
Ah, quit insulting my coding styles and teach me the proper! I took a Java class and have those coding standards fixed in my brain now, so eh.
August 25, 2006, 4:37 AM
Quarantine
Yea thats was to hard looking to be C#, C# is all about doing it easy.
August 25, 2006, 5:13 AM
Myndfyr
[quote author=Joe[x86] link=topic=15573.msg157187#msg157187 date=1156480629]
Ah, quit insulting my coding styles and teach me the proper! I took a Java class and have those coding standards fixed in my brain now, so eh.
[/quote]
If you wanted to make it hex, you'd just call:
[code]
i.ToString("x8");
[/code]

The 8 is included to ensure that it's 8 characters wide (with 0s prepended).  To skip that, just "x" will do.

Plus your code would have failed because you attempted to do the logical AND (&&) when the numerical AND was desired.

Plus it would have failed because you typecasted the result of the AND to (byte), and then shifted it, when you should have shifted the AND result and then typecasted.  Proper:
[code](byte)((i & 0xff000000) >> 24);[/code]

Plus your example would have printed out the number big-endian.  Not necessarily wrong (it's how we write individual numbers in hex), I'm just saying.

Note that Strings are not adequate nor proper places to store binary data in either Java or C# because they are subject to encoding strategies related to their runtime environments.  The CopyMemory function is used to store data in Strings in Visual Basic because people don't use byte arrays for it.  Byte arrays are, however, available in Java and C#.
August 25, 2006, 3:45 PM
A2
[code][code]copy memory is really usefull, especially for filling vb types which can be build alot like cstructs

double = 8bytes
long = 4bytes
integer = 2bytes
byte = 1byte

to answer your question, without copymem that function looks like

[/code]function long2str(byval n as long) as string

  long2str = _
    chr((n and &HFF000000&) mod 256) & _
    chr((n and &H00FF0000&) mod 256) & _
    chr((n and &H0000FF00&) mod 256) & _
    chr(n and &H000000FF&)

end function[/code]

or backwards... dont know offhand, too lazy for testing!
February 13, 2007, 3:06 AM
TheMinistered
It seems kind of pointless to use a function like that to convert between each other when you could just use pointers and mov operations?  I mean I'm sure pointers would be far more effecient and easier to read than using a bunch of binary operators to do it.  but then again joe is a complete newbie++ the vb6 users used copymemory because vb6 doesn't support pointers... and joes code also sucks because it only applys to dword->string conversion... what happens if you need support for bigger types/arrays... seems like a lot more overhead than required but as I said before joe is a completely dumb newbie++
February 14, 2007, 3:15 AM
shout
[quote author=MyndFyre[vL] link=topic=15573.msg157196#msg157196 date=1156520749]
[code]
i.ToString("x8");
[/code]
[/quote]

"X8" for uppercase! :D
February 14, 2007, 12:37 PM
Ante
[quote author=FrostWraith link=topic=15573.msg157026#msg157026 date=1156298606]
Yes, but if this function had to be recoded w/o using copymemory, what would it look like?
[/quote]
[code]
Function Makedword(Value as long) as string
if value >= 0 then
makedword = makedword & chr(value mod 256) & chr((value mod (256^2)) \ (256^1)) & chr((value mod (256^3)) \ (256^2)) & chr((value mod (256^4)) \ (256^3))
else
value=value + 2147483648#
makedword = makedword & chr(value mod 256) & chr((value mod (256^2)) \ (256^1)) & chr((value mod (256^3)) \ (256^2)) & chr(((value mod (256^4)) \ (256^3))+128)
end if
End Function
[/code]

notice they are backward slashes (\), which mean integer division, not normal division

if its not the above, then add Makedword=strreverse(makedword) right below the "End if"
February 14, 2007, 4:08 PM
JoeTheOdd
[quote author=Ante link=topic=15573.msg164825#msg164825 date=1171469320]
[quote author=FrostWraith link=topic=15573.msg157026#msg157026 date=1156298606]
Yes, but if this function had to be recoded w/o using copymemory, what would it look like?
[/quote]
[code]
Function Makedword(Value as long) as string
if value >= 0 then
makedword = makedword & chr(value mod 256) & chr((value mod (256^2)) \ (256^1)) & chr((value mod (256^3)) \ (256^2)) & chr((value mod (256^4)) \ (256^3))
else
value=value + 2147483648#
makedword = makedword & chr(value mod 256) & chr((value mod (256^2)) \ (256^1)) & chr((value mod (256^3)) \ (256^2)) & chr(((value mod (256^4)) \ (256^3))+128)
end if
End Function
[/code]

notice they are backward slashes (\), which mean integer division, not normal division

if its not the above, then add Makedword=strreverse(makedword) right below the "End if"
[/quote]

OH DEAR GOD THAT HURT, lol.

[code]Function MakeDWORD(Value as Long) As String
    Dim RET as String * 4
    Mid(RET, 1, 1) = Value And &HFF000000
    Mid(RET, 2, 1) = Value And &HFF0000
    Mid(RET, 3, 1) = Value And &HFF00
    Mid(RET, 4, 1) = Value And &HFF
    MakeDWORD = RET
End Function[/code]

Note that you're doing three bad things:
1. Using return as a variable.
2. Concatenating to a string that has no contents.
3. Modifying a value passed to the function with out intention of making a ByRef return.

I don't even know what that whole if is about. :P
February 15, 2007, 3:42 AM
Ante
[code]Function MakeDWORD(Value as Long) As String
    Dim RET as String * 4
    Mid(RET, 1, 1) = Value And &HFF000000
    Mid(RET, 2, 1) = Value And &HFF0000
    Mid(RET, 3, 1) = Value And &HFF00
    Mid(RET, 4, 1) = Value And &HFF
    MakeDWORD = RET
End Function[/code]

that's setting values of longs into chars. for example, if the value is &H11111111, the first line will read Mid(RET, 1, 1) = &H11000000. at least add chr to convert it to string. make sure to shift it over several places
February 17, 2007, 3:08 PM
l2k-Shadow
FYI, neither one of those functions work.
February 17, 2007, 3:17 PM
dRAgoN
Frozen's will also overflow at random for no reason btw, can tell you this without even testing it.
You should never use a straight out value of 256 in that manor.
February 17, 2007, 3:25 PM
Ante
[quote author=l2k-Shadow link=topic=15573.msg164994#msg164994 date=1171725477]
FYI, neither one of those functions work.
[/quote]
does mine work if u add Makedword=strreverse(makedword) at the end of the function?
February 18, 2007, 5:24 PM
l2k-Shadow
[quote author=Ante link=topic=15573.msg165096#msg165096 date=1171819482]
[quote author=l2k-Shadow link=topic=15573.msg164994#msg164994 date=1171725477]
FYI, neither one of those functions work.
[/quote]
does mine work if u add Makedword=strreverse(makedword) at the end of the function?
[/quote]

lol... no it overflows on the first statement you can see it from the code.
February 18, 2007, 7:36 PM
JoeTheOdd
Oh yeah.. add Chr() to mine.

I'm so used to byte[] now. :P
February 18, 2007, 8:23 PM
l2k-Shadow
[quote author=Joe[x86] link=topic=15573.msg165117#msg165117 date=1171830197]
Oh yeah.. add Chr() to mine.

I'm so used to byte[] now. :P
[/quote]

No that won't work either, you are forgetting that all variables in VB except char are signed. You'd have to do something like this to overcome that barrier (this won't work in all cases such as VB will think that &HFFFF = &HFFFFFFFF because both produce same result, -1.. therefore you'd have to have multiple functions recognizing each one, so if you must not use RtlMoveMemory, you'd best go with the Chr$(eachbyte of the DWORD) function as postead earlier):
[code]
Function MakeDWORD(Value As Long) As String
Dim Outbuf As String, nLng As Long

    Outbuf = Chr$((Value And &HFF))
   
    nLng = (((Value And &HFF00) \ (2 ^ 8)) Mod 256)
    If nLng < 0 Then nLng = (nLng + 256)
    Outbuf = Outbuf & Chr$(nLng)
   
    nLng = (((Value And &HFF0000) \ (2 ^ 16)) Mod 256)
    If nLng < 0 Then nLng = (nLng + 256)
    Outbuf = Outbuf & Chr$(nLng)
   
    nLng = (((Value And &HFF000000) \ (2 ^ 24)) Mod 256)
    If nLng < 0 Then nLng = (nLng + 256)
    Outbuf = Outbuf & Chr$(nLng)
   
    MakeDWORD = Outbuf
End Function
[/code]
February 18, 2007, 10:24 PM
Ante
[quote author=l2k-Shadow link=topic=15573.msg165109#msg165109 date=1171827401]
[quote author=Ante link=topic=15573.msg165096#msg165096 date=1171819482]
[quote author=l2k-Shadow link=topic=15573.msg164994#msg164994 date=1171725477]
FYI, neither one of those functions work.
[/quote]
does mine work if u add Makedword=strreverse(makedword) at the end of the function?
[/quote]

lol... no it overflows on the first statement you can see it from the code.
[/quote]
did u mean the if value >= 0 then line? cuz theres no place for that to overflow
i can't imagine which other line it could overflow from...every chr() has a value within it that is positive and less than 256
and the negative values are bumped into positive with that line, and put back into "negative" with the last char
February 19, 2007, 2:24 AM
l2k-Shadow
[quote author=Ante link=topic=15573.msg165162#msg165162 date=1171851846]
[quote author=l2k-Shadow link=topic=15573.msg165109#msg165109 date=1171827401]
[quote author=Ante link=topic=15573.msg165096#msg165096 date=1171819482]
[quote author=l2k-Shadow link=topic=15573.msg164994#msg164994 date=1171725477]
FYI, neither one of those functions work.
[/quote]
does mine work if u add Makedword=strreverse(makedword) at the end of the function?
[/quote]

lol... no it overflows on the first statement you can see it from the code.
[/quote]
did u mean the if value >= 0 then line? cuz theres no place for that to overflow
i can't imagine which other line it could overflow from...every chr() has a value within it that is positive and less than 256
and the negative values are bumped into positive with that line, and put back into "negative" with the last char
[/quote]

no i mean the statement after value >=0 line. it's because VB does math operations using long variables.. and (256 ^ 4) is &HFFFFFFFF which is an unsigned long, however since longs in VB are signed, overflow occurs.
February 19, 2007, 2:49 AM
Ante
hm...
u coulda jus doen this then
[code]
Function Makedword(Value as long) as string
if value >= 0 then
makedword = makedword & chr(value mod 256) & chr((value mod (256^2)) \ (256^1)) & chr((value mod (256^3)) \ (256^2)) & chr(value \ (256^3)
else
value=value + 2147483648#
makedword = makedword & chr(value mod 256) & chr((value mod (256^2)) \ (256^1)) & chr((value mod (256^3)) \ (256^2)) & chr((value  \ (256^3))+128)
end if
End Function
[/code]
February 19, 2007, 3:40 PM
Imperceptus
[quote author=A2 link=topic=15573.msg164690#msg164690 date=1171336008]
[code][code]copy memory is really usefull, especially for filling vb types which can be build alot like cstructs

double = 8bytes
long = 4bytes
integer = 2bytes
byte = 1byte

to answer your question, without copymem that function looks like

[/code]function long2str(byval n as long) as string

  long2str = _
    chr((n and &HFF000000&) mod 256) & _
    chr((n and &H00FF0000&) mod 256) & _
    chr((n and &H0000FF00&) mod 256) & _
    chr(n and &H000000FF&)

end function[/code]

or backwards... dont know offhand, too lazy for testing!
[/quote]

omg this post was more then 3 months old when you replied to it. jeez.
February 19, 2007, 9:35 PM
l2k-Shadow
[quote author=Ante link=topic=15573.msg165195#msg165195 date=1171899602]
hm...
u coulda jus doen this then
[code]
Function Makedword(Value as long) as string
if value >= 0 then
makedword = makedword & chr(value mod 256) & chr((value mod (256^2)) \ (256^1)) & chr((value mod (256^3)) \ (256^2)) & chr(value \ (256^3)
else
value=value + 2147483648#
makedword = makedword & chr(value mod 256) & chr((value mod (256^2)) \ (256^1)) & chr((value mod (256^3)) \ (256^2)) & chr((value  \ (256^3))+128)
end if
End Function
[/code]
[/quote]

Then your function produces the same result as any MakeDWORD VB function.. you get the -1 problem where &HFFFF = &HFFFFFFFF and produces ˙˙˙˙ if you input &HFFFF or &HFFFFFFFF... :'(
February 20, 2007, 5:53 AM

Search