VB.Net - Converting multi-byte hex values to decimal - vb.net

How would I convert the two individual byte values (in hex) to a decimal value representing the concatenated hex values? For example, if I have the
Dim byte1 As Byte = &H99
Dim byte2 As Byte = &H99
' I want the decimal representation of the hex value "9999"
' (byte1.ToString + byte2.ToString) which should be "39321"
Currently I use the following code:
Dim decVal as integer
decVal = Val("&H" + Hex$(byte1).ToString + Hex$(byte2).ToString)
However, when I use this, the value (decVal) comes out to "-26215"
What am I doing wrong in the conversion here?

Try the following:
decVal = byte1 * 256 + byte2
Your problem is when you call Val("&H9999"), if you use CInt("&H9999") or Integer.Parse("9999", System.Globalization.NumberStyles.HexNumber) you would get the correct answer (in this case at least)
If you look at the output from:
decVal = Val("&H9999")
Console.WriteLine(decVal.ToString("X"))
you get FFFF9999
I'm not sure why this is happening, but I would see this as a reason not to use the Val function to parse hexadecimal strings

#Patrick McDonald has a good inline way of doing it and if that's what you're looking for then I recommend using that. But I can't resist not giving you the overly complicated but cross-platform and expanded versions. One thing to note, you are asking to convert bytes in big-endian mode which is a very important topic to understand if you're playing with bytes.
Both functions below take a param array and perform conversions but in two different ways. One handles an arbitrarily large byte array (bound only by platform restrictions) and the other uses the built-in converter class that reads byte arrays.
Once again, let me stress the "overly complicated" part.
Private Shared Function ConvertBytes2(ByVal ParamArray bytes() As Byte) As UInt32
''//Reverse the array order
Dim NewBytes = bytes.Reverse().ToList().ToArray()
''//Our return value
Dim Dec As UInt32 = 0
''//Temporary value for bit shifting
Dim T As UInt32
''//Loop through the bytes from left to right
For I = (NewBytes.Count - 1) To 0 Step -1
''//Grab the byte
T = NewBytes(I)
''//Shift it and add to our return value
Dec += T << (8 * I)
Next
Return Dec
End Function
Private Shared Function ConvertBytes1(ByVal ParamArray bytes() As Byte) As UInt32
''//We want to read the bytes in big-endian order but BitConverter works in little-endian mode on most Windows systems (but not all) so convert if needed
Dim NewBytes() As Byte
If BitConverter.IsLittleEndian Then
NewBytes = bytes.Reverse().ToList().ToArray()
Else
NewBytes = bytes
End If
''//Our return value
Dim Dec As UInt32
''//BitConverter can return UIn16, 32 or 64, we're only supporting 16 and 32 below
If NewBytes.Count = 2 Then
Dec = BitConverter.ToUInt16(NewBytes, 0)
ElseIf NewBytes.Count = 4 Then
Dec = BitConverter.ToUInt32(NewBytes, 0)
Else
''//Invalid number of bytes sent
Throw New ArgumentOutOfRangeException("bytes")
End If
Return Dec
End Function
Dim Byte1 As Byte = &HA
Dim Byte2 As Byte = &H99
''//&h0A99 = 2,713
Trace.WriteLine(ConvertBytes1(Byte1, Byte2))
Trace.WriteLine(ConvertBytes2(Byte1, Byte2))

Related

How can I read a specific byte in a byte array in VB.NET?

I have a binary file being loaded into a Byte array using Dim settingsBinary As Byte() = My.Computer.FileSystem.ReadAllBytes(settingsFile) where settingsFile is the path to my binary file.
Let's say that my binary file has got three bytes and I want to read the first byte as a Boolean (00 = False, 01 = True). How can I get those bytes? I have tried to use this question's answer to read those three bytes, but I can't wrap my head around it.
Just to clarify, I need to get the three bytes separately: Get the first byte, then set CheckBox1.Checked to the first byte, and so on with the other bytes.
A byte array works just like any other array: You can use an indexer to access a specific element.
Dim firstByte As Byte = settingsBinary(0)
Dim secondByte As Byte = settingsBinary(1)
Dim thirdByte As Byte = settingsBinary(2)
And then you can convert the byte into a boolean:
Dim firstBoolean As Boolean
Select Case firstByte
Case 0
firstBoolean = False
Case 1
firstBoolean = True
Case Else
Throw New Exception("Invalid first byte in settings file: " & firstByte)
End Select
(Generalizing the conversion logic into a method that can be used for all three bytes is left as an exercise to the reader.)

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

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

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.

Displaying the ASCII Table Number for each item in a byte array

