CopyMemory ( "RtlMoveMemory" )
Simple Long example
This would be one of the more typically seen function prototype ( VBA Declare line ) in literature, especially in the older well cited VB(A) literature and blogs.
Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef pDst As Any, ByRef pSrc As Any, ByVal ByteLen As Long)
The CopyMemory function is actually an Alias for the RtlMoveMemory api function, but the CopyMemory has been so widely used that it has even crept into official documentation
The Copy memory/RtlMoveMemory function does something very simple, but extremely useful for our experimenting: It copies Byte for Byte a memory segment from one place to another.
This is also very dangerous . We are playing with fire.
The simplest pseudo coding, or note to jog the memory of what the parameters are , for this is
_______________[Destination] , _____ [Source] ___ , [Number of bytes]
Slightly elaborated version:
___[ where to start** putting the copied bytes ] , [ where to **start copying Bytes from ] , [ Number of bytes to copy ]
** This will be the left most byte memory address, deep down in the hardware memory. Then any more than 1 byte being copied will be referring to the next bytes to the right from the source and destination, pseudo like, in simple layman terms, :
[ 1234567] [23456789] [2]
, will result in whatever is at the addresses 23456789 and 23456790 being copied and put at the addresses 1234567 and 1234568
Putting it again in another very pseudo layman way of thinking: Doing this,
[x y z] [a b c] [2]
, will end up with the stuff in the first bracket, ( the destination place ), getting changed to looking like
[a b z]
We will keep it simple for this introduction, and do some simple Long number and (just very limited###) String experiments in some of the later following posts to compliment the stuff done earlier
https://www.excelfox.com/forum/showt...ll=1#post17881
https://www.excelfox.com/forum/showt...ll=1#post11888
### We must proceed with great caution here: it is a bit of a chicken and egg situation: This CopyMemory function is very useful to help us delve into the trying to understand the complex situation of VB strings in the win32 api, but we run the danger of doing a lot of damage if we do not understand what is going on, but what is going on is what we want to trying and find out.
We can reduce the potential for damage to some extent, by tightening up the type in the declarations: You will note that the Type of the two memory locations required is Any. This is because the type is fairly irrelevant to the api, as it is only interested in pointer addresses.
It may be convenient for us to pass a variable such as a simple Long as the destination. The start position will be the first byte, but our variable will be initialised to having the next 3 bytes "there at that memory location" available, and at 0 value, so we have a "safe" free space for us to copy up to 4 Bytes to.
Simple Long examples, - 3 examples '_1 , _2 and _3 in the coding below
Although the following macro is fairly simple, there are still some important things to learn and to be aware of.
We use the RtlMoveMemory initially twice in its most basic commonly used form which is generally taken as the CopyMemory function . Throughout the entire coding for all three examples the destination is a long variable , LngDest ,which is initialised and so gives us a memory location inequitably looking like, and "their" available to us of:
00000000 00000000 00000000 00000000
In order to understand the results we need to appreciate that deep down in the CPU hardware innards, a number made up of Bytes, which is in most conventions taken as , for example
00000001 00000010 00000100 00001000
, would be actually held deep down in the computers hardware with the bytes** reversed, "back to front" as it were, so using that same example it would look like
00001000 00000100 00000010 00000001
** Very Important to note here is that it is the Bytes that are reversed, not the entire bits reversed
In the actual example in the coding below, our decimal number of 16777216 would have the conventional binary representation of
00000001 00000000 00000000 00000000 ( 16777216 = 2^24 )
So deep inside in the computer hardware memory it would have been stored as
00000000 00000000 00000000 00000001
_1 In that coding below, the first use of CopyMemory only takes the first 3 bytes from deep down in memory , and when that is put starting from where our LngDest starts, we end up there with
00000000 00000000 00000000 00000000
For any orientation that is inequitably for a Long type variable 0, which is the result we get.
_2 For the second use of the CopyMemory we copy all 4 bytes in their deep down "backward" orientation and so effectively the destination Long type variable looks in the correct deep down form
00000000 00000000 00000000 00000001
When we attempt to Debug.Print out this via Debug.Print LngDest, that is interpreted correctly as the value which would be from a conventionally looking binary number of
00000001 00000000 00000000 00000000 = decimal 16777216
The correct interpretation comes about as VBA knows all about how these things are stored deep down in memory.
ByVal and ByRef : Parameter Declareation
An Important point to note here already:
As ever, the Declareation parameters parts of ByVal and ByRef are instructions to VBA. They are used to get thiunbgs correct as the api needs them to do what we want.
The default ByRef in the common use of RtlMoveMemory ( the one we are using , conventionally named as CopyMemory ) is required so as to pass the pointer of the variable. This is what the api wants. A characteristic of api us that it is not enough to have a general rule about the use of ByVal and ByRef : We must think about every usage.
_3 In the final third usage I have used a less typical Declareation which I organised myself.
Private Declare Sub VBGetTarget Lib "kernel32" Alias "RtlMoveMemory" (ByRef pDst As Any, ByVal pSrc As Long, ByVal ByteLen As Long)
The purpose here was to supply a specific memory address myself of where to copy from ( the "source" ), and not rely on the ByRef giving the start address , ( the address of the first of 4 bytes ), of the Long variable, LngSource . I have to then be careful and change the ByRef to ByVal as I intend in the Call of the api function, named VBGetTarget, to pass the address. (If I continue to use the ByRef the results may be somewhat unpredictable, at least to most people on this Earth, - possibly for example there may be some attempt to get the address of where that pointer number itself is held, maybe somewhere associated with the COFF symbol table. As ever, with this api experimenting we need to be much more careful about trying things out as we would with higher level coding: if we are not sure, then best for now is not to try and see what happens. We need to tread carefully as some of this is pioneering work!
Once we have an address such as the start ( first byte) of the Long variable, LngSource, ( done by VarPtr(LngSource) in the main coding), we can then very easily get the next Byte addresses along by simply adding, 1 or 2 or 3 and so on. This is because the bytes in this and similar variable memory location situations are held at sequential addresses, - in a line as it were: For example , when I ran this coding, the address I got for the start ( first byte) of the Long variable, LngSource was 1962380 ,
https://i.postimg.cc/8CqM61F5/First-...ss-1962380.jpg
, ( you would get a different address , just as I would at a different time or different computer, etc. )
In my case I therefore know that my 4 bytes are "in a row " of bytes with these addresses
1962380 1962380+1=1962381 1962380+2=1962382 1962380+3=1962383
So , as an experiment, in this third example I take the last byte as it is deep in memory. Because of the "back to front" byte way of deep down storage, I get the byte looking like 00000001. This now gets put at the start Byte address of the LngDest variable. So effectively this originally initialised to 0 of
00000000 00000000 00000000 00000000
, gets changed to
00000001 00000000 00000000 00000000
Remember that this is still deep down in memory in the "back to front" byte way of deep down storage. Consequently when VBA is asked to Debug.Print LngDest, it recognises/ knows that in normal school maths "Right way around" we would be looking at
00000000 00000000 00000000 00000001
, which in decimal is the number 1 , the final result from the demo coding
Code:
Option Explicit
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef pDst As Any, ByRef pSrc As Any, ByVal ByteLen As Long) ' The most typical use of RtlMoveMemory has become known as the CopyMemory
Private Declare Sub VBGetTarget Lib "kernel32" Alias "RtlMoveMemory" (ByRef pDst As Any, ByVal pSrc As Long, ByVal ByteLen As Long)
Sub LongLE()
Dim LngSource As Long
' "Normal" maths Binary 00000001 00000000 00000000 00000000 - if this is "normal typical conventional everyday school maths" binary then its decimal number is 2^24=16777216
Let LngSource = 16777216 ' 2^24=16777216
' In memory,16777216 is 00000000 00000000 00000000 00000001 , - Byte order "back to front" as it were
'_1 CopyMemory missing last Byte of a Long in memory
Dim LngDest As Long ' 00000000 00000000 00000000 00000000
CopyMemory LngDest, LngSource, 3
Debug.Print LngDest ' 0
'_2 Copymemory of all 4 Bytes of a Long in memory
Let LngDest = 0 ' 00000000 00000000 00000000 00000000
CopyMemory LngDest, LngSource, 4
Debug.Print LngDest ' 16777216
'_3 Just take the furthest right byte from deep down in memory
Let LngDest = 0 ' 00000000 00000000 00000000 00000000
VBGetTarget LngDest, VarPtr(LngSource) + 3, 1 ' 00000001
Debug.Print LngDest ' 1
End Sub