Results 1 to 10 of 555

Thread: Tests Copying, Pasting, API Cliipboard issues. and Rough notes on Advanced API stuff

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Fuhrer, Vierte Reich DocAElstein's Avatar
    Join Date
    Aug 2014
    Posts
    9,468
    Rep Power
    10
    VBA and VB variable addresses and pointers.

    This does not follow the classical listing and brief description of variable types. (That can for example been found here https://www.excelfox.com/forum/showt...ll=1#post23882 )
    We are more concerned here with how the variable is held in memory, and the concepts centred around the pointer idea
    Pointer/ Variable object constants.
    Pointer

    Let us say first the very very basic simplest description of a computer pointer, ( hoping that no professional computer exert reads it and then want to kill me for my naivety): A pointer can be thought of as a variable , ( which itself is a computer memory chunk held somewhere somehow) and that itself contains not the final value, but some information allowing you to get at the actual value.
    This pointer idea seems a strange concept initially, but it grows on you, and after a bit of thinking makes some sense :
    A simple idea of a variable holding a value or even of it directly giving you the address / location is a bit of a hap hazard not particularly ordered way if you think about it. A simplified idea of the variable saying where or naming the memory place of a value is, is an OK idea to explain you storing some things in a shoe box, but it is unlikely to be an efficient way to organise something that is not a human brain but a mesh of simple 0s and 1s

    Variable object constants
    The ordered / efficient way involves processes requiring a very high low level computer knowledge. We might attribute some things to a variable, so we are in the realms of vague Object Orientated Programming property concepts and so, maybe not surprisingly, we end up calling a variable an object, although what the object is blurred often and we might call the pointer the object, whereas the object might be regarded of the pointer class. Here is a good one I just thought of: What a variable might be considered to actually be is an object of a pointer class , or the word pointer might be considered the name of the object , that being the object we perhaps do not name or refer to so well by use of the word variable. The word variable is more like a convenient term of reference to relate something to a value being , or a value to be, held. But this misleads a bit since the over simplified idea of the variable saying where or naming the memory place of a value is an OK idea to explain you storing some things in a shoe box, but it is totally inappropriate to the more sane and efficiently organised ides starting at the COFF symbol table and extending into the pointer(s)
    These object ideas come about as we end up attributing things to them so then they have properties

    I will use the examples of a Long and a String
    Starting at the bottom with a simple variable, Long , as it then leads on nicely to the slightly more complex String

    ( A Tool, VarPtr
    For the time being we will just accept that this is a function, ( ** which coincidently has no documentation ), which gives us the pointer value, (the address it is intended to "point" to), or the number value actually held at that address. ( ** My best guess is that it is some API function that is made available to us in VB(A) without needing a Declareing
    ) )

    Long
    Skematic VBA Long.jpg
    Code:
    Sub  VBALongTypeMemoryStuff()
    1 Dim Lng As Long
    2 Debug.Print VarPtr(Lng)        '  2355700    I don't know where this number is held in memory. I don't care
    3 Debug.Print VarPtr(ByVal Lng)  '  0
    4 Debug.Print Lng                '  0
     
     Let Lng = 2
    
    5 Debug.Print VarPtr(Lng)          '  2355700
    6 Debug.Print VarPtr(ByVal Lng)    '  2
    
    8 Debug.Print Lng                  '  2
    End Sub
    (** Pointer values are not unique, neither across different computers or in the same computer and software at different times. The method used to allocate them may result coincidentally occasionally in getting the same number for a short time period, for re runs of the same simple coding on the same computer )
    Starting at the very bottom, and from after line 1 of the coding, a number, that usually referred to pointer , will be stored somehow, somewhere in the computer. The actual location of this requires an in depth low level knowledge of which is perhaps really too complicated and of much too little importance for us to consider. Surfice to say it goes by the name of Common Object File Format ( COFF ) symbol table, and could perhaps be by a layman be regarded as some sort of stack or shelf arrangement populated by some complex rules allowing software to access as necessary. Our interest starts as what is "in" this , and perhaps some knowledge of its size/ construction would not go a miss.
    It is a number which is made with 4 bytes, (32 bits). It is called a Pointer, often. The actual number refers to the first memory address ( the first memory address at the left hand side) of 4 Bytes (32 bits) set aside in memory to hold the final value which I later assign to the variable, (with Let Lng = 2 ). In other words, the act of doing Dim Lng As Long reserves for me 4 sequential memory addresses/ byte locations, in a sequential row as it were. And the first one, the first byte, at the left as it were, has the memory location got for me with the second code line, Debug.Print VarPtr(Lng) ( by me , I got the value shown in the ' comment - 2355700** )
    The third line, Debug.Print VarPtr(ByVal Lng) , is perhaps giving me the same as the 4th line, which is the value of the variable. For the case of a Long Type it does have a value even before I assign one. It has the value 0. If I assigned it 0 with Let Lng = 0 nothing would change anywhere, (before I do Let Lng = 2 ). What I would have possibly done is changed the value of 0 with 0
    In line 5 , after, Let Lng = 2 , the memory location , 2355700, has not changed, as it never does for the VBA Long type, even if I assign a much different number. This is because It doesn’t need to change, because 32 bits are enough to hold a binary value of any number in the range of the defined number range for a VBA Long type
    Line 6 and line 8: My results suggest that Debug.Print VarPtr(ByVal Lng) and Debug.Print Lng are giving me the same thing – the value it sees at the address, 2355700**.
    ( **The address I got was 2355700 , you will get a similar but different number. )
    In this situation what is actually the "Pointer" is a slightly vague concept you would use to refer to one or more of those things or all of them depending on the context in which you use it




    String experiments in the next post.
    Last edited by DocAElstein; 01-25-2025 at 03:47 PM.

  2. #2
    Fuhrer, Vierte Reich DocAElstein's Avatar
    Join Date
    Aug 2014
    Posts
    9,468
    Rep Power
    10
    This is page 3 - https://www.excelfox.com/forum/showt...PI-stuff/page3
    This is post
    https://www.excelfox.com/forum/showt...age3#post17882




    Tools and things for further detailed investigations and experiments

    This page introduces some more tools for more detailed investigations of VB strings, but it merges into more detailed experiments.

    One important central phenomena is that the unit of a Byte being a fundamental single unit, often mean that strings and 1 dimensional byte type arrays tend to equate sometimes to the same or similar things. Manipulating these Bytes directly and/ or messing with them with some api functions gives some good insights into the subject of VB(A) strings in win32 api
    Last edited by DocAElstein; 02-09-2025 at 12:06 AM.

  3. #3
    Fuhrer, Vierte Reich DocAElstein's Avatar
    Join Date
    Aug 2014
    Posts
    9,468
    Rep Power
    10
    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
    Last edited by DocAElstein; 02-15-2025 at 01:33 PM.

  4. #4
    Fuhrer, Vierte Reich DocAElstein's Avatar
    Join Date
    Aug 2014
    Posts
    9,468
    Rep Power
    10
    In this post are some very simple codings that may be helpful in conjunction with some of the api codings later and some reviews of some important points that may be helpful in conjunction with some of the slightly more advanced api codings later


    Simple 1 dimensional array to convenient Debug.Print
    This very simply Function DBugPrntArr( ) makes a Debug.Print of the elements of a 1 dimensional array.
    Here with a test example calling coding from here, https://eileenslounge.com/viewtopic....323516#p323516
    https://www.excelfox.com/forum/showt...ll=1#post21746

    Code:
    
    Public Function DBugPrntArr(ByVal Arr As Variant) As Variant
    'ReDim DBugPrntArr(LBound(Arr) To UBound(Arr))
    Dim Var As Variant: ReDim Var(LBound(Arr) To UBound(Arr))
    Dim Eye As Long, strOut As String
        For Eye = LBound(Arr) To UBound(Arr)
         Let Var(Eye) = Arr(Eye)
         Let strOut = strOut & Arr(Eye) & ", "
        Next Eye
     Let strOut = "{" & Left(strOut, Len(strOut) - 2) & "}" '    Left(strOut, Len(strOut - 2))  is  Take off last  comma and space
    Debug.Print strOut
    'Stop ' Check watch window on var    '
     Let DBugPrntArr = Var
    End Function
    
    
    ' Example to test 
    Sub arrChrs() ' https://eileenslounge.com/viewtopic.php?p=323516#p323516   https://www.excelfox.com/forum/showthread.php/2872-Appendix-Thread-App-Index-Rws()-Clms()-Majic-code-line-Codings-for-other-Threads-Tables-etc)-TEST-COPY?p=21746&viewfull=1#post21746
    ' 1b) test string example
    Dim ZAC As String
     Let ZAC = "ZAC" ' This is a demo example text string
    Rem 2 String to array
    Dim UniCrud As String: Let UniCrud = StrConv(ZAC, Conversion:=vbUnicode)            '  "Z" & vbNullChar & "A" & vbNullChar & "C" & vbNullChar
     Let UniCrud = Left(UniCrud, Len(UniCrud) - 1)                                      '  "Z" & vbNullChar & "A" & vbNullChar & "C"
    Dim Letas() As String: Let Letas() = Split(UniCrud, vbNullChar)                     '  { "Z" , "A" , "C"  }
    Call DBugPrntArr(Letas())
    End Sub
    
    
    The coding is principally just as a development aid to give a convenient visual output and one that can be copied easily , mostly for numbers, so characters, even if they are text, are not in the typically text required enclosed " " pair. In this example, for example, we get in the Immediate window
    {Z, A, C}







    Another useful similar function: ' This function assumes you have a 1 dimensional to fill from, and the array you fill to is a one dimensional array of the same first element indicie and that the array to fill is the same size or bigger
    Code:
    '  This function assumes you have a 1 dimensional to fill from, and the array you fill to is a one dimensional array of the same first element indicie and that the array to fill is the same size or bigger
    Public Function AddBytesToArray(ByVal arrTo As Variant, arrFrom As Variant) As Variant
    Dim Cnt As Long
        For Cnt = 0 To UBound(arrFrom)
         Let arrTo(Cnt) = arrFrom(Cnt)
        Next Cnt
     Let AddBytesToArray = arrTo
    End Function
    




    Microsoft Unicode encoding UTF-16 LE
    This is a quick review of how deep in memory windows typically holds text characters in a number code. The word code here meaning the sequence of 1 and 0 digits which represent any text character, (aka in the jargon, the encoding)
    As example I choose a two character string.
    The first character is capital A, which in almost all computer systems and conventions is assigned the capital number of 65
    The second character is chosen as it has a to help show up the important characteristics of the UTF-16 LE Unicode encoding used by Microsoft.
    Further more it happens to be a character that is both
    _ assigned a decimal number (code point ) in Unicode (as almost all characters and everything in the world is, or will be eventually),
    , but also
    _ this character happens to be also assigned a decimal number (code point) in most of the windows code pages , ( which refer to characters code point in the range up to 255
    This character I use as the second character example looks like 3 small dots, but is in fact, a single character which just pictorially looks like 3 small dots.

    Just to demo that character, and how it looks compared to 3 normal dots , here are some different views of 5 characters comprising
    [ 3 dots ( 3 standard dot characters ) ] [ a space ] [ the single character that looks like 3 small dots ]
    ... …
    Code:
    12345
    ... …
    https://i.postimg.cc/MG5jBVKM/3-dots...n-in-Excel.jpg
    https://i.postimg.cc/yNsZWvBC/3-dots...-VB-Editor.jpg
    https://i.postimg.cc/vZWnQCJv/3-dots...ate-Window.jpg



    This simple coding gives us some outputs as discussed
    Code:
    '  https://eileenslounge.com/viewtopic.php?p=297502#p297502   https://eileenslounge.com/viewtopic.php?p=297500#p297500  https://eileenslounge.com/viewtopic.php?p=323085#p323085        https://www.excelfox.com/forum/showthread.php/2824-Tests-Copying-Pasting-API-Cliipboard-issues-and-Rough-notes-on-Advanced-API-stuff?p=17883&viewfull=1#post17883
    Sub WUnicodeAASIUTF16LE()
    Debug.Print "... …" ' Here are 5 characters, 3 standard dots, a space and then the character which looks like 3 small dots
     Let Range("A1") = "... …"
     Let Range("A2") = "A" & Chr(133): Debug.Print Asc(Right(Range("A1").Value, 1)), AscW(Right(Range("A1").Value, 1))
     Let Range("A3") = "A" & ChrW(8230): Debug.Print Asc(Right(Range("A2").Value, 1)), AscW(Right(Range("A2").Value, 1))
    End Sub
    An additional reason why I use this particular character, is that we avoid a typical awkward problem: We have the problem usually in the VB Editor / Immediate Window etc., when investigating Unicode characters, that the VB Editor / Immediate Window does not support most Unicode characters. This means we get an annoying ? or some incorrect character shown if we try to display it. However, if as in this character, , the character does also appear in the code page, then usually the VB Editor / Immediate Window does show correctly the character, as seen in the last two screen shots

    OK, so now we investigate how Microsoft holds those two characters: Follow careful these steps/ explanations:
    _ The number representations are themselves wrapped inside
    __ 4 bytes at the start holding the string length,
    __ and two Bytes at the end both set at 0, ( which are together known as / representing the vbNullChar or or ChrW(0) , but this is not the number 0 which is almost always code point 48, Chr(48) , ChrW(48) )
    We are less concerned here with those start and end Bytes, - rather we are interested principally here on the Bytes representing the characters, or rather how we represent the numbers, (decimal code points ) assigned to the characters
    _ We need the decimal code points (decimal numbers) :
    __ For the character capital A it is 65 ;
    __For demonstration purposes I will choose the Unicode number for which is 8230
    _ Each of the two Bytes is 8 bits, and each bit can be 0 or 1 – so in other words, 8 bit Binary or B bit base 2, or 8 digit binary or 8 digit base 2
    The maximum number possible for each Byte will therefore be the binary
    11111111
    which is
    2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0
    = 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1
    = 255
    What we do is use the two Byte pieces, like a 2 digit base 256 number system that is "the wrong way around", or the other way around to what we may be more familiar with the base 2 (binary ) number system, whose first two bits or pieces are
    2^1 2^0
    0-1 0-1
    , so we are looking at a system using two pieces (Bytes) like this
    256^0 256^1
    0-255 0-255
    In this system, a number up to 255 is easy to see the representation, The total value is
    low-end + 256 x high-end .

    So for our A for example we have
    65 0

    For our 8230 it needs a bit more maths: The total value is again low-end + 256 x high-end.
    8230 can be written as 256 * 32 + 38
    The high-end bye is 8230 \ 256 = 32
    The low-end byte is 8230 Mod 256 = 38
    So Windows writes it as
    38 32

    Here a short coding for those two characters and another , e , whose decimal code point is mist usually 101. The coding uses the function discussed at the start of this post and the main Calling coding uses the Byte type (array) ideas of the previous post
    Code:
    Sub ByteArray()
    Dim Harry() As Byte '
     Let Harry() = "A" & ChrW(8230) & "e" '  https://eileenslounge.com/viewtopic.php?p=297329#p297329
    Call DBugPrntArr(Harry())   '      {65, 0, 38, 32, 101, 0}
    End Sub
    Last edited by DocAElstein; 02-15-2025 at 01:32 PM.

Similar Threads

  1. Replies: 21
    Last Post: 12-15-2024, 07:13 PM
  2. Replies: 114
    Last Post: 03-04-2024, 02:39 PM
  3. Replies: 42
    Last Post: 05-29-2023, 01:19 PM
  4. Some Date Notes and Tests
    By DocAElstein in forum Test Area
    Replies: 0
    Last Post: 11-23-2021, 10:40 PM
  5. Replies: 11
    Last Post: 10-13-2013, 10:53 PM

Posting Permissions

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