For the most part we're already set for converting to/from number bases in VB6. But sometimes people want something more.
Here is a little bit more based upon two API calls:
RtlInt64ToUnicodeString()
RtlUnicodeStringToInteger()
Bases:
Binary (2)
Octal (8)
Decimal (10)
Hex (16)
This wrapper code supports a number of numeric data types.
FromString():
Byte
Integer
Long
Single
ToString():
Byte
Integer
Long
Single
Double
Date
Currency
ToString() will optionally pad with 0's on the left to fill out the full precision of the input numeric data type (32 bits for Long, etc.). By default leading 0's are suppressed.
FromString() can be told what base to convert explicitly, or the input text can use a leading prefix (like "0x" for Hex) to indicate the base. An optional + or - sign can also be used in the input text.
------------------ FromString ----------------------
"-123" In Default -123
"0b1111" In Default 15
" +1111" In Binary 15
"-b" In Hex -11
------------------- ToString -----------------------
From Byte:
254 Out Default 254
254 Out Dec 254
254 Out Hex FE
254 Out Binary 11111110
From Integer:
32767 Out Dec 32767
32767 Out Hex 7FFF
32767 Out Binary 111111111111111
From Long:
32767 Out Dec 32767
32767 Out Oct pad 00000077777
32767 Out Hex pad 00007FFF
32767 Out Binary pad 00000000000000000111111111111111
-65535 Out Dec 4294901761
-65535 Out Hex FFFF0001
-65535 Out Binary 11111111111111110000000000000001
From Double:
1.123 Out Dec 4607736361554183979
1.123 Out Hex 3FF1F7CED916872B
1.123 Out Binary 11111111110001111101111100111011011001000101101000011100101011
From Currency:
0.0254 Out Dec 254
0.0254 Out Hex FE
0.0254 Out Binary pad 0000000000000000000000000000000000000000000000000000000011111110
You could easily write your own wrappers for these API calls if you don't like these.
There does seem to be a RtlUnicodeStringToInt64() function (at least in Windows 10). This would provide more symmetry but as far as I can tell it is only available to kernel-mode code such as some drivers.
This is pretty cool. I've written pieces of this in the past, but never gathered it all into one function. Also, just perusing the code, I had a couple of thoughts. I primarily focused on the ToString function:
You're passing in a Variant for your value, but you don't seem to be checking the ByRef/ByVal flag of the variant. If it's ByRef, it doesn't seem that things would work correctly.
The way it is, I'm not sure why you couldn't add LongLong to the list. Also, these only work ByVal (as sitting in a variant) so that issue is obviated.
With just a bit of work (on how the CopyMemory is performed), it seems that you could also accommodate the Decimal type.
----------
Ok, another one. I didn't include it in the bullet-list because it's a bit complex. As it is, you're getting Hex (or Octal, or whatever) representations for integers (Long, Integer, Byte). But that's not at all what you're getting when anything floating point (Double, Single, Currency) is supplied. Basically, it's just treating the memory representation of these floating point numbers as integers, and then converting them to the base you've specified.
But, there actually are Hex representations of floating point numbers. Using the &H... notation, VB6 has never done it. But I believe almost all C, C++, and C# compilers will do it with the 0x... notation. I suspect you're familiar with this, but here's a link in case you're not. Basically, the secret is in the "p" separator, with everything after the "p" denoting a base-2 exponent for moving the decimal-point. (It's a quasi-scientific-notation.) Since you actually specify the base format for both FromString and ToString (and don't use the &H), you could actually use this same convention for the other basis.
This hex (etc) floating-point representation of a number is something I've wished for in the past.
----------
Ok, one more. It's clearly possible to just forget the "p" notation and just report the full floating-point number in the other bases. For Single (and I believe Double as well), I've done this to Binary. However, I see no problem with doing it to Hex and Octal as well. Basically, our Format$ function already does it for us to base-10.
----------
Take Care,
Elroy
Last edited by Elroy; Sep 28th, 2020 at 03:11 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. To all, peace and happiness.
Doesn't ByVal Value As Variant always pass a ByVal Variant? I'd expect that even if passing in a String Variant.
The wrapper functions I wrote have their limitations, and I don't consider them perfect by any means. I really just meant to show that these two RTL functions are readily callable from VB6 and what they do. Stuff like adding left-zero padding was just an example of possible added value in wrapper functions.
I just found these in an old program and realized that searches turned up no examples of use in VB. So posting them along with a bunch of key words and phrases (like "to hex" and "to binary" and "from octal" as well as byte, long, and integer) might help this turn up in future searches.
I don't have a lot of uses for these myself. That old program was only using them because it processed some delimited text input that had numeric columns containing a mix of decimal values and "0x" prefixed hex values with an optional sign.
Doesn't ByVal Value As Variant always pass a ByVal Variant? I'd expect that even if passing in a String Variant.
Hmmm, ok, I wasn't talking about how the Variant was passed. I suspect it makes little difference whether it was passed ByVal or ByRef. However, within the Variant's 16 bytes, there's another ByRef/ByVal flag. It's the &H4000 bit of the "Type" (first) two-bytes. If it's set, the Variant is a ByRef Variant. If not, it's a ByVal Variant.
Yes, most Variants are ByVal, especially those that deal with the intrinsic numeric types. However, that's not mandated. It's entirely possible to have a Variant with an address pointer (i.e., ByRef) with the vbSingle (4) (or other numeric) type in the second-byte of the Variant. When this is done, the Variant contains an address to the data.
And, you mentioned a String Variant. Regardless of how the Variant variable is actually passed, these Variants with Strings are basically always ByRef. They've got to be, or we'd be limiting strings to 14 bytes (7 characters).
Last edited by Elroy; Sep 28th, 2020 at 03:53 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. To all, peace and happiness.
Yes of course a String is always indirect. But passing a String Variant ByVal makes a copy by definition.
I just don't think this is an issue for numeric types passed ByVal As Variant. If you want to go to the trouble of including code to handle that possibility feel free, I doubt your extra code will ever get used though.
Do you have concrete examples that result in the VT_BYREF bit being set?
It's just something I thought of when reading your code. I thought I had seen the VB_BYREF flag set in the past on intrinsic numeric types, but I'm not sure I'm up for trying to make an example. I certainly don't have anything handy.
Also, I suspect you're right in that it's an esoteric situation. Also, I now see that you're just playing around with RtlInt64ToUnicodeString and RtlUnicodeStringToInteger, showing possible uses for them. So, I'll leave it at that.
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. To all, peace and happiness.
ByVal Value As Variant parameters have never VT_BYREF set. Dim Value As Variant too.
In VB it would have been cumbersome to have Public Function ToString(ByRef Value As VariantArg, ...) explicitly used, just to be reminded that VT_BYREF might be set when caller uses a variable for the actual argument.
ByVal Value As Variant parameters have never VT_BYREF set. Dim Value As Variant too.
In VB it would have been cumbersome to have Public Function ToString(ByRef Value As VariantArg, ...) explicitly used, just to be reminded that VT_BYREF might be set when caller uses a variable for the actual argument.
cheers,
</wqw>
A byte array passed to a Sub which has ByRef as Variant can have the VT_BYREF flag.
Here's one of the scenarios I was imagining. When you have a Variant as an incoming argument, that particular Variant argument declaration is often used so that many different data types can be passed in. And that's specifically the case for the way dilettante used it.
However, when this is done, there's nothing to stop you from actually setting up a Variant for the call before the call is actually made, and actually passing a Variant type into the function. And furthermore, in that case, we may have no idea where that Variant came from, or whether or not it has VT_BYREF set. In that case, I would think it'd make little difference as to whether the Variant was passed ByVal or ByRef, but I'm not 100% sure about that. I would think ... if that Variant is passed ByRef, when we get into the function, we're just using the same variable.
And, I would think ... if that Variant is passed ByVal, the original 16 bytes (VT_BYREF and all) are copied and placed on the stack to be "caught" by the function. And, the fact that it's VT_BYREF, it'd still use the same data, even though it was passed ByVal. However, again, I'm not entirely certain about that. And wqweto (in post #10) seems to suggest that I'm wrong about that.
ByVal Value As Variant parameters have never VT_BYREF set. Dim Value As Variant too. [emphasis added]
If that's truly the case, then the way dilettante is doing it would probably work just fine, as he did specify that the Variants be pass as ByVal. I am a bit surprised though that, whether you pass ByVal or ByRef potentially tampers with the VT_BYREF flag.
-----
Also, all of this seems to be getting away from Dil's original intent for this thread. But this stuff is good to know, at least form my own edification.
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. To all, peace and happiness.