Page 3 of 57 FirstFirst 123451353 ... LastLast
Results 21 to 30 of 565

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

  1. #21
    Fuhrer, Vierte Reich DocAElstein's Avatar
    Join Date
    Aug 2014
    Posts
    9,521
    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.

  2. #22
    Fuhrer, Vierte Reich DocAElstein's Avatar
    Join Date
    Aug 2014
    Posts
    9,521
    Rep Power
    10

    I am thinking/ guessing that some of the workings being examined here may be involved in the workings that cause the issues/ problems that are the subject of this page 2
    I am thinking there seems to be a some similarity in the back end workings / storings of the ANSI and Unicode Byte arrays suggested and a string itself, a sort of 1 to 1 matching to Byte and character, which we are exposing.
    For learning and explaining convenience, 3, -4 things could be considered,
    _ Hacks/ possibilities when using a declarations and assignments involving Byte() type arrays. This seems to be slightly more useful than the next two, even though this is not a specific function but arises due to the 1 to 1 matching of Byte and character idea
    _ the StrConv(string, conversion:= vbFromUnicode, LCID)
    _ the StrConv(string, conversion:= vbUnicode, LCID)
    (_ Tricks involving user defined Types and the LSet statement. These mainly give us a simpler alternative to using some api functions )

    Here are some coding experiments, remembering a few important things and/or some gut feelings:
    _ that the 32 bit pointer in the variable BSTR, points to the beginning of the character array, not to the 4-byte length field that precedes the array.
    _ The strConv is a fairly simple ignorant thing. I am thinking that argument names have some meaning: That does occasionally happen with Microsoft, especially for older things such as this function
    _ I am thinking that the strConv() function is a bit simple / ignorant, something like a cross wire or short circuit allowing, amongst other things, a conversion between ANSI and Unicode to work depending on how we organise things. Think of it as a crude junction between two transport mechanisms. One that is made by simply arranging that they come together. Something else will usually need to be done to make the transfer work. When using a declarations and assignments involving Byte() type arrays, perhaps some similar processes go on.
    Regardless of if any of those ideas are any good, they may match or help to remember the important useful results, so let’s get on with looking at those:

    Rem 2 "Unicode To ANSI"
    According to the documentation …
    vbFromUnicode ' Converts the string from Unicode to the default code page of the system.
    This sounds not so inaccurate considering the results. At least initially: A typical result from something like
    Dim ByteArr() As Byte
    ByteArr() = StrConv("Alan", vbFromUnicode)

    , suggests perhaps that an array of Bytes separated by a vbNullChar is looked for, after which in such a case it may , deep down see something that (in its decimal interpretation), would look like
    65 0 108 0 97 0 110 0 or perhaps interpreted as an array {65, 0, 108, 0, 97, 0, 110, 0}
    , so a reasonable return could be considered as what we do indeed get ,
    {65, 108, 97, 110}
    ( I am not sure why a decimal code point is given, perhaps that is just the convention of VBA and the VB editor. But it is what we get: https://i.postimg.cc/fytpYm4V/Byte-Array.jpg )
    However, it seems quite easy to trick the StrConv(string, conversion:= vbFromUnicode, LCID) In ' 2d we feed it a string "A" & vbNullChar & "E" & vbNullChar and perhaps it somehow sees a sort of character set like deep inside which in some form or another is pseudo like [65 0] & vbNullChar & [69 0] & vbNullChar or [65 vbNullChar] & vbNullChar & [69 vbNullChar] & vbNullChar. So maybe it interprets that as an array of two things that have a non used byte (vbNullChar ) separating them which is how a "ANSI" looks, and so removes the separating vbNullChar, but VB and VBA now see 65 0 69 0, and this is recognised as a valid Unicode UTF-16 2 Byte string representation of AE

    Rem 1 … Unicode? ToUnicode?
    We consider here the StrConv(string, conversion:= vbUnicode, LCID) , a slightly rather curious thing, considering …we note that the documentation suggests some sort of opposite of the previous example, VbUnicode ' Converts the string to Unicode using the default code page of the system , whereas the argument name is not quite the opposite, since the opposite would suggest ToVbUnicode, which it isn't.
    Based on the discussions so far, we might have expected that feeding it something like "Alan" would return us something that perhaps could be got in a Byte array looking something like this.
    {65, 0, 108, 0, 97, 0, 110, 0}
    It does not do that. That would be showing how VB holds "Alan" in memory. ( Rem 0 seems to do that !!!)
    However we can’t seem to get that directly, and the results suggest it does nothing more than add a vbNullChar to each character.
    However interesting is the result in ' 1b which appears to be doing some thing like we expected, but doing on the modified string coming from the initial StrConv(BSTR, vbUnicode). This appears to come from a string assignment to the Byte array ' ### !!!

    ' 1d We can only get some opposite idea to conversion:= vbFromUnicode if we apply conversion:= vbUnicode to what we think , based on Rem 2, has been converted to ANSI, from like StrConv("Alan", vbFromUnicode)
    Let vTemp = StrConv(StrConv(BSTR, vbFromUnicode), vbUnicode) ' "Alan"
    Let ByteArr() = StrConv(StrConv(BSTR, vbFromUnicode), vbUnicode) ' 65 0 108 0 97 0 110 0


    ( ' 1e Before leaving this Rem 1 section, we note, the curious behaviour does give us a useful one line code line to get the characters of a text sting to a 1 dimensional array of those characters, which can help get other interesting approaches to solutions , example https://eileenslounge.com/viewtopic....323516#p323516 https://www.excelfox.com/forum/showt...ge53#post22623 )


    Rem 0 !!! ' ###
    Working backwards from the last two sections, it appears that the assignment of a text string to a Byte array shows us how a VB / VBA string is actually represented internally. It could be regarded as converting from the string to a representation of the Unicode encoding, specifically here, Microsoft’s UTF-16 2 byte LE Unicode encoding.
    ' 0b It is perhaps important here to do an example which includes a large code point.
    Example in the second half and towards the end of the over next post





    Full coding is here
    https://www.excelfox.com/forum/showt...ll=1#post24938
    https://www.excelfox.com/forum/showt...ge19#post24938
    ( Also in code module MikeWombatstrConv in uploaded file )


    Ref
    https://www.eileenslounge.com/viewto...297326#p297326 https://www.eileenslounge.com/viewto...297329#p297329
    https://eileenslounge.com/viewtopic....297332#p297332
    https://eileenslounge.com/viewtopic....297500#p297500
    https://eileenslounge.com/viewtopic....323085#p323085
    Attached Files Attached Files
    Last edited by DocAElstein; 02-20-2025 at 04:03 PM.

  3. #23
    Fuhrer, Vierte Reich DocAElstein's Avatar
    Join Date
    Aug 2014
    Posts
    9,521
    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. #24
    Fuhrer, Vierte Reich DocAElstein's Avatar
    Join Date
    Aug 2014
    Posts
    9,521
    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 Byte type (Array) post above and at the start of the next 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-20-2025 at 04:12 PM.

  5. #25
    Fuhrer, Vierte Reich DocAElstein's Avatar
    Join Date
    Aug 2014
    Posts
    9,521
    Rep Power
    10
    Byte type (Array) and CopyMemory

    As a prelude to moving on finally to the main reason for a lot of this Thread, (VB strings in the win32 api), a review of the connection to Byte type arrays and strings and an introduction to byte type arrays in the CopyMemory function is worthwhile.

    1 to 1 Byte to string character phenomena, arrByte()="xyz"
    This "phenomena" has crept up a few time in previous discussions, and it is quite useful to us. Briefly, this "phenomena" that we observe is that things like the following do not error, but rather seem to give us a simple way to go convert from a string of characters directly to the Unicode byte array of code point numbers as represented by the Microsoft Unicode Encoding of UTF-16 LE*
    Dim Harry() As Byte, pBSTR As String
    Let pBSTR = "A" & "…" & "e"
    Let Harry() = pBSTR
    Let Harry() = "A" & "…" & "e"

    Debug.Print Harry()

    Here below a few actual working demo codings in Rem 1
    Code:
    Sub BitesArray() '   https://www.excelfox.com/forum/showthread.php/2824-Tests-Copying-Pasting-API-Cliipboard-issues-and-Rough-notes-on-Advanced-API-stuff?p=17885&viewfull=1#post17885
    Rem 1    1 to 1 Byte to string character phenomena, arrByte()="xyz"
    Dim Harry() As Byte, pBSTR As String, LngSrc As Long, vTemp As Variant
     Let LngSrc = 1
    ' Let Harry() = LngSrc ' Compile error  -  No assignment to data field possible
     Let pBSTR = "A" & "…" & "e"  '  Three characters, ( the middle one is Unicode ChrW(8230) and also it is in most Window Code Pages also AASI Chr(133)
     Let Harry() = pBSTR
     Let vTemp = Harry()          ' This remains as an array
     Let vTemp = CStr(Harry())    ' This gives the 3 character string      A…e        https://i.postimg.cc/XYvP7Wyh/CStr-A-e.jpg    https://postimg.cc/CzW7mW7H
     Debug.Print Harry()          '   A…e
    
    Call DBugPrntArr(Harry())     '  {65, 0, 38, 32, 101, 0}  -  see  https://www.excelfox.com/forum/showthread.php/2824/page3#post17883
    
    ' Let vTemp = Len(Harry())    ' oompile error  -  variable required - assignment to this expression not possible
    
    Rem 2 VBA seems to know when my string ends, and its not at a null character
    Dim ByteArray(0 To 11) As Byte, arrByte As Variant
     Let arrByte = AddBytesToArray(ByteArray(), Harry())
    Call DBugPrntArr(arrByte)    '   {65, 0, 38, 32, 101, 0, 0, 0, 0, 0, 0}
    Debug.Print arrByte          '   A…e
    'Call WtchaGot_Unic_NotMuchIfYaChoppedItOff(arrByte) ' "A" & ChrW(8230) & "e" & Chr(0) & Chr(0) & Chr(0)
     Let arrByte(10) = 65
    Debug.Print arrByte
    'Call WtchaGot_Unic_NotMuchIfYaChoppedItOff(arrByte) ' "A" & ChrW(8230) & "e" & Chr(0) & Chr(0) & "A"
    End Sub
    
    Code lines like that seem to allow us to either fill a byte type array by simply assigning a string of text characters to the array, or conversely print out a string by using that array of numbers , ( *the Unicode byte array of code point numbers as represented by the Microsoft Unicode Encoding of UTF-16 LE ) , where we might more typically require to pass the string of text characters
    So there seems to be some simple equivalent , something like
    _____" A…e" < ------- > {65, 0, 38, 32, 101, 0}
    In most situations something similar to this, that is to say putting an array to a string somehow, would almost always cause an error. That sounds reasonable, - even if knowing nothing about computers , common sense tells us that a string of text and an array of numbers are different things that are unlikely usually to "fit" together. But in this particular case it "works", or rather it seems that what we would like to have done gets done somehow…. Or maybe it just happens as expected if we look in more detail at what is going on behind the scenes.
    So what is going on
    We could put it down to one of, or a combination of three things.
    _1 We sometimes talk about coerce or coerceing and suggest it is when we do something that cause something to happen, and this can partly be part of _2
    _2 VBA is often set to do some things in situations where it may not have all the correct full explicit syntax.
    _3 In this particular case, it might just be that the two things are the same or very similar: a VB(A) string is just a chunk of sequential memory containing the bytes representing the characters in the string, at least in the middle section of the full memory place holding it. Similarly, or in some ways exactly the same, an array of bytes ... is just a chunk of sequential memory containing bytes. In other words they are pretty much the same thing, just a different context. When we try to obtain or print out a string in VBA, we know we are "pointed" to the start of this character array. It is perhaps reasonable to assume that when we "point" to a byte array then we are doing the same thing, - going the start of the array. At this point what is seen deep down is indeed the same sort t of thing. VBA is told to give us the character string in the context something wanting a string, whereas in the context of an array, it is just an array of numbers, a "field" of values, of the appropriate type which we may apply to a dynamic array directly, just as we may be more familiar with things like
    Dim arrRng() As Variant: Let arrRng() = Range("A1:C1").Value
    Dim arrStr() As String: Let arrStr() = Split("a b", " ")



    What use is this / significance to our current discussions.
    The last bit above tells us that the above possibilities are likely at some point to be useful in any playing around with strings. And that above was by way of reminding revising the "phenomena". So having the abilities to do some convenient things with Byte arrays, leads us on to, that is to say compliments nicely the following. As far as experiments with RtlMoveMemory are concerned, we were passing bytes around, and for the first two parameters , that is to say the locations to copy from or to, we used actual variables. In the simple case of a long things were fairly "safe" and understandable. However as we precede to more complicated variables, copying and pasting bytes into existing variables could be dangerous – for example perhaps putting bytes representing a number where they may have been representing a string or a pointer etc. might get messy. It is therefore perhaps worth considering, for example assigning a Byte array and use that to copy to. We can then consider that array examining that array then in VBA explicitly, rather then looking at what happens when we use a filed variable, as we did in the last long examples.

    _.____________

    As a simple example, we repeat the last long experiment but pass the copied bytes to a byte array.
    In the coding below, in Rem 1, we repeat the section '_2 CopyMemory of all 4 Bytes of a Long in memory The only difference in this coding below is that the first element of a byte array is used for the destination.
    I use the same number example, and we on examining the final filled byte array, we see once again from the results that the byte order in memory is in the "back to front" way

    Rem 2 is a bit of playing around for fun: I pick the number 65 for the source long variable. In normal maths, with 4 bytes (32 bits) , we would have this,
    00000000 00000000 00000000 01000001
    , but deep in memory it is
    01000001 00000000 00000000 00000000
    So when we copy these 4 bytes in the order that they are , we will have in our Byte array
    65 0 0 0
    (In fact we used a larger byte array, so the full byte array looks finally like
    65 0 0 0 0 0 0 0 0 0 0 0 )
    When we do the Debug.Print ByteArray() trick of putting a byte array where a string is expected, we will get printed out an A character, ( along with 5 Chr(0) characters which typically we do not see )
    Code:
    Sub LongLEwithBites()
    Dim LngSource As Long
    Rem 1
    ' "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
    
    Dim ByteArray(0 To 11) As Byte
    CopyMemory ByteArray(0), LngSource, 4
    Call DBugPrntArr(ByteArray()) ' {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}
    
    Rem 2 "make a string using a long"
    ' ' "Normal" maths Binary 00000000 00000000 00000000 01000001
     Let LngSource = 65    '     0        0        0        65
    ' In memory,     65  is   01000001 00000000 00000000 00000000 ,  - Byte order "back to front" as it wereCopyMemory ByteArray(0), LngSource, 4
    '                            65       0        0         0
    Call DBugPrntArr(ByteArray()) ' {65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
    
    Debug.Print ByteArray()                           '      65  0   0  0    0  0     0   0     0  0     0  0
    'Call WtchaGot_Unic_NotMuchIfYaChoppedItOff(ByteArray()) ' "A" & Chr(0) & Chr(0) & Chr(0) & Chr(0) & Chr(0)
    End Sub
    Attached Images Attached Images
    Last edited by DocAElstein; 02-15-2025 at 01:31 PM.

  6. #26
    Fuhrer, Vierte Reich DocAElstein's Avatar
    Join Date
    Aug 2014
    Posts
    9,521
    Rep Power
    10
    String Long And Bytes RtlMoveMemory

    This follows on from the last posts, using similar coding, and only "touches" at the edge strings, because we want to remain cautious and develop slowly coding that experiments with strings and the win32 API
    We will use the 3 character string example already discussed in a few places previously
    A…e
    We will put the copied bytes into a byte array, and to be on the safe side for now we will use our second version of the RtlMoveMemory which takes a pointer of the source memory location. That way we will avoid a direct use of a string variable.
    Because we want to be careful initially we will consider the string as we are fairly sure that it appears in memory:
    https://i.postimg.cc/RhmXzjq7/6A-e0.jpg
    6A…e0.jpg

    We are fairly sure of the 12 byte total length structure, and that the pointer that we use is going to the start of the character array. So we will make the byte array 12 bytes, and pass the memory location of 4 less than that given by StrPtr( )
    Code:
    Option Explicit
     #If VBA7 Then
      Private Declare PtrSafe Sub VBGetTarget Lib "kernel32" Alias "RtlMoveMemory" (ByRef  Target As Byte, ByVal lPointer As LongPtr, ByVal cbCopy As LongPtr)
     #Else
      Private Declare Sub  VBGetTarget Lib "kernel32" Alias "RtlMoveMemory" (ByRef Target As Byte, ByVal lPointer As Long, ByVal cbCopy As Long)
     #End If
    Sub StringLongsCopyMemory()        '  https://www.excelfox.com/forum/showthread.php/2824-Tests-Copying-Pasting-API-Cliipboard-issues-and-Rough-notes-on-Advanced-API-stuff?p=17885&viewfull=1#post17885
    Dim strBSTR As String
     Let strBSTR = "A" & "…" & "e"     '  Three characters, ( the middle one is Unicode ChrW(8230) and also it is in most Window Code Pages also AASI Chr(133)    https://i.postimg.cc/jjpq36WF/6A-e0.jpg
    
    Dim ByteArray(0 To 11) As  Byte      ' 4 bytes for length indicator, then 6 bytes for 3 characters then the ladt two bytes fir the trailing  Null Character
    VBGetTarget ByteArray(0), StrPtr(strBSTR) - 4, 12 '  The  StrPtr  takes us to the start of the character array, so  -4  will takes us to the start of the  12  bytes that we are interested in
    Call DBugPrntArr(ByteArray())      ' {6, 0, 0, 0, 65, 0, 38, 32, 101, 0, 0, 0}
    End Sub
    The final results
    ______________ {6, 0, 0, 0, 65, 0, 38, 32, 101, 0, 0, 0}
    , tie up very well with our prediction.
    Note that the 4 byte 32 bit length indicator held in memory is the byte length of the character array, and the order follows the same "back to front byte order" as we saw for the long variable. In other words, the 32 digit binary that normal school maths and most other conventional things for the value of 6 would look like this
    00000000 00000000 00000000 00000110 = decimal 6
    , but in the "back to front byte order" as held in memory, we have
    00000110 00000000 00000000 00000000
    ( As ever, remember that it is the 4 bytes that are shuffled/ reordered, it is not the entire bits in reverse order )
    Last edited by DocAElstein; 02-15-2025 at 01:31 PM.

  7. #27
    Fuhrer, Vierte Reich DocAElstein's Avatar
    Join Date
    Aug 2014
    Posts
    9,521
    Rep Power
    10
    Trick involving user defined Typess and the LSet statement.
    The general Types structure
    This trick was passed on to me in a couple of places https://eileenslounge.com/viewtopic....323073#p323073
    https://eileenslounge.com/viewtopic....324977#p324977

    These mainly give us a simpler alternative to using some api functions, in particular the CopyMemory / "RtlMoveMemory"
    Originally when I looked at things like the LSet a few years ago, I noted myself that LSet worked on strings, as did many of the similar things, but for just the LSet I made the extra note, - …. and …. ** Some other possibilities to do with replacing variable types that I have not figured out yet. . I was not sure myself what I meant


    The General Structure
    I also saw an introduction to this idea in a Steven Roman book . That did not get as far as using the LSet, but it may have given an insight into how user-defined data types react sometimes. A suggestion was that when we react with a user-defined data types, we may react with the structure "seen". So a basic process and not one customised to suit a VB(A) known type.
    In the following short coding I take a look at the addresses (pointers), (held in the COFF symbol table) for some variables within a user defined type, and the length given for the variables as well as the total length obtained from the variables within, and then importantly the length obtained for the whole
    The following codings are some example of what I looked at. I also juggled around with the number of variables and position of these variables quite a bit to confirm my conclusions below
    Code:
    Option Explicit
    Private Type  StructStrLng
     Astring As String
     Bstring As String
     Gstring As String
     Lng1 As Long
     Lng2 As Long
    End Type
    Sub TypeStructureTests()
    Dim StructTest As StructStrLng
     Let StructTest.Astring = "More than a few characters" ' 26 characters here
     Let StructTest.Bstring = "More than a few characters" ' 26 characters here
    Debug.Print VarPtr(StructTest), VarPtr(ByVal StructTest) '                                                                            1504276       1504276
    Debug.Print Len(StructTest.Astring), LenB(StructTest.Astring), VarPtr(StructTest.Astring), VarPtr(ByVal StructTest.Astring) ' 26  52  1504276      213632476
    Debug.Print Len(StructTest.Bstring), LenB(StructTest.Bstring), VarPtr(StructTest.Bstring), VarPtr(ByVal StructTest.Bstring) ' 26  52  1504280      21363312
    Debug.Print Len(StructTest.Gstring), LenB(StructTest.Gstring), VarPtr(StructTest.Gstring), VarPtr(ByVal StructTest.Gstring) '  0   0  1504284         0
    Debug.Print Len(StructTest.Lng1), LenB(StructTest.Lng1), VarPtr(StructTest.Lng1), VarPtr(ByVal StructTest.Lng1)         '  4   4  1504288             0
    Debug.Print Len(StructTest.Lng2), LenB(StructTest.Lng2), VarPtr(StructTest.Lng2), VarPtr(ByVal StructTest.Lng2)         '  4   4  1504292             0
    Debug.Print
    Debug.Print Len(StructTest), LenB(StructTest) '                                           ' 20  20
    End Sub
     
    From my experiments I have some initial conclusions for now.
    _ In order to get the final length, Len( ) , in any experiment, I would need to take a number of 1 for Byte types , 4 for Long types, and, significantly, 4 for String types . The latter confirms that the string variable is a pointer of 4 bytes, (regardless of if it is assigned a value or not)
    _ If similar variable types are listed sequentially, they appear to be held in memory in a similar sequential list with no space between them. (In this respect, a Long and a String type appear to be interchangeable, suggesting/ confirming that what is actually held in the structure is the same sort of thing – 4 Bytes)
    _ With 1 *** ( so far seen ), exception, the LenB( ) and Len( ) give similar results
    Last edited by DocAElstein; 02-16-2025 at 11:17 PM.

  8. #28
    Fuhrer, Vierte Reich DocAElstein's Avatar
    Join Date
    Aug 2014
    Posts
    9,521
    Rep Power
    10
    Trick involving user defined Types and the LSet statement.
    Including Bytes , ( and the Len( ) LenB( ) anomaly *** )
    In the last post we considered in the user defined type structure Long and String which appeared to reside similarly therein as 4 Byte numbers (regardless of whether the variables were "filled" or not).
    Initially the case of a Byte is straight forward, but there is a slight anomaly, perhaps not directly caused by the Byte variable itself, but it can effect how they are held within a user defined Type
    This anomaly does not appear for the case of just bytes alone in the Type declaration. For example, in this following type declaration example,
    Private Type StructByte1
    Bite1 As Byte
    Bite2 As Byte
    Bite3 As Byte
    Bite4 As Byte
    Bite5 As Byte
    End Type

    , which is examined in Rem 1 of the coding below, there are no surprises: In the Debug.Print Immediate Window output I give
    _ what appears to be given for the address and value of an instantiated instance ,
    _ which is then also the address given for the first Byte,
    _ and the other bytes appear to be in sequential Byte order
    _ and finally both Len( ) and lenB( ) give the same value of 5

    Here is a typical color=Blue]Debug.Print [/color] Immediate Window output, ( you will get different numbers , as I would if I repeated at a different tine, and7 or a different computer)
    1504566 1504566
    1 1504566
    1 1504567
    1 1504568
    1 1504569
    1 1504570

    5 5


    Things go a bit strange in Rem 2, but having done a few experiments, I think initially I can see a pattern to what is happening. For Rem 2 I am considering a slightly modified user defined Type
    Private Type StructByte2
    Bite1 As Byte
    Bite2 As Byte
    Bite3 As Byte
    Lng As Long
    Bite5 As Byte
    End Type

    Here is a typical actual Debug.Print Immediate Window output
    1504268 1504268
    1 1504268
    1 1504269
    1 1504270
    4 1504272
    1 1504276

    8 12


    To help explain what seems to be going on, I will add a bit to that output, and I will use
    _ Blue as before for byte the addresses obtained,
    _ I will use green for long address, which will be the start address as given in the output, along with the next 3 which we usually assume are there for the typical 4 Byte long.
    _ But I will also add some extra grey addresses to help me explain what is going on.
    Here we go then:-
    Code:
         1504268     1504268   ____
         1             1504268
         1             1504269
         1             1504270
                       1504271  ___
         4             1504272
                       1504273
                       1504274
                       1504275  _____
         1             1504276
                       1504277
                       1504278
                       1504279  ______
    
         8          12
    So what is going on?:-
    It would appear that as soon as I add something of 4 byte length, then 4 bytes becomes the standard minimum length unit size, sort of: Each 4 byte length unit can be occupied by something like a single Long, or up to 4 Bytes
    So what I have in grey are "empty" byte "spaces".
    It appears that Len( ) is telling me the bytes used, whereas LenB( ) tells me the total number of bytes allocated for the user defined Type structure.

    Rem 3 demonstrates more of the same minimum length unit size idea. It takes the last user defined Type and adds 4 bytes in the end.
    Private Type StructByte2
    Bite1 As Byte
    Bite2 As Byte
    Bite3 As Byte
    Lng As Long
    Bite5 As Byte
    Bite6 As Byte
    Bite7 As Byte
    Bite8 As Byte
    Bite9 As Byte
    End Type

    We have in this user defined Type structure exactly the same minimum length unit size as the user defined type used in Rem 2 This means that the situation is now something similar: here a typical actual Debug.Print Immediate Window output
    1504252 1504252
    1 1504252
    1 1504253
    1 1504254
    4 1504256
    1 1504260
    1 1504261
    1 1504262
    1 1504263
    1 1504264

    12 16

    Here is the same view again illustrating the apparent 4 byte unit ordering structure
    Code:
     1504252       1504252 ______
     1             1504252
     1             1504253
     1             1504254
                   1504255 ______
     4             1504256
                   1504257
                   1504258
                   1504259 ______
     1             1504260
     1             1504261
     1             1504262
     1             1504263 ______
     1             1504264
                   1504265
                   1504266
                   1504267 ______
    
     12            16
    Code:
    Private Type StructByte1
     Bite1 As Byte
     Bite2 As Byte
     Bite3 As Byte
     Bite4 As Byte
     Bite5 As Byte
    End Type
    Private Type StructByte2
     Bite1 As Byte
     Bite2 As Byte
     Bite3 As Byte
     Lng As Long
     Bite5 As Byte
    End Type
    Private Type StructByte3
     Bite1 As Byte
     Bite2 As Byte
     Bite3 As Byte
     Lng As Long
     Bite5 As Byte
     Bite6 As Byte
     Bite7 As Byte
     Bite8 As Byte
     Bite9 As Byte
    End Type
    
    Sub TypeWithBytesTest()
    Rem 1
    Dim StructTest1 As StructByte1
    Debug.Print VarPtr(StructTest1), VarPtr(ByVal StructTest1)     ' 1504566       1504566
    Debug.Print Len(StructTest1.Bite1), VarPtr(StructTest1.Bite1) '  1             1504566
    Debug.Print Len(StructTest1.Bite2), VarPtr(StructTest1.Bite2) '  1             1504566
    Debug.Print Len(StructTest1.Bite3), VarPtr(StructTest1.Bite3) '  1             1504566
    Debug.Print Len(StructTest1.Bite4), VarPtr(StructTest1.Bite4) '  1             1504566
    Debug.Print Len(StructTest1.Bite5), VarPtr(StructTest1.Bite5) '  1             1504566
    Debug.Print
    Debug.Print Len(StructTest1), LenB(StructTest1) '   5     5
    Debug.Print
    
    Rem 2
    Dim StructTest2 As StructByte2
    Debug.Print VarPtr(StructTest2), VarPtr(ByVal StructTest2)   '   1504268 1504268
    Debug.Print Len(StructTest2.Bite1), VarPtr(StructTest2.Bite1) '        1 1504268
    Debug.Print Len(StructTest2.Bite2), VarPtr(StructTest2.Bite2) '        1 1504269
    Debug.Print Len(StructTest2.Bite3), VarPtr(StructTest2.Bite3) '        1 1504270
    Debug.Print Len(StructTest2.Lng), VarPtr(StructTest2.Lng)  '           4 1504272
    Debug.Print Len(StructTest2.Bite5), VarPtr(StructTest2.Bite5) '        1 1504276
    Debug.Print
    Debug.Print Len(StructTest2), LenB(StructTest2) '   8    12
    Debug.Print
    
    
    Rem 3
    Dim StructTest3 As StructByte3
    Debug.Print VarPtr(StructTest3), VarPtr(ByVal StructTest3)  '     1504252    1504252
    Debug.Print Len(StructTest3.Bite1), VarPtr(StructTest3.Bite1) '  1             1504252
    Debug.Print Len(StructTest3.Bite2), VarPtr(StructTest3.Bite2) '  1             1504253
    Debug.Print Len(StructTest3.Bite3), VarPtr(StructTest3.Bite3)  ' 1             1504254
    Debug.Print Len(StructTest3.Lng), VarPtr(StructTest3.Lng)  '     4             1504256
    Debug.Print Len(StructTest3.Bite5), VarPtr(StructTest3.Bite5) '  1             1504260
    Debug.Print Len(StructTest3.Bite6), VarPtr(StructTest3.Bite6)  ' 1             1504261
    Debug.Print Len(StructTest3.Bite7), VarPtr(StructTest3.Bite7)  ' 1             1504262
    Debug.Print Len(StructTest3.Bite8), VarPtr(StructTest3.Bite8)  ' 1             1504263
    Debug.Print Len(StructTest3.Bite9), VarPtr(StructTest3.Bite9) '  1             1504264
    Debug.Print
    Debug.Print Len(StructTest3), LenB(StructTest3) '     12   16
    Debug.Print
    End Sub



    Here is the same again with a smaller simpler example with a string and a byte
    Code:
    Private Type StructStrByte
     Astring As String
     Bite As Byte
    End Type
    Sub TestStrBite()
     Dim AstrBite As StructStrByte
    Debug.Print VarPtr(AstrBite), VarPtr(ByVal AstrBite)  '       1504288       1504288
    Debug.Print Len(AstrBite.Astring), VarPtr(AstrBite.Astring) ' 0             1504288
    Debug.Print Len(AstrBite.Bite), VarPtr(AstrBite.Bite) '       1             1504292
    Debug.Print
    Debug.Print Len(AstrBite), LenB(AstrBite) '     5  8
    Debug.Print
    
    End Sub
    
    Here is the actual Debug.Print Immediate Window output
    1504288 1504288
    0 1504288
    1 1504292

    5 8


    This would be my pictorial attempt to show the actual structure
    Code:
     1504288       1504288 _____
     0             1504288
                   1504289
                   1504290
                   1504291 _____
     1             1504292
                   1504293
                   1504294
                   1504285  _____
    
     5             8
    I use a similar color as before:
    _ As before blue is the Byte byte addresses
    _ , green is now the String byte addresses, ( the first was given, and as we assume that a 4 byte pointer is being held, so we know the next 3 addresses)
    _ , and as before, grey is the allocated but not used byte addresses




    Conclusions at this stage
    Based on the stuff shown above as well a lot of similar and slightly different experiments, too many to mention for now
    _ For the case of Bytes, Longs and Strings we will have a 4 byte "unit" size, unless we only have Byte in which the "unit" size will be 1
    _ If we have the 4 byte "unit" size, each unit will either
    __ be filled with a color=Blue]Long[/color] or String , or
    __ any or all of them can be filled with bytes, any not used are .. well .. not used! But they are there , allocated as it were
    _ The order given of the variables inside the Type is important – this next would result in 8 bytes being used, 2 "spare",
    Bite1 As Byte
    Bite2 As Byte
    Astring As String

    The next would result in 12 bytes being used , 6 "spare"
    Bite1 As Byte
    Astring As String
    Bite2 As Byte
    Last edited by DocAElstein; 02-17-2025 at 02:29 PM.

  9. #29
    Fuhrer, Vierte Reich DocAElstein's Avatar
    Join Date
    Aug 2014
    Posts
    9,521
    Rep Power
    10
    Trick involving user defined Types and the LSet statement.
    How/ Why the LSet trick bit works
    Although the LSet is intended as a string manipulation thing, for our purposes it works on the fundamental low level byte addresses, and will not be of much direct use in handling characters. It will only be helpful in the case of their fundamental low level number (code point )
    We will look at a couple of examples , using the trick to do
    _ something we did with Long stuff
    , and
    _ something we did to consider the low level number (code point) for a character, that single character that looks like 3 small dots (CP 8230)

    Type byte size issues

    A quick bit of info… I would have thought the "byte size" would be known for sure, but on the internet information is sometimes contradictory. Based on the previous two posts, I think I am happy for now to say that the following coding is telling me that deep in memory I have bytes used of
    Integer – 2 , Long – 4 , Single - 4, Double - 8
    Code:
    Private Type MyInt
     Lng As Long
    End Type
    Private Type MyLong
     Lng As Long
    End Type
    Private Type MySingle
     Sgle As Single
    End Type
    Private Type MyDouble
     Dble As Double
    End Type
    Private Type MyString
     Strg As String
    End Type
    Sub MyTypesTypes()
    Dim MeInt As Integer, MeLong As Long, MeSgle As Single, MeDouble As Double, MeString As String
    Debug.Print LenB(MeInt), LenB(MeLong), LenB(MeSgle), LenB(MeDouble), LenB(MeString)                     '  2  4  4  8  0
    Dim MeIntType As MyInt, MeLongType As MyLong, MeSgleType As MySingle, MeDoubleType As MyDouble, MeStringType As MyString
    Debug.Print LenB(MeIntType), LenB(MeLongType), LenB(MeSgleType), LenB(MeDoubleType), LenB(MeStringType) '  2  4  4  8  4
    End Sub


    Long
    We demonstrated before that the 4 bytes used in memory for a long were "shuffled the wrong way around"
    We will do that again using the trick
    How/why
    It is thought that the working of the LSet is fundamentally that of the RtlMoveMemory. When used in the conventional way with a string, VB(A) knows how to get at the relevant Bytes.
    Some documentation also states that we can use the LSet to copy a variable from one user-defined type to another…. compatible, user-defined type… , and that what happens then is that … the binary data from one variable is copied into the memory space of the other…
    Initial experiments suggest that any number type orientated user defined Types are compatible.
    Example
    In words, the general idea of coding below , is doing the following
    Our "Destination" is a simple sequential memory location of bytes
    The LSet code lines effectively put a copy of the binary data from / of the variable on the RHS of the = , starting from the left of the memory location of the "Destination" variable given as the argument of the LSet( Destination ) on the LHS of the =
    We can attempt a pseudo coding comparison of the previous CopyMemory ( "RtlMoveMemory" ) way to our LSet user defined Type way thus…


    This is what we had previously for the CopyMemory ( "RtlMoveMemory" ) way

    _______________[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



    This is the equivalent pseudo coding when using the trick

    _____________LSet(Destination) ___ = ___ Source ___ [Number of bytes]
    Slightly elaborated version:
    LSet( where to start** putting the copied bytes ) ____ = __ ( where to **copy all Bytes from )


    The specific coding
    This is similar in its working to the Long experiments with CopyMemory ( "RtlMoveMemory" )
    My Type SourceLong is a 4 sequential byte memory location that gets filled with the decimal number 16777216, which in normal maths binary for 4 byte, (32 bits), would look like
    00000001 00000000 00000000 00000000
    , but we think that in computer memory it gets put in the reversed byte order
    00000000 00000000 00000000 00000001
    Now the LSet code line takes all that and puts it starting from the first byte in my Type ByteBlock
    We Debug.Printthe byte vales out, in the order they are there. The result confirms the reversed byte order as it is in memory. (In this case the last 4 bytes were not used, so remain at 0)

    As ever, note that it is the bytes that are reversed. It is not that the entire bits are simply reversed If that had been the case we would have this,
    00000000 00000000 00000000 10000000 00000000 00000000 00000000 00000000
    In other words we would see the numbers thus
    0 0 0 255 0 0 0 0
    We do not ever experience that complete bit reversing


    Code:
    Private Type ByteBlock     '
     byte0 As Byte
     byte1 As Byte
     byte2 As Byte
     byte3 As Byte
     byte4 As Byte
     byte5 As Byte
     byte6 As Byte
     byte7 As Byte
    End Type
    Private Type SourceLong
     MyLong As Long  '  '  Any number type is OK here.  String gives a compile error at the  LSet  line
    End Type
    Sub LSetLongExample()
    Dim Dest As ByteBlock, SrcLng As SourceLong ' These both seem to  be  userdefined Types with numbers, bytes actually, which is perhaps required for avoiding  compile mismatch erroro at  LSet Dest = Src
     Let SrcLng.MyLong = 16777216 '   2^24=16777216 -  "Normal" maths Binary 00000001 00000000 00000000 00000000 , but, ....
    '           ....  we think in memory the actial byte order is reversed - 00000000 00000000 00000000 00000001
     LSet Dest = SrcLng
    Debug.Print Dest.byte0, Dest.byte1, Dest.byte2, Dest.byte3, Dest.byte4, Dest.byte5, Dest.byte6, Dest.byte7
    '              0            0         0            1           0           0          0            0   (only the first  4 bytes  were "fillled")
     End Sub
    Last edited by DocAElstein; 02-19-2025 at 03:46 PM.

  10. #30
    Fuhrer, Vierte Reich DocAElstein's Avatar
    Join Date
    Aug 2014
    Posts
    9,521
    Rep Power
    10
    String Insights and the LSet trick

    ( As with many interesting insights and tricks I was shown this https://eileenslounge.com/viewtopic....323073#p323073 )

    We are still not far enough to tackle finally in explicit detail strings in the win 32 api, so once again we are talking around them, looking at string characteristics generally

    Some basic maths review
    Once again the relevant very basic low level number computer number issues are good to revise again.
    For clarity in this post I am restricting to 16 digits. From previous posts related to string things it may be obvious why, and if not, it will be after this revision
    All this in this initial review section is based on conventional school maths stuff. We will discuss the Microsoft / computer deviations from the norm in the next section, LSet Type trick to explore UTF-16 LE

    We are probably all aware of the base 2 ( binary ) system, and are certainly aware of the base 10 ( decimal ) system.
    We can have any base system, and the basic idea and workings are the same.
    Let us consider a few bases using 16 digits, for a decimal number , an old friend of ours, a number, which in decimal is 8230
    Unicode code point 8230.JPG
    ( In binary, so deep down in computer 0s and 1s, we need 14 digits for decimal 8230, so 16 digits is sufficient )
    We consider a spread of bases with 16 digits ( bits ) :
    base 2 (binary) ;
    base 16 (Hexadecimal);
    and base 256

    The following sketch shows that fundamentally the base 2, or 0/1 state bits are the same in either base system.
    The final number we see or use, whether it is
    0 0 1 0 0 0 0 0 0 0 1 0 0 1 1 0
    or
    2 0 2 6
    or
    32 38
    or
    8230
    , is, well… the final number the software or system we are using presents the same 0/1 state bits to us
    Code:
     '                          Base  2 (Binary)   with   16  digits
    ' 2^15  2^14  2^13 2^12  2^11   2^10  2^9  2^8      2^7 2^6  2^5  2^4    2^3  2^2  2^1  2^0
    ' 32768 16384 8192 4096  2048   1024  512  256      128  64   32   16     8    4    2    1
    '   0     0    1     0     0      0    0    0         0   0    1    0     0    1    1    0          - Binary ( Base 2 )
    '   0   + 0 + 8192 + 0  +  0   +  0 +  0 +  0    +    0 + 0  + 32 + 0  +  0 +  4 +  2 +  0  = 8230  - calculating the decimal 8230
    '                                 0 0 1 0 0 0 0 0 0 0 1 0 0 1 1 0
    
    '                          Base  16 (Hexadecimal)   with   16  digits
    '    16^3  = 4096     |       16^2 = 256       |        16^1 = 16      |   16^0  =  1
    '  2^3 2^2  2^1  2^0  | 2^3  2^2  2^1  2^0     |    2^3 2^2  2^1  2^0  | 2^3  2^2  2^1  2^0
    '   8   4    2    1   |  8    4    2    1      |     8   4    2    1   |  8    4    2    1
    '   0   0    1   0    |  0    0    0    0      |     0   0    1    0   |  0    1    1    0        ( - Binary ( Base 2 ) )
    '   0 + 0  + 2 + 0 =2 |  0 +  0 +  0 +  0 = 0  |     0 + 0  + 2  + 0=2 |  0 +  4 +  2 +  0 = 6
    '     ( 2 x 256 )      +           0           +          ( 2 x 16 )    +   ( 6 x 1 )      = 8230  - calculating the decimal 8230
    '           2                      0                           2               6          2 0 2 6  - Hexadecimal ( Base 16 )
    
    '                                 Base 256  with   16  digits
    '                 256^1  = 256                                       256^0  =  1
    '  2^7 2^6  2^5  2^4    2^3  2^2  2^1  2^0     |    2^7 2^6  2^5  2^4    2^3  2^2  2^1  2^0
    '  128  64   32   16     8    4    2    1      |    128  64   32   16     8    4    2    1
    '   0   0    1    0      0    0    0    0      |     0   0    1    0      0    1    1    0        ( - Binary ( Base 2 ) )
    '   0 + 0  + 32 + 0   +  0 +  0 +  0 +  0  =32 |     0 + 0  + 32 + 0   +  0 +  4 +  2 +  0 = 38
    '                   ( 32 x 256 )               +          ( 38 x 1 )     =      8230               - calculating the decimal 8230
    '                        32                                    38              32 38               - Base 256
    Although the 0/1 state thing ( a bit ) is the most fundamental computer number unit, for many reasons , in many computer systems, we consider a Byte ( 8 bits ) as a fundamental unit. For example, in computer memory, if a position has been defined as address 123456788, then the next 8 bits along ( so the next Byte along ) will have the address 123456789 The address in both cases refers to 8 bits. So a Byte could have a decimal value from 0 to ( 128+64+32+16+8+4+2+1 )= 255, so 256 numbers 0-255

    Microsoft choose to use the number system similar to the last in the sketch above, but they have the two bytes placed the other way around. They call this (2 byte) UTF-16 LE, as we have discussed before. This means that we are likely to see the character ChrW(8230) , , somehow represented in the form 38 32

    We explore this in the next section


    LSet Type trick to explore UTF-16 LE
    We will not directly be looking at strings , just the number 8230, to investigate how it may look in memory, or rather how it might be presented to us. In other words we investigate the theoretical memory storage that we mentioned at the end of the last section….. Microsoft choose to use the number system similar to the last in the sketch above, but they have the two bytes placed the other way around. They call this UTF-16 LE, as we have discussed before. This means that we are likely to see the character ChrW(8230) , , somehow represented in the form 38 32 ….

    We are interested in simple whole number variables. Long is like this , with 4 bytes. Integer is like it as well, but with 2 bytes

    If things go as we expect, then 2 bytes as a Destination for where we use the LSet trick to copy our number to, should be sufficient, but we will use a destination user defined type made of 6 bytes, just to see what happens
    Code:
    Private Type MeDestBytes
     byte0 As Byte
     byte1 As Byte
     byte2 As Byte
     byte3 As Byte
     byte4 As Byte
     byte5 As Byte
    End Type
    So that above is our destination.
    We will use 2 different sources, the Long and Integer structures of these u ser defined types
    Code:
    Private Type UTF_16LEint
     CharBytes As Integer
    End Type
    Private Type UTF_16LElng
     CharBytes As Long
    End Type
    So the coding below which uses those will have two similar sections. In those two similar sections we place the number 8230 in the Long or integer structure, which we are expecting to get put in memory in the two byte ( 256 base ) form of the last part of the sketch above, but with the bytes the other way around
    Each of the two resulting bytes in memory will get LeftSeted at our destination memory place.
    The Debug.Printed results seem to tie up with the prediction, - for example, the 38 of the last byte from our sketch above gets switched around to the first position
    Code:
    Sub CharBytesInMemory()  '     https://www.excelfox.com/forum/showthread.php/2824-Tests-Copying-Pasting-API-Cliipboard-issues-and-Rough-notes-on-Advanced-API-stuff?p=17891&viewfull=1#post17891
    Rem 1 Long
    Dim LELng As UTF_16LElng, Dest As MeDestBytes
     Let LELng.CharBytes = 8230 '   In Microsoft Unicode UTF-16 LE encoding, this will look like  38 32
    LSet Dest = LELng
    Debug.Print Dest.byte0, Dest.byte1, Dest.byte2, Dest.byte3, Dest.byte4, Dest.byte5
    '             38            32           0             0          0          0      The last 2 bytes never get used here so stay at 0
    Rem 2 Integer
    Dim LEInt As UTF_16LEint
     Let LEInt.CharBytes = 8230
    LSet Dest = LEInt
    Debug.Print Dest.byte0, Dest.byte1, Dest.byte2, Dest.byte3, Dest.byte4, Dest.byte5
    '             38            32           0             0          0          0      The last 4 bytes never get used here so stay at 0
    End Sub





    As things seem to look exactly as expected we can do a neater tidier representation of the way Microsoft represent characters ( or rather their code point number ) in memory by restricting ourselves to 2 bytes everywhere, as in the initial reference
    Code:
    Private Type UTF_16LElng
     CharBytes As Long
    End Type
    Private Type DestHiLoBytePair ' For the two bytes, as in memory for a character in Microsoft Unicode UTF-16 LE 2 byte encodung
      byteHi As Byte ' we are expecting this to get  38
      byteLo As Byte ' we are expecting this to get  32
    End Type
    Private Type SourceUTF_16LEint
     CharBytes As Integer ' this will be given the single character that looks like 3 samll dots,  ChrW(8230) (it is also most Chr(133))     …
    End Type
    Sub UTF_16LE2ByteChr()  '  https://eileenslounge.com/viewtopic.php?p=323073#p323073
    Dim Dest As DestHiLoBytePair, Source As SourceUTF_16LEint
     Let Source.CharBytes = 8230
    LSet Dest = Source
    Debug.Print Dest.byteHi, Dest.byteLo
    '                38          32
    End Sub

    Remember finally, that we are just dealing with numbers here. All we have really done is shown that the decimal number of 8230 will be held in a backward base 256 when we hold it in a 2 byte Integer
    In other words, the little Indian backward byte shuffle dance is done by an Integer as it is with a Long.




    Here is a quick check / correlation with a short coding, using the 1 to 1 Byte to string character phenomena discussed at the start of this post (
    https://www.excelfox.com/forum/showt...ll=1#post17885
    )
    Code:
    Sub ChrW8230ByteArray()
    Dim arrBytes() As Byte
     Let arrBytes() = "…"  '   1 to 1 Byte to string character phenomena https://www.excelfox.com/forum/showthread.php/2824-Tests-Copying-Pasting-API-Cliipboard-issues-and-Rough-notes-on-Advanced-API-stuff?p=17885&viewfull=1#post17885
    Debug.Print arrBytes(LBound(arrBytes)), arrBytes(UBound(arrBytes))
    '                    38                       32
    End Sub
    
    Last edited by DocAElstein; 02-20-2025 at 04:39 PM.

Similar Threads

  1. Some Date Notes and Tests
    By DocAElstein in forum Test Area
    Replies: 5
    Last Post: 03-26-2025, 02:56 AM
  2. Replies: 116
    Last Post: 02-23-2025, 12:13 AM
  3. Replies: 21
    Last Post: 12-15-2024, 07:13 PM
  4. Replies: 42
    Last Post: 05-29-2023, 01:19 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
  •