I am writing a Message builder and in it I have to send a byte array to a remote machine requesting data.
I have an 8 bit number that counts how many items are being requested (count) and am trying to get the number inserted into a byte array.
The Byte array locations are in positions 70 & 71 of the array and in little endian order.
I am trying to just convert the byte and then insert the converted bytes with the following code.
Dim count As Byte = 3
Dim countbyteArray As Byte() = BitConverter.GetBytes(count)
sendBytes(70) = (countbyteArray(0))
sendBytes(71) = (countbyteArray(1))
when I do this for anything greater than 1 it does not get accepted by the device I am sending the byte array to.
Am I in the right ball Park or completely wrong as I am not completely up to speed with Bytes and Bits. Also any example or explanation would be greatly appreciated.
Little endian is the natural order in .NET so it is easy to work with.
As for a message builder, wouldn't it be easier to say
Dim message = New Message With { .Command = Command.Request, .Device = &hF, .Count = 8, .Filter = &h1234 }
Dim bytes = message.GetBytes()
' Hex for debugging
Dim hex = BitConverter.ToString(bytes)
You just have to layout the structure of the message. One way is with LayoutKind.Explicit. I'd probably choose the LayoutKind that most closely aligns with the device documentation.
Public Enum Command As Byte
Reset = 0
Request = 1
End Enum
<StructLayout(LayoutKind.Explicit)>
Public Structure Message
<FieldOffset(0)> Public Command As Command
<FieldOffset(1)> Public Device As Byte
' unused bytes default to 0
<FieldOffset(10)> Public Count As UInt16
<FieldOffset(12)> Public Filter As Int16
Public Function GetBytes() As Byte()
Dim rawData(0 To Marshal.SizeOf(Me) - 1) As Byte
Dim handle = GCHandle.Alloc(rawData, GCHandleType.Pinned)
Try
Dim rawDataPtr = handle.AddrOfPinnedObject()
Marshal.StructureToPtr(Me, rawDataPtr, False)
Return rawData
Finally
handle.Free()
End Try
End Function
End Structure
'TODO define enums, etc to make it easier for caller.
'TODO update struct per device documentation

Convert string from Base2 to Base4

I wish to convert text to base 4 (AGCT), by first converting it to binary (I've done this bit) and then break it into 2 bit pairs.
can someone help me turn this into code using vb.net syntax?
if (length of binary String is an odd number) add a zero to the front (leftmost position) of the String. Create an empty String to add translated digits to. While the original String of binary is not empty { Translate the first two digits only of the binary String into a base-4 digit, and add this digit to the end (rightmost) index of the new String. After this, remove the same two digits from the binary string and repeat if it is not empty. }
in this context:
Dim Base2Convert As String = ""
For Each C As Char In Result.Text
Dim s As String = System.Convert.ToString(AscW(C), 2).PadLeft(8, "0")
Base2Convert &= s
Next
Result.Text = Base2Convert
Dim Base4Convert As String = ""
For Each C As Char In Result.Text
'//<ADD THE STATEMENT ABOVE AS CODE HERE>//
Base4Convert &= s
Next
Result.Text = Base4Convert
.NET does not support conversion to non-standard base, such as 4, so this will not work:
Dim base4number As String = Convert.ToString(base10number, 4)
From MSDN:
[...] base of the return value [...] must be 2, 8, 10, or 16.
But you can write your own conversion function, or take the existing one off the web:
Public Function IntToStringFast(value As Integer, baseChars As Char()) As String
Dim i As Integer = 32
Dim buffer(i - 1) As Char
Dim targetBase As Integer = baseChars.Length
Do
buffer(System.Threading.Interlocked.Decrement(i)) =
baseChars(value Mod targetBase)
value = value \ targetBase
Loop While value > 0
Dim result As Char() = New Char(32 - i - 1) {}
Array.Copy(buffer, i, result, 0, 32 - i)
Return New String(result)
End Function
Used this answer. Converted with developer fusion from C# + minor adjustments. Example:
Dim base2number As String = "11110" 'Decimal 30
Dim base10number As Integer = Convert.ToInt32(base2number, 2)
Dim base4number As String = IntToStringFast(base10number, "0123")
Console.WriteLine(base4number) 'outputs 132
Notice that you don't need base 2 there as an intermediate value, you can convert directly from base 10. If in doubt, whether it worked correctly or not, here is a useful resource:
Number base converter
Converting the number to base first and then to base 4 doesn’t make a lot of sense, since directly converting to base 4 is the same algorithm anyway. In fact, representation of a number in any base requires the same general algorithm:
Public Shared Function Representation(number As Integer, digits As String) As String
Dim result = ""
Dim b = digits.Length
Do
result = digits(number Mod b) & result
number \= b
Loop While number > 0
Return result
End Function
Now you can verify that Representation(i, decimal) does the same as i.ToString():
Dim decimalDigits = "0123456789"
For i = 0 To 30 Step 3
Console.WriteLine("{0}, {1}", i.ToString(), Representation(i, decimalDigits))
Next
It’s worth noting that i.ToString() converts i to decimal base because this is the base we, humans, are mostly using. But there is nothing special about decimal, and in fact internally, i is not a decimal number: its representation in computer memory is binary.
For conversion to any other base, just pass a different set of digits to the method. In your case, that’d be "ACGT":
Console.WriteLine(Representation(i, "ACGT"))
Hexadecimal also works:
Console.WriteLine(Representation(i, "0123456789ABCDEF"))
And, just to repeat it because it’s such a nice mathematical property: so does any other base with at least two distinct digits.