Is there a VB function to convert 32 bit float to little endian hex - vba

As shown here: https://gregstoll.com/~gregstoll/floattohex/
I need to convert a 32 bit float to a little endian hex (click the swap endiness button before converting). I've managed to do this in python by converting to big endian then reordering, but I have no idea how to approach this issue in VB as I'm entirely new to the language. Using the Hex inbuilt function returns 19a, which i assume means its not correctly evaluating my input as a single.
I've found a recommended solution here but cant get it working:
https://www.tek-tips.com/faqs.cfm?fid=6404
Any suggestions would be great, thanks in advance.

There are a number of ways you could do this - the most obvious being the Copy Memory API. Some time ago, a pretty neat solution was published here: Extracting bits from a float in vba which avoided the need for the API
Basically, you'd just need a couple of short functions:
Option Explicit
Type SingleType
Value As Single
End Type
Type FourBytesType
Value(3) As Byte
End Type
Private Function SingleToBytes(f As Single) As Variant
Dim sngType As SingleType
Dim bytesType As FourBytesType
sngType.Value = f
LSet bytesType = sngType
SingleToBytes = bytesType.Value
End Function
Private Function BytesToHex(bytes As Variant) As String
Dim result As String
Dim i As Long
For i = LBound(bytes) To UBound(bytes)
result = result & IIf(bytes(i) < 16, "0", "") & Hex(bytes(i))
Next
BytesToHex = result
End Function
If you wanted to test Endianness and reverse the array, then something like the following, which kind of uses a Byte Order Mark, could be added. I haven't tested it on a big-endian processor but I think it'd work:
Private Function IsLittleEndianProcessor() As Boolean
Const BOM As Single = 1
Const MSB As Byte = 63
Dim bytes() As Byte
Dim n As Long
bytes = SingleToBytes(BOM)
n = UBound(bytes)
IsLittleEndianProcessor = (bytes(n) = MSB)
End Function
Private Function ChangeEndianness(bytes As Variant) As Variant
Dim result() As Byte
Dim n As Long, m As Long
ReDim result(UBound(bytes))
m = UBound(bytes)
For n = LBound(bytes) To UBound(bytes)
result(m) = bytes(n)
m = m - 1
Next
ChangeEndianness = result
End Function
I'm not actually sure how you want the hex string displayed but you could step backwards through the array to write the hex if needed. Sample test would be:
Public Sub TestMe()
Dim bytes As Variant
Dim output As String
bytes = SingleToBytes(3.1415)
If Not IsLittleEndianProcessor Then
bytes = ChangeEndianness(bytes)
End If
output = BytesToHex(bytes)
Debug.Print output
End Sub

Related

Assign a string to a byte array in a VBA user-defined type

