Results 1 to 38 of 38

Thread: VB6 - can we use the 'Any' type?

  1. #1

    Thread Starter
    PowerPoster joaquim's Avatar
    Join Date
    Apr 2007
    Posts
    3,515

    VB6 - can we use the 'Any' type?

    some functions declarations, from a library('Lib'), can have a 'Any' argument type...
    can we use these type or not? or the 'Object' type is similar?
    VB6 2D Sprite control

    To live is difficult, but we do it.

  2. #2
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    6,576

    Re: VB6 - can we use the 'Any' type?

    Here's my take on this...

    I've never seen the "Any" type used anywhere but an API declaration. I don't think it's possible to use it in a normal procedure's declaration (i.e., it'll be a syntax error). I suppose it'd be nice if it were allowed, but the Variant pretty much accomplishes the same objective.

    Regarding the "Object" type, I see this as something very different. The "Object" type is just a way to have late-bound objects passed around and referenced. Whereas, I see the "Any" type having more to do with fundamental types.

    When the "Any" type is seen, particularly on ByRef arguments, it just allows variables (typically the fundamental types, integer, long, single, double, maybe string) to be passed into the API. Under certain circumstances, the same API might understand how to deal with different "types" coming in, and this allows for that possibility.

    I don't see it as much more than that.

    Take Care,
    Elroy

    EDIT: And, more than anything, I see this as a compiler directive. In other words, it tells the compiler to not be so persnickety about type mismatches when the type is "Any". It's still up to you to make sure these API declarations are declared and used correctly.
    Last edited by Elroy; Jan 11th, 2021 at 02:04 PM.
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  3. #3

    Thread Starter
    PowerPoster joaquim's Avatar
    Join Date
    Apr 2007
    Posts
    3,515

    Re: VB6 - can we use the 'Any' type?

    "..When the "Any" type is seen, particularly on ByRef arguments..."
    that's why we use 'byval 0' for we get zero on 'Any' arguments
    VB6 2D Sprite control

    To live is difficult, but we do it.

  4. #4
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,937

    Re: VB6 - can we use the 'Any' type?

    VB is a strongly typed language. Allowing the general use of Any would violate this. It is allowed in Declare statements because imported functions may not hold themselves to the same restrictions. C in particular allows you to bypass strong typing protections by the use of void pointers. You can acquire the pointer to any type in C and if an imported function takes pointer parameters, it would only make sense to allow VB to do the same as C when importing functions. ByRef in combination with Any is VB6's version of casting the address of a variable to a void*.

    A common example is the pervasive use of Kernel32's RtlMoveMemory API in VB6 applications. You really don't want to have to make multiple declarations of this function for every data type who's buffer you want to access.
    Last edited by Niya; Jan 11th, 2021 at 04:33 PM.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  5. #5
    PowerPoster
    Join Date
    Feb 2006
    Posts
    21,764

    Re: VB6 - can we use the 'Any' type?

    If you want something like that there is always the Variant type.

  6. #6
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,937

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by dilettante View Post
    If you want something like that there is always the Variant type.
    To be clear it's not semantically the same. Using Variants invokes a conversion, using Any does not in many cases.

    To understand the difference, think about what happens when you use CStr on an Integer value. CStr converts the underlying binary representation of the Integer value to the binary representation that would allow a String to display the number as we humans would understand it. Even in simpler scenarios like assigning an Integer to a Double. Floating point numbers are represented differently to integers. Assignments between Doubles and Integers result in conversions of these binary representations. The use of Variants does a conversion similar to what boxing is .Net. It creates a complicated 16 byte structure that contains the value along with it's type information. In all these cases, there is some kind of change happening to the binary representation before it gets copied to the address of the new variable it's being assigned to.

    The use of Any can bypass all this binary conversion. If you pass an Integer as an argument to a function that was declared ByRef arg as Any, what the other side gets is a pointer to the raw unmodified binary data that represents the Integer. However, if you declared the parameter as ByRef arg as Variant, what the other side would get is a pointer to a Variant that contains the Integer. In short, Any allows you to bypass implicit conversions.

    Here is an example of this in action:-
    vb6 Code:
    1. '
    2. Option Explicit
    3. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
    4.             (lpDest As Any, lpSource As Variant, ByVal cbCopy As Long)
    5.  
    6. Private Sub Form_Load()
    7.     Dim a As Integer 'Declare an Integer
    8.     Dim b As Variant 'Declare a Variant
    9.    
    10.     'Assign the value of 51 to the Integer variable
    11.     '---------------------------------------------
    12.     a = 51
    13.    
    14.     'The lpSource parameter is declared
    15.     'as ByRef Variant, yet we are passing an Integer
    16.     '---------------------------------------------
    17.     CopyMemory b, a, 16
    18.    
    19.     'This will print the value of 51 to
    20.     'the immediate window which means the code works.
    21.     'It works because VB6 implicity
    22.     'converts 'a' to a Variant before copying the memory.
    23.     'Notice in our CopyMemory call that
    24.     'we copy 16 bytes and not 2 bytes
    25.     'because we expected this implicit conversion
    26.     '---------------------------------------------
    27.     Debug.Print CStr(b)
    28.  
    29.  
    30. End Sub
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  7. #7
    PowerPoster
    Join Date
    Feb 2017
    Posts
    2,794

    Re: VB6 - can we use the 'Any' type?

    Any means it is a pointer to any data type.

    It is used only in API declarations and there is no corresponding in VB6 normal code because VB6 does not work with pointers.

  8. #8
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,937

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by Elroy View Post
    EDIT: And, more than anything, I see this as a compiler directive. In other words, it tells the compiler to not be so persnickety about type mismatches when the type is "Any". It's still up to you to make sure these API declarations are declared and used correctly.
    That's not entirely accurate.

    Any is actually a kind of alias for the Long type. This is clear when you try using Any ByVal parameters to pass anything beside Longs. It won't compile:-
    vb6 Code:
    1. '
    2. Option Explicit
    3. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
    4.             (ByVal lpDest As Any, ByVal lpSource As Any, ByVal cbCopy As Long)
    5.  
    6. Private Sub Form_Load()
    7.     Dim a As Integer
    8.     Dim b As Integer
    9.    
    10.     a = 102
    11.    
    12.    
    13.     'If these variables were declared as Long
    14.     'instead of Double, this program
    15.     'would compile and work correctly.
    16.     Dim a_ptr As Double
    17.     Dim b_ptr As Double
    18.    
    19.     a_ptr = VarPtr(a)
    20.     b_ptr = VarPtr(b)
    21.    
    22.     'This would not compile because it
    23.     'expects the first two parameters
    24.     'to be longs
    25.     CopyMemory b_ptr, a_ptr, 2
    26.  
    27.     Debug.Print CStr(b)
    28.    
    29.  
    30. End Sub

    CopyMemory in the above program is declared to expect two Any parameters to be passed by value yet if you tried to pass anything beside Longs, it would not compile citing a Type mismatch error. It is extremely clear that Any is meant to be used to pass 32 bit pointers. It is EXACLTY the same as declaring them as ByVal Long. What makes Any special is that it completely bypasses any type checking when used in combination with ByRef. If you declare the parameters as ByRef Any and you will be able to use the same call to pass the pointer to any type of variable. Variants are a special case. There's no type checking with Variants either but you do get in trouble with implicit conversions. You can refer to my previous post to see what happens when you use Variants instead of Any.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  9. #9
    Frenzied Member
    Join Date
    Jun 2012
    Posts
    1,660

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by joaquim View Post
    "..When the "Any" type is seen, particularly on ByRef arguments..."
    that's why we use 'byval 0' for we get zero on 'Any' arguments
    Be carefull. "ByVal 0" is actually an Integer. If the API expects a Long then 2 bytes are off. It can cause "surprise errors".
    To avoid this use "ByVal 0&".

  10. #10
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Posts
    2,585

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by Krool View Post
    Be carefull. "ByVal 0" is actually an Integer. If the API expects a Long then 2 bytes are off. It can cause "surprise errors".
    To avoid this use "ByVal 0&".
    For As Any params this makes no difference. (For As Variant it does.)

    My point is that (ByVal) Integer params are passed as zero-extended Longs on the stack in stdcall.

    Boolean (16-bit) and Byte (8-bit) integral data types get zero-extended to 4 bytes when pushed on the stack too.

    cheers,
    </wqw>

  11. #11
    Frenzied Member
    Join Date
    Jun 2012
    Posts
    1,660

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by wqweto View Post
    For As Any params this makes no difference. (For As Variant it does.)

    My point is that (ByVal) Integer params are passed as zero-extended Longs on the stack in stdcall.

    Boolean (16-bit) and Byte (8-bit) integral data types get zero-extended to 4 bytes when pushed on the stack too.

    cheers,
    </wqw>
    True. BUT. How is the 0 Integer get extended to Long 0?
    It needs to read 2 bytes off somewhere.
    I experienced the problem in comctl32.dll that a "ByVal -1" got interpret as -2321345 (example value) and doing a "ByVal -1&" fixed the issue. (was the TCM_SETMINTABWIDTH where SendMessage is "As Any")
    Last edited by Krool; Jan 12th, 2021 at 07:05 AM.

  12. #12
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,937

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by Krool View Post
    True. BUT. How is the 0 Integer get extended to Long 0?
    It needs to read 2 bytes off somewhere.
    I experienced the problem in comctl32.dll that a "ByVal -1" got interpret as -2321345 (example value) and doing a "ByVal -1&" fixed the issue. (was the TCM_SETMINTABWIDTH where SendMessage is "As Any")
    I will take a guess and say is it BECAUSE it is zero extended. In your case, you're dealing with negative numbers so if it was sign extended instead of just zero extended, your code could have worked without passing the literal as a Long. This is just a wild guess though. I don't know all the details behind what you were doing so I can't say with 100% certainty.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  13. #13
    Frenzied Member
    Join Date
    Jun 2012
    Posts
    1,660

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by Niya View Post
    I will take a guess and say is it BECAUSE it is zero extended. In your case, you're dealing with negative numbers so if it was sign extended instead of just zero extended, your code could have worked without passing the literal as a Long. This is just a wild guess though. I don't know all the details behind what you were doing so I can't say with 100% certainty.
    The real code is: (from an IntValue variable, -1 is just the default for the API)
    Code:
    PropTabMinWidth = IntValue
     
    SendMessage TabStripHandle, TCM_SETMINTABWIDTH, 0, ByVal CLng(PropTabMinWidth)

  14. #14
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,937

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by Krool View Post
    The real code is: (from an IntValue variable, -1 is just the default for the API)
    Code:
    PropTabMinWidth = IntValue
     
    SendMessage TabStripHandle, TCM_SETMINTABWIDTH, 0, ByVal CLng(PropTabMinWidth)
    Hmmm that is interesting. If it was zero extended then it should have interpreted -1 as 65535, not -2321345. The binary representation of -1 in a 16 bit Integer is 1111111111111111. If that value is zero extended to 32 bits, it would become 00000000000000001111111111111111 which is 65535. Something else is happening here. You might be right. The only explanation for that random negative number is that the 2 bytes of the 4 bytes are not being overwritten when you passed a 16 bit Integer which implies it's not being zero extended to 32 bits.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  15. #15
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Posts
    2,585

    Re: VB6 - can we use the 'Any' type?

    https://docs.microsoft.com/en-us/cpp...?view=msvc-160

    It says "all arguments are widened to 32 bits when they are passed" so I might be wrong about zero-extending. Generally widening means sign-extending for signed types and zero-extending for unsigned.

    cheers,
    </wqw>

  16. #16

    Thread Starter
    PowerPoster joaquim's Avatar
    Join Date
    Apr 2007
    Posts
    3,515

    Re: VB6 - can we use the 'Any' type?

    thank you so much for all to all... thanks
    VB6 2D Sprite control

    To live is difficult, but we do it.

  17. #17
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,937

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by wqweto View Post
    https://docs.microsoft.com/en-us/cpp...?view=msvc-160

    It says "all arguments are widened to 32 bits when they are passed" so I might be wrong about zero-extending. Generally widening means sign-extending for signed types and zero-extending for unsigned.

    cheers,
    </wqw>
    Actually you may have just solved the mystery. That article says that all arguments are widened to 32 bits, it didn't say they are sign extended and I don't think there would be any sign extension. Zero extensions and sign extensions are performed by only a handful of x86 instructions. CBW/CWDE instructions perform sign extensions as well as MOVZX type instructions. These instructions aren't involved in setting up a stack frame. Parameters are setup by PUSH instructions so all they would have to do to widen is just PUSH everything as 32 bits. Now this doesn't make sense 100% because if you PUSH a 16 bit variable onto the stack as a 32 bit variable, it means you would end up reading 2 extra bytes that have nothing to do with the variable which would risk an access violation exception. Another way they could do it but without risking access violations is to PUSH the 16 bit value as a 16 bit value but then decrement the stack pointer by 2 bytes before pushing another parameter. This option is more likely but doesn't make much sense either as compiler developers would want their compiled code to be as optimized as possible. I'm not sure that they would sacrifice performance to align the stack this way. I'm not an expert in these matters so I'm at a loss as to what is really happening. The only way to know for sure is to look at what the compiler is actually doing by disassembling a VB6 compiled EXE.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  18. #18
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Posts
    2,585

    Re: VB6 - can we use the 'Any' type?

    You might be right "widening" does not mean zero/sign-extending unless zero/sign-extending is cheaper/happen automatically.

    Consider this snippet

    Code:
    Option Explicit
    
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, Length As Any)
    
    Private Sub Form_Load()
        Dim a As Long
        Dim b As Long
        Dim i(0 To 10) As Integer
        
        b = 5
        MsgBox "b=" & b, vbExclamation
        Call CopyMemory(a, b, ByVal 4)
        i(0) = 4
        i(1) = 4
        Call CopyMemory(a, b, ByVal i(0))
        Debug.Assert a = 5
        MsgBox "a=" & a, vbExclamation
    End Sub
    . . . is compiled with no bounds checking to this

    Code:
        Call CopyMemory(a, b, ByVal 4)
    00401B31 6A 04                push        4  
    00401B33 8D 45 C8             lea         eax,[b]  
    00401B36 50                   push        eax  
    00401B37 8D 45 CC             lea         eax,[a]  
    00401B3A 50                   push        eax  
    00401B3B E8 88 FD FF FF       call        ___vba@07DB35D4 (4018C8h)  
    00401B40 E8 27 F6 FF FF       call        ___vbaSetSystemError (40116Ch)  
        i(0) = 4
    00401B45 8B 45 E0             mov         eax,dword ptr [ebp-20h]  
    00401B48 66 C7 00 04 00       mov         word ptr [eax],4  
        i(1) = 4
    00401B4D 8B 45 E0             mov         eax,dword ptr [ebp-20h]  
    00401B50 66 C7 40 02 04 00    mov         word ptr [eax+2],4  
        Call CopyMemory(a, b, ByVal i(0))
    00401B56 8B 45 E0             mov         eax,dword ptr [ebp-20h]  
    00401B59 66 8B 00             mov         ax,word ptr [eax]  
    00401B5C 50                   push        eax  
    00401B5D 8D 45 C8             lea         eax,[b]  
    00401B60 50                   push        eax  
    00401B61 8D 45 CC             lea         eax,[a]  
    00401B64 50                   push        eax  
    00401B65 E8 5E FD FF FF       call        ___vba@07DB35D4 (4018C8h)  
    00401B6A E8 FD F5 FF FF       call        ___vbaSetSystemError (40116Ch)
    So first direct literal ByVal 4 is compiled to push 4 where 6A as instruction pushes a *byte* that is sign-extended to 32-bit dword.

    Second call to CopyMemory is fatal and results in 99% chance of access violation. The first mov eax,dword ptr [ebp-20h] gets the array pointer into eax while the mov ax,word ptr [eax] retrieves the 16-bit Integer into ax without zero/sign-extending it and finally the whole eax gets pushes on stack with upper 16-bit containing the high-word of the array address. Ouch!

    In conclusion:
    1. ByVal 0 is always safe.
    2. ByVal -1 might not be safe (any other literal above 255 or negative too)
    3. ByVal intVar is most probably not safe

    cheers,
    </wqw>

  19. #19
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,937

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by wqweto View Post
    So first direct literal ByVal 4 is compiled to push 4 where 6A as instruction pushes a *byte* that is sign-extended to 32-bit dword.
    This is where all the confusion is. I'm not entirely sure that this PUSH instruction does zero/sign extension. According to this document, the op code 6A is a PUSH imm8 which means it directly pushes an 8 bit value onto the stack. It doesn't however make it clear how it does that. Either this document is incomplete or we are supposed to deduce exactly what it does from what is described. Further down the document we see this:-
    Code:
    '
    if(StackAddressSize == 32) {
    	if(OperandSize == 32) {
    		ESP = ESP - 4;
    		SS:ESP = Source //push doubleword
    	}
    	else { //OperandSize == 16
    		ESP = ESP - 2;
    		SS:ESP = Source; //push word
    	}
    }
    else { //StackAddressSize == 16
    	if(OperandSize == 16) {
    		SP = SP - 2;
    		SS:ESP = Source //push word
    	}
    	else { //OperandSize == 32
    		SP = SP - 4;
    		SS:ESP = Source; //push doubleword
    	}
    }
    The above basically describes how PUSH instructions are executed by the processor. It describes what happens when the memory addresses are 16 bit and when they are 32 bit. In our case the address size will always be 32 bit. But it also describes the behavior based on different operand sizes. It accounts for 16 bit and 32 bit operand sizes. However, the PUSH imm8(6A) implies an operand size of 8 bits yet the code above does not describe what would happen with an 8 bit operand. So all we can do is make an assumption based on what is done with other operand sizes. With a 16 bit operand size, PUSH decrements the stack pointer by 2 and writes the value on the stack. With a 32 bit operand, it decrements the stack pointer by 4 so I am assuming the 6A opcode will decrement the stack pointer by 1 byte and writes an 8 bit value. None of this gives me the impression that any kind of sign/zero extension is happening. It doesn't even imply that any kind of widening is happening either.

    I'm gonna perform some tests of my own to find out what the 6A instruction actually does and make a follow-up post on what I find.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  20. #20
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,937

    Re: VB6 - can we use the 'Any' type?

    Ok, after hours of experimenting with this, I've discovered that there are a lot of tiny nuances that only someone with extensive assembly language experience would know. I'm still very much an amateur at this.

    I set out to find out the behavior of 6A instruction, expecting it to be a simple test. It led me down a deep rabbit hole for quite a bit of time before I had to make a decision before I ended up spending days on this. I still have a few questions but I also found a few answers.

    My idea was to measure how much the stack pointer is moved by the 6A PUSH instruction. So I loaded up and old VB.Net project where I did some work on self modifying code. It already had all the infrastructure I needed for this experiment, like an assembler and support code for executing that code in the process on the fly. After a couple hours trying to the project to work again, I began my experiment. I started with this:-
    Assembly Code:
    1. ; VB.Net function signature:
    2. ; Function MeasureA6() As Integer
    3.  
    4. ;**************************************************************
    5. ;This is a test function that measures how far the stack pointer moves after
    6. ;executing a PUSH instruction
    7. ;**************************************************************
    8.  
    9. use32                               ; Tell FASM.Net that this is 32 bit code
    10.  
    11.                                     ; Set up stack frame
    12. ; -----------------------------------------------------------------------------------------
    13. push ebp                            ; Save the calling function's base pointer
    14. mov ebp, esp                        ; Set the base pointer to represent this function's stack frame
    15. ; -----------------------------------------------------------------------------------------
    16.  
    17. push ebx                            ; Preserve EBX register. We use it to save the address of the stack pointer before the PUSH
    18. push ecx                            ; Preserve ECX register. We use it to save the address of the stack pointer after the PUSH
    19.  
    20. mov ebx, esp                        ; Save the address of the stack pointer
    21.  
    22. push [value]                        ; Push the value at the address of "value" onto the stack
    23.                                     ; The size of the value, which is determined by the data directive(db, dw, dd etc)
    24.                                     ; would determine how many bytes are pushed onto the stack
    25.  
    26. mov ecx, esp                        ; The above PUSH would have decremented the stack pointer
    27.                                     ; so we record this new address
    28.  
    29. pop [value]                         ; We are going to tearing down the stack
    30.                                     ; frame so we need to pop our little test value off the stack
    31.                                     ; before we tear it down
    32.  
    33. sub ebx, ecx                        ; Subtract the old SP address(ebx) from the new SP address(ecx)
    34.  
    35. mov eax, ebx                        ; Return the result of the subtraction
    36.  
    37.                                     ; Tear down stack frame
    38. ; -----------------------------------------------------------------------------------------
    39.  
    40. pop ecx
    41. pop ebx
    42.  
    43. mov esp, ebp
    44. pop ebp
    45.  
    46.  
    47. ret                                 ; Return from this function. Note that the return value resides in EAX
    48.  
    49. value dd 24                         ; Store a 32 Integer at this address. The datatype of this variable would determine
    50.                                     ; what version of the PUSH instruction the assembler would choose.

    The above is a simple assembly function that measures by how much the stack pointer moves after a PUSH instruction. Executing the above would return the 4 which is correct. After this was tested, all my troubles began. I wanted to test the 6A PUSH instruction so I have to try and coerce the assembler to generate it by changing this line:-
    Code:
    push [value]
    I needed to change it to something that would generate 6A PUSH but no matter what I did I could not find a way. I combed through the Flat Assembler documentation(The Flat Assembler is the Assembler I used), I tried all kinds of tricks and nothing worked. I even tried a macro to embed the instruction directly. After failing repeatedly I decided to go looking for answers and after about 45 minutes wrestling with Google, I came across this and this. The long and short of it is that pushing 8 byte values are strongly discouraged, some even suggesting that some platforms prevent it entirely. It was also suggested that even when you can push an 8 bit literal, the processor would just sign extend it to 16/32/64 bits depending of a number of factors. But what seemed to be clear is that there is no way any version of PUSH would simply push an 8 bit value onto the stack and decrement the stack pointer by 1 byte which is consistent with what typically written about PUSH in various documentation. PUSH can only decrement the stack pointer by 16, 32 or 64 bits depending on operand size or address size.

    What all this has led be to conclude is that you were correct in your original assertion, which is that pushing any value onto the stack that is less that 32 bits will sign extend it and push it as a 32 bit number. This still leaves the mystery of what what is actually happening with Krool's code when he tries to pass a literal without converting it to a Long. And as much as I love this kind of stuff and would love nothing more than to keep digging, I've already spent too much time on this today and neglected work that would pay the bills. I have a deadline to meet on a small project and I need to get back to it so I'm gonna stop for now. This was very interesting and fun, not gonna lie but there so much about all this I still do not know. Anyways, I'm definitely gonna come back to this at some point.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  21. #21
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Posts
    2,585

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by Niya View Post
    This still leaves the mystery of what what is actually happening with Krool's code when he tries to pass a literal without converting it to a Long.
    I did some more testing with untyped integral literals

    Code:
        Call CopyMemory(a, b, ByVal 4)
    00401B31 6A 04                push        4  
    
        Call CopyMemory(a, b, ByVal 130)
    00401B31 68 82 00 00 00       push        82h  
    
        Call CopyMemory(a, b, ByVal 35000)
    00401B48 68 B8 88 00 00       push        88B8h  
    
        Call CopyMemory(a, b, ByVal -4)
    00401B5F 6A FC                push        0FFFFFFFCh  
    
        Call CopyMemory(a, b, ByVal -130)
    00401B73 68 7E FF FF FF       push        0FFFFFF7Eh  
    
        Call CopyMemory(a, b, ByVal -35000)
    00401B8A 68 48 77 FF FF       push        0FFFF7748h
    The results show that for the -128 to 127 range the compiler emits 6A push instruction (which sign-extends to dword) and for all other values uses 68 push which operates directly on a dword so everything should work as expected so far as if the VB6 literal is & suffixed without it actually being.

    Another possibility is that the IDE interpreter (incl. p-code compiled executables) uses different approach to literals on stack for a ByVal As Any parameters which can fail on certain conditions.

    cheers,
    </wqw>

  22. #22
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,937

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by wqweto View Post
    The results show that for the -128 to 127 range the compiler emits 6A push instruction
    I actually came across this several times during my research into it. I think most assemblers do this, not just the VB6 compiler.

    Quote Originally Posted by wqweto View Post
    Another possibility is that the IDE interpreter (incl. p-code compiled executables) uses different approach to literals on stack for a ByVal As Any parameters which can fail on certain conditions.
    I have a another explanation I complete forgot to take into account, compiler created marshalling code. I think they are called thunks. When I was first experimenting with self-modifying code, one of the things I remember trying was reading the call stack of caller, that is to say I was trying to read the parameters of the function that called my assembly function. When I attempted this, I found something odd. I can't remember the details of what happened but I do remember concluding that I couldn't see the callers call stack and I stopped to think about why the calling functions parameters aren't where they should be. Then I realized that I was working under the assumption that my VB.Net code was directly calling my assembled code. This was a dumb assumption on my part because I didn't take P/Invoke into account. P/Invoke is the .Net sub-system that among other things, is responsible for marshalling parameters from .Net's managed environment to the external environment whether it be a typical DLL or a COM component. If external functions were being called directly then how would it be possible to perform marshalling. The compiler has to embed code to transform the parameters before actually calling the external function from within that embedded code. VB6 also marshals parameters. Think about what happens when you pass a String by value to a Win32 API function. It has to convert that BSTR into an ANSI null terminated C String which means the VB6 compiler must also embed code between the VB6 call and the actual external function. It is probably inside whatever marshalling code is being inserted that unexpected and unknown things are happening.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  23. #23
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Posts
    2,585

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by Niya View Post
    It is probably inside whatever marshalling code is being inserted that unexpected and unknown things are happening.
    If you think about the compiler generated invisible thunk on CopyMemory declared with As Any parameters it cannot possibly deal with string conversion because the same API declare can be called with ByVal sText and ByVal VarPtr(sText) so the ANSI<->Unicode conversion in the first case has to happen in the callsite as it's callsite specific responsibility that is not happening in the second case

    Another option would be for the compiler to generate two copies of the invisible thunk -- one for ByVal As String calls and one for ByVal As Long and rest of the cases.

    cheers,
    </wqw>

  24. #24
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    6,576

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by Niya View Post
    Quote Originally Posted by Elroy View Post
    EDIT: And, more than anything, I see this as a compiler directive. In other words, it tells the compiler to not be so persnickety about type mismatches when the type is "Any". It's still up to you to make sure these API declarations are declared and used correctly.
    That's not entirely accurate.

    Any is actually a kind of alias for the Long type. This is clear when you try using Any ByVal parameters to pass anything beside Longs. It won't compile:-
    Code:
    '
    Option Explicit
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
                (ByVal lpDest As Any, ByVal lpSource As Any, ByVal cbCopy As Long)
    
    Private Sub Form_Load()
        Dim a As Integer
        Dim b As Integer
        
        a = 102
        
        
        'If these variables were declared as Long
        'instead of Double, this program
        'would compile and work correctly.
        Dim a_ptr As Double
        Dim b_ptr As Double
        
        a_ptr = VarPtr(a)
        b_ptr = VarPtr(b)
        
        'This would not compile because it
        'expects the first two parameters
        'to be longs
        CopyMemory b_ptr, a_ptr, 2
    
        Debug.Print CStr(b)
        
    
    End Sub
    CopyMemory in the above program is declared to expect two Any parameters to be passed by value yet if you tried to pass anything beside Longs, it would not compile citing a Type mismatch error. It is extremely clear that Any is meant to be used to pass 32 bit pointers. It is EXACLTY the same as declaring them as ByVal Long. What makes Any special is that it completely bypasses any type checking when used in combination with ByRef. If you declare the parameters as ByRef Any and you will be able to use the same call to pass the pointer to any type of variable. Variants are a special case. There's no type checking with Variants either but you do get in trouble with implicit conversions. You can refer to my previous post to see what happens when you use Variants instead of Any.
    Hmmm, that's an interesting point. However, I don't think it's quite so simple as being an alias for As Long. Actually, from playing around, I think the ByRef As Any and the ByVal As Any cases must be considered separately. Let's just focus on the ByVal As Any case, which is what you did in your example. And furthermore, let's focus on the following two lines of code:

    Code:
        Dim a_ptr As Double
        Dim b_ptr As Double
    If we change them to the following...

    Code:
        Dim a_ptr As Single
        Dim b_ptr As Single
    ...they'll compile just fine.

    In fact, we can also use As String. IMHO, this is the VB6 p-code compiler trying to do stack management, by only allowing four-byte "things" to be pushed onto the stack for these As Any declarations.

    Now, if we consider the ByRef As Any case, we know that we're always getting four-bytes pushed onto the stack ... the address of the data of whatever data-type we're passing in.

    ---------------------

    Now personally, I see the whole Unicode-to-Ansi string conversion "shim" thing as a separate issue from all of this. Personally, with respect to Declared library calls, I guess I always thought that VB6 saw the outside-world as an ANSI world, converting all out-going strings to ANSI. Why else would we need to jump through the StrPtr wrappers when actually calling a Unicode (...W) function?

    Returning to the ByVal As Any case for a moment, I've got no idea what's happening if we declare those arguments as follows (regardless of whether or not the CopyMemory will work):

    Code:
        Dim a_ptr As String
        Dim b_ptr As String
    Are those strings getting converted to ANSI when they go through the As Any declaration? I've got no idea.

    ---------------------

    Take Care,
    Elroy
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  25. #25
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Posts
    2,585

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by Elroy View Post
    Are those strings getting converted to ANSI when they go through the As Any declaration?
    Not unless called with ByVal sText on a ByRef As Any declaration.

    cheers,
    </wqw>

  26. #26
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,937

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by wqweto View Post
    Not unless called with ByVal sText on a ByRef As Any declaration.

    cheers,
    </wqw>
    Actually, I think he asking what would happen if you try to pass a String through a parameter that is declared as ByVal Any.


    Quote Originally Posted by Elroy View Post
    Are those strings getting converted to ANSI when they go through the As Any declaration? I've got no idea.
    The answer to that is that it will behave as if the parameter were declared ByVal String. It will do a BSTR to ANSI conversion and pass the pointer to that. You can see test for yourself with this code:-
    vb6 Code:
    1. '
    2. Option Explicit
    3. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
    4.             (ByVal lpDest As Any, ByVal lpSource As Any, ByVal cbCopy As Long)
    5.  
    6.  
    7. Private Sub Form_Load()
    8.    
    9.     Dim myString As String
    10.     Dim buffer() As Byte
    11.    
    12.     ReDim buffer(0 To 2)
    13.     myString = "dog"
    14.            
    15.     CopyMemory VarPtr(buffer(0)), myString, 3
    16.    
    17.     'This would write the String to the immediate window
    18.     'showing that the copy was successful
    19.     Debug.Print StrConv(buffer, vbUnicode)
    20. End Sub

    As you can see in the above code lpSource is declared as Any but passed by value and when I passed the String through that, what ended up in the buffer was an ANSI String 3 bytes long suggesting that VB6 actually passed a pointer to an ANSI String after converting it from a BSTR.
    Last edited by Niya; Jan 14th, 2021 at 02:12 PM.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  27. #27
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,937

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by wqweto View Post
    If you think about the compiler generated invisible thunk on CopyMemory declared with As Any parameters it cannot possibly deal with string conversion because the same API declare can be called with ByVal sText and ByVal VarPtr(sText) so the ANSI<->Unicode conversion in the first case has to happen in the callsite as it's callsite specific responsibility that is not happening in the second case

    Another option would be for the compiler to generate two copies of the invisible thunk -- one for ByVal As String calls and one for ByVal As Long and rest of the cases.

    cheers,
    </wqw>
    I imagine what happens is that the compiler looks at the entire source code and look at all the calls to CopyMemory and generates a thunk for each unique call signature. And when the calls are actually made, it matches the signature to it's thunk and compiles code to call that specific think.

    Going back to my experiments in .Net, when I was digging into how P/Invoke works, I found this article which goes into detail about what happens behind the scenes when you make an external call. Now there is quite a bit there than cannot be applied to VB6 since the JIT is a big part of it in .Net and there is no JIT in VB6. However, there are a few principles I can see being applied to the VB6 compiler's approach whatever it may be. Remember, the engineers that designed this are very likely to be the same people that also designed VB6 and it's compiler.

    The strategy used by .Net compilers is to create thunks for each unique signature. These thunks or stubs as they are called, perform marshalling for all external functions with the exact same signature. As revealed toward the end of the article, the JIT compiler puts the address that corresponds to the external function being called in the EAX register before calling the stub. This is how a single stub can be used to call different external functions of identical signatures.

    It's not hard for me to imagine the VB6 compiler uses a similar strategy. In our case, ByVal sText and ByVal VarPtr(sText) would each have their own marshalling thunks since their signatures are different. I know that technically, their signatures are identical since the external function is declared once but it's not hard to imagine that external functions with Any parameters are treated specially. Calls to functions with Any parameters could in theory cause the VB6 compiler to examine the callsite to resolve function signatures instead of just relying on the Declare statements.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  28. #28
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    6,576

    Re: VB6 - can we use the 'Any' type?

    So, I guess the rule is: Any VB6 string that is "directly" (not wrapped in VarPtr or StrPtr) used in a Declared API call is going to get converted to null terminated ANSI, and it doesn't matter if the API is declared As Any or As String ... and it doesn't matter whether it's declared as ByRef or ByVal.
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  29. #29
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    6,576

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by Niya View Post
    I imagine what happens is that the compiler looks at the entire source code and look at all the calls to CopyMemory and generates a thunk for each unique call signature.
    I'm not 100% following what y'all are trying to get sorted, but I certainly don't think that's what's happening. However, I could be proven wrong.

    I do believe that compilers (including VB6) do a bit of optimization. However, overall, I think they're pretty dumb (in the sense that they just compile what's in front of them). If anything, it's the linker that's the smart one, hooking up all the addresses (really just a table though).

    -----

    EDIT: I really don't understand the confusion. Personally, I think the Unicode-to-ANSI conversion is definitely done on the VB6 side (and not on the library side). No source code "scanning" necessary. Everytime the compiler gets to a call to a declared API, it looks to see if there are any strings in it, and, if there are, a "thunk" of code is inserted to convert those strings to ANSI. In the case of As Any, if it's a string in the call, it gets converted. If it's anything else in the call, it doesn't get converted.

    And again, when something like StrPtr(sText) is in an API call, there's no string in the API call. The string is in the call to StrPtr, and not the actual API call.

    Probably, rather than a "thunk" of code, it's just a compiled call to a VB6 library function with the BSTR that makes the conversion.
    Last edited by Elroy; Jan 14th, 2021 at 05:07 PM.
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  30. #30
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,937

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by Elroy View Post
    I do believe that compilers (including VB6) do a bit of optimization. However, overall, I think they're pretty dumb (in the sense that they just compile what's in front of them). If anything, it's the linker that's the smart one, hooking up all the addresses (really just a table though).
    This is actually a very insightful suggestion. There is no reason there can't be standard stubs or thunks that can be statically linked. That can't be ruled out 100%.

    However, there is one huge advantage to having compiler generated thunks, flexibility. Remember, thunks are responsible for the compiler's ability to produce code that can marshal calls. They don't necessarily care what the calls are doing. They only care about being able to marshal the parameters correctly. Because their primary concern are with the parameters, the signatures of the function declarations are important. It isn't possible to write a static thunk for every possible combination of parameter types, parameter orders and parameter counts. The potential number of possible function signatures is limitless. It is far better to be able to read a function's signature and generate a thunk on the go to deal with that particular signature.

    However, there is no reason the code placed inside the thunk itself cannot be statically or dynamically linked to helper functions that deal with marshalling specific data types before calling the external function. For example, the compiler might generate a thunk that calls a BSTRToAnsi library function to help with the conversion of Strings before making the actual call. As a matter of fact this is exactly what .Net's JIT compiler does. The thunk itself is auto generated but the code inside make calls to normal mscorlib methods. These methods are just normal private methods like any user could write themselves and they are dynamically linked, no different to how user written code would be linked.
    Last edited by Niya; Jan 14th, 2021 at 06:52 PM.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  31. #31
    PowerPoster
    Join Date
    Feb 2006
    Posts
    21,764

    Re: VB6 - can we use the 'Any' type?

    I think you guys have missed the point entirely and run down a rabbit hole. The question doesn't seem to be asking about API calls:

    Quote Originally Posted by joaquim View Post
    some functions declarations, from a library('Lib'), can have a 'Any' argument type...
    can we use these type or not? or the 'Object' type is similar?
    I still think he was grasping for the Variant type.

  32. #32
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,937

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by Elroy View Post
    So, I guess the rule is: Any VB6 string that is "directly" (not wrapped in VarPtr or StrPtr) used in a Declared API call is going to get converted to null terminated ANSI, and it doesn't matter if the API is declared As Any or As String ... and it doesn't matter whether it's declared as ByRef or ByVal.
    You're complicating it. The rule is very simple. Any String passed by value is converted to a null terminated ANSI string and a pointer to the ANSI string's buffer is passed. The only things that matter is that it is a String being passed and that it is being passed by value.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  33. #33
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,937

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by dilettante View Post
    I think you guys have missed the point entirely and run down a rabbit hole. The question doesn't seem to be asking about API calls:



    I still think he was grasping for the Variant type.
    No, we settled joaquim's question. We went on this tangent for our own sakes.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  34. #34
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Posts
    2,585

    Re: VB6 - can we use the 'Any' type?

    Is seems the Unicode<->ANSI conversion is happening at the callsite for plain ByVal As String declares and for As Any declares with ByVal sText invokation.

    Code:
        Call CopyMemory(a, b, ByVal sText)
    00401BE1 FF 75 E8             push        dword ptr [sText]  
    00401BE4 8D 45 C0             lea         eax,[unnamed_var1]  
    00401BE7 50                   push        eax  
    00401BE8 E8 9B F5 FF FF       call        ___vbaStrToAnsi (401188h)  
    00401BED 50                   push        eax  
    00401BEE 8D 45 C4             lea         eax,[b]  
    00401BF1 50                   push        eax  
    00401BF2 8D 45 C8             lea         eax,[a]  
    00401BF5 50                   push        eax  
    00401BF6 E8 F9 FC FF FF       call        ___vba@092FFE14 (4018F4h)  
    00401BFB E8 82 F5 FF FF       call        @__vbaSetSystemError (401182h)  
    00401C00 FF 75 C0             push        dword ptr [unnamed_var1]  
    00401C03 8D 45 E8             lea         eax,[sText]  
    00401C06 50                   push        eax  
    00401C07 E8 70 F5 FF FF       call        ___vbaStrToUnicode (40117Ch)  
    00401C0C 8D 4D C0             lea         ecx,[unnamed_var1]  
    00401C0F E8 86 F5 FF FF       call        @__vbaFreeStr (40119Ah)
    So the compiler is pretty simple (and ineffective) about ANSI magic around string parameters -- there are no multiple stubs for a single declare and there are no name mangling or other complications.

    A simple optimization for Unicode<->ANSI conversion that the compiler does when the actual argument is not an LValue is that it skips the backward ANSI->Unicode conversion altogether, i.e. when invoked with ByVal "Literal" or ByVal sText & "suffix" the ___vbaStrToUnicode call is skipped.

    cheers,
    </wqw>

  35. #35
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,937

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by wqweto View Post
    Is seems the Unicode<->ANSI conversion is happening at the callsite for plain ByVal As String declares and for As Any declares with ByVal sText invokation.

    Code:
        Call CopyMemory(a, b, ByVal sText)
    00401BE1 FF 75 E8             push        dword ptr [sText]  
    00401BE4 8D 45 C0             lea         eax,[unnamed_var1]  
    00401BE7 50                   push        eax  
    00401BE8 E8 9B F5 FF FF       call        ___vbaStrToAnsi (401188h)  
    00401BED 50                   push        eax  
    00401BEE 8D 45 C4             lea         eax,[b]  
    00401BF1 50                   push        eax  
    00401BF2 8D 45 C8             lea         eax,[a]  
    00401BF5 50                   push        eax  
    00401BF6 E8 F9 FC FF FF       call        ___vba@092FFE14 (4018F4h)  
    00401BFB E8 82 F5 FF FF       call        @__vbaSetSystemError (401182h)  
    00401C00 FF 75 C0             push        dword ptr [unnamed_var1]  
    00401C03 8D 45 E8             lea         eax,[sText]  
    00401C06 50                   push        eax  
    00401C07 E8 70 F5 FF FF       call        ___vbaStrToUnicode (40117Ch)  
    00401C0C 8D 4D C0             lea         ecx,[unnamed_var1]  
    00401C0F E8 86 F5 FF FF       call        @__vbaFreeStr (40119Ah)
    So the compiler is pretty simple (and ineffective) about ANSI magic around string parameters -- there are no multiple stubs for a single declare and there are no name mangling or other complications.

    A simple optimization for Unicode<->ANSI conversion that the compiler does when the actual argument is not an LValue is that it skips the backward ANSI->Unicode conversion altogether, i.e. when invoked with ByVal "Literal" or ByVal sText & "suffix" the ___vbaStrToUnicode call is skipped.

    cheers,
    </wqw>
    Whoa....I didn't see that one coming. The compiler really is very simple. Elroy was correct. I guess this settles the question of how marshalling works in VB6. It marshals at the callsite of every invocation. A thunk or stub could have been more efficient but I guess they had their reasons and they would certainly know better than me on these matters.

    This entire discussion began with Krool's post. Do we know enough now to correctly answer what was happening in his case?

    To recap he said:-
    I experienced the problem in comctl32.dll that a "ByVal -1" got interpret as -2321345 (example value) and doing a "ByVal -1&" fixed the issue. (was the TCM_SETMINTABWIDTH where SendMessage is "As Any")
    And we have established that ByVal -1 would sign extend it to 32 bits which means it should have worked whether he passed -1 as an Integer or as a Long. But it didn't. We should be able to answer this by now, right? We really fleshed all of this out so I must be missing something.
    Last edited by Niya; Jan 15th, 2021 at 04:20 AM.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  36. #36
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Posts
    2,585

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by Niya View Post
    And we have established that ByVal -1 would sign extend it to 32 bits which means it should have worked whether he passed -1 as an Integer or as a Long. But it didn't. We should be able to answer this by now, right? We really fleshed all of this out so I must be missing something.
    I'm positive ByVal <intLiteral> cannot fail for As Any parameter in compiled applications whether 6A or 68 push instruction is emitted.

    It must have been some other kind of actual argument used -- a variable, a (mis)typed constant, a complex expression, a function/method call.

    cheers,
    </wqw>

  37. #37
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,937

    Re: VB6 - can we use the 'Any' type?

    Quote Originally Posted by wqweto View Post
    I'm positive ByVal <intLiteral> cannot fail for As Any parameter in compiled applications whether 6A or 68 push instruction is emitted.

    It must have been some other kind of actual argument used -- a variable, a (mis)typed constant, a complex expression, a function/method call.

    cheers,
    </wqw>
    Well I guess that answer is acceptable since we've pretty much eliminated any other possibility.

    This was fun and very interesting. Hope to do it again some time.

    Cheers
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  38. #38

Posting Permissions

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



Click Here to Expand Forum to Full Width