I work with a relay module that I normaly connect via USB. That all works perfectly. Now I would like to connect it via the network. All manufacturer's VB.NET code works in vba except for accessing this module over the network.
Public Declare PtrSafe Function DapiOpenModuleEx Lib "DELIB64" (ByVal moduleID As Long, ByVal nr As Long, ByRef exbuffer As DAPI_OPENMODULEEX_STRUCT, ByVal open_options As Long) As Long
' Definitions for DapiOpenEx
Public Type DAPI_OPENMODULEEX_STRUCT
address(255) As Byte
timeout As Long
portno As Long
encryption_type As Long
encryption_password(31) As Byte
End Type
'Open ETH-Module with parameter
Dim handle as Ulong
Dim MyModuleID As UInt32
MyModuleID = 42
Dim open_buffer As New DELib64.DAPI_OPENMODULEEX_STRUCT
open_buffer.address = System.String.Copy(192.168.1.1 As String) As String
open_buffer.portno = 0
handle = DELib.DapiOpenModuleEx(MyModuleID, 0, open_buffer)
I am getting an error "open_buffer.address = System.String.Copy(192.168.1.1 As String) As String "
Can someone help me with what i need to change here?
Dim handle as LongLong
Dim MyModuleID As Long
MyModuleID = 42
Dim open_buffer As Delib64.DAPI_OPENMODULEEX_STRUCT
open_buffer.address = system.String.Copy("192.168.1.1" AS String) As String
open_buffer.portno = 0
handle1 = DapiOpenModuleEx(MyModuleID, 0, open_buffer, 0)
According to your comment, the original line of code is
strcpy((char*) open_buffer.address, "192.168.1.10");
So you need to copy the ASCII (single-byte) string "192.168.1.10" into a VBA byte array. This is surprisingly hard, since the obvious approach of open_buffer.address = StrConv("192.168.1.10", vbFromUnicode) won't work (you can't assign to a fixed-size array that's part of a type).
One obvious solution would be to make a Windows API call to CopyMemory, but if we want a VBA-only solution, a simple loop should suffice:
Dim i As Long
Dim b() As Byte
b = StrConv("192.168.1.10", vbFromUnicode)
For i = 0 To UBound(b)
open_buffer.address(i) = b(i)
Next
open_buffer.address(UBound(b) + 1) = 0 ' C-strings need to be 0-terminated
(I do have the feeling that this should be easier, so I'll gladly upvote competing, simpler answers.)

Get first three bits from a byte

My question is simple, i need to convert the first three bits of a byte to a integer (Enum) value. However the things i try i always get a 0 back. This is wat the document says:
Bit 0-3: These bits indicate the connection state.
Value 0 to 14 indicate the connection state. Value 3 = Connected
Now the response i get (from a serial device) is a encoded byte stream of ASCII hex values, so i first need to convert it from a hex string to byte array and then get the bits from it. This is my code so far:
Dim strResonse As String = "0C03" 'This should result in a connection state value of 3
Dim ConState(2) As Byte
ConState = HexStringToByteArray(strResonse)
Dim int As Integer = ConState(1) << 1 And ConState(1) << 2 And ConState(1) << 3
Debug.Print(int.ToString)
Private Function HexStringToByteArray(ByVal shex As String) As Byte()
Dim B As Byte() = Enumerable.Range(0, shex.Length).Where(Function(x) x Mod 2 = 0).[Select](Function(x) Convert.ToByte(shex.Substring(x, 2), 16)).ToArray()
Return Enumerable.Range(0, shex.Length).Where(Function(x) x Mod 2 = 0).[Select](Function(x) Convert.ToByte(shex.Substring(x, 2), 16)).ToArray()
End Function
It would be easier to use bit operation
Dim connectionState As Integer
Dim response As Integer = &HC03
' Get the first 4 bits. 15 in binary is 1111
connectionState = response And 15
If your input is really a string, there a built-in way to convert to integer.
Dim response As Integer = Convert.ToInt32("C03", 16)
If you really want to get an array, I suggest you use the built in method.
Dim allBits As Byte() = BitConverter.GetBytes(response)
There also the BitArray class that can be handy.

Parsing binary to BigInteger in VB .NET?

How are you?
I wrote a program manipulating big binary chains (string variables). This said manipulation requires me to store my chains in a variable so I can use them as numbers. The only variable type that I have found big enough to store such lengthy numbers is BigInteger (we are talking 1.0E100+).
I would like to use something like:
val = BigInteger.Parse(bin, 2)
But the second parameter needed is a NumberStyles object, which can only refer to a NumberStyles.HexNumber.
Is there a simple/optimal way to do this?
Thank you very much. :)
This converts a binary string to BigInteger in 8 bit chunks. It assumes that the binary string represents a positive number.
Private Function BinToBI(ByRef binstr As String) As BigInteger
Dim t As New List(Of Byte)
Dim s As String
Dim idx As Integer = binstr.Length
Do While idx > 0
'get 8 bits
If idx >= 8 Then
s = binstr.Substring(idx - 8, 8)
Else
s = binstr.Substring(0, idx).PadLeft(8, "0"c)
End If
'convert to byte and add to list
Dim b As Byte = Convert.ToByte(s, 2)
t.Add(b)
idx -= 8
Loop
'force to positive
If t(t.Count - 1) >= 128 Then
t.Add(0)
End If
Dim rv As New BigInteger(t.ToArray)
Return rv
End Function
for testing
Dim d As Double = 1.0E+101
Debug.WriteLine(d.ToString("n2"))
Dim bi As BigInteger
' Dim bin As String = "1111111111111111111111111111111" 'Integer.MaxValue
' Dim bin As String = "111111111111111111111111111111111111111111111111111111111111111" 'Long.MaxValue
Dim bin As String = "1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110"
bi = BinToBI(bin)
Debug.WriteLine(bi.ToString("n2"))
This was not well tested but should give you some ideas.

How to replace bytes in VB.NET?

I have two strings:
Dim Original_Hex_Bytes as string = "616572646E61"
Dim Patched_Hex_Bytes as string = "616E64726561"
Then I have a binary file and I need to search for the Original_Hex_Bytes and replace them with Patched_Hex_Bytes; I don't konw the offset where begin to write new bytes :(
How can I do this?
If needed, I also know how to convert Hex strings in bytes, I use this:
Private Function Hex_To_Bytes(ByVal strinput As String) As Byte()
Dim i As Integer = 0
Dim x As Integer = 0
Dim bytes(strinput.Length / 2) As Byte
Do While (strinput.Length > i + 1)
Dim lngDecimal As Long = Convert.ToInt32(strinput.Substring(i, 2), 16)
bytes(x) = Convert.ToByte(lngDecimal)
i += 2
x += 1
Loop
Return bytes
End Function
You can use BinaryReader and BinaryWriter classes to achieve this.
But in this case, as you do not know the file structure, need to read the entire file and sweep it in search of bytes array and will be easier to use ASCII strings as aerdna and andrea.
When you know the structure of a file is more appropriate to work with data structure to manipulate its contents.

Mixed Encoding to String

I have a string in VB.net that may contain something like the following:
This is a 0x000020AC symbol
This is the UTF-32 encoding for the Euro Symbol according to this article http://www.fileformat.info/info/unicode/char/20ac/index.htm
I'd like to convert this into
This is a € symbol
I've tried using UnicodeEncoding() class in VB.net (Framework 2.0, as I'm modifying a legacy application)
When I use this class to encode, and then decode I still get back the original string.
I expected that the UnicodeEncoding would recognise the already encoded part and not encode it against. But it appears to not be the case.
I'm a little lost now as to how I can convert a mixed encoded string into a normal string.
Background: When saving an Excel spreadsheet as CSV, anything outside of the ascii range gets converted to ?. So my idea is that if I can get my client to search/replace a few characters, such as the Euro symbol, into an encoded string such as 0x000020AC. Then I was hoping to convert those encoded parts back into the real symbols before I insert to a SQL database.
I've tried a function such as
Public Function Decode(ByVal s As String) As String
Dim uni As New UnicodeEncoding()
Dim encodedBytes As Byte() = uni.GetBytes(s)
Dim output As String = ""
output = uni.GetString(encodedBytes)
Return output
End Function
Which was based on the examples on the MSDN at http://msdn.microsoft.com/en-us/library/system.text.unicodeencoding.aspx
It could be that I have a complete mis-understanding of how this works in VB.net. In C# I can simply use escaped characters such as "\u20AC". But no such thing exists in VB.net.
Based on advice from Heinzi I implemented a Regex.Replace method using the following code, this appear to work for my examples.
Public Function Decode(ByVal s As String) As String
Dim output As String = ""
Dim sRegex As String = "0x[0-9a-zA-Z]{8}"
Dim r As Regex = New Regex(sRegex)
Dim myEvaluator As MatchEvaluator = New MatchEvaluator(AddressOf HexToString)
output = r.Replace(s, myEvaluator)
Return output
End Function
Public Function HexToString(ByVal hexString As Match) As String
Dim uni As New UnicodeEncoding(True, True)
Dim input As String = hexString.ToString
input = input.Substring(2)
input = input.TrimStart("0"c)
Dim output As String
Dim length As Integer = input.Length
Dim upperBound As Integer = length \ 2
If length Mod 2 = 0 Then
upperBound -= 1
Else
input = "0" & input
End If
Dim bytes(upperBound) As Byte
For i As Integer = 0 To upperBound
bytes(i) = Convert.ToByte(input.Substring(i * 2, 2), 16)
Next
output = uni.GetString(bytes)
Return output
End Function
Have you tried:
Public Function Decode(Byval Coded as string) as string
Return StrConv(Coded, vbUnicode)
End Function
Also, your function is invalid. It takes s as an argument, does a load of stuff and then outputs the s that was put into it instead of the stuff that was processed within it.