Introduction
I was recently asked to write to an API implemented using COM. The publisher of the API has only limited support for languages other than VB and encourages developers to use VB. Writing to their COM interface is simple from VB, but the application I wrote must also connect to a TCP server, read binary messages from the server, then translate those messages into commands that are relayed to COM API. After an exhaustive search for a simple way to convert the binary messages into consumable data, I created a class to handle the conversion, which is presented in this tip.
Background
I have been using C# for the past several years and am spoiled, I guess, by the built-in BitConverter
class and the BinaryWriter
and BinaryReader
classes. I wanted the solution to be as clean and simple as possible. I could have used the Windows API to copy memory between variables and I actually started out in that direction. However, not long into the process, I decided it was too messy. I took it upon myself as a learning excercise to figure out how to do it using pure VB.
Using the Code
The source code contains two classes. One is the main form that is just used as a spring board. The binary conversion occurs in the second class: BitConverter
. This class contains several private
user-defined types and several public
functions. These functions either convert from a byte array to a simple type (Integer
, Word
, etc.) or they convert from a simple type to a byte array. These functions include a starting position as an argument so that a single buffer can be used to store several pieces of data.
Here is an excerpt from the BitConverter
class used to convert between an array of bytes and integers:
Public Function GetInteger(b() As Byte, start As Integer) As Integer
Dim bytes As TwoBytes
CopyArray b, start, bytes.b, 0, 2
Dim v As IntegerValue
LSet v = bytes
GetInteger = v.value
End Function
Public Sub SetInteger(b() As Byte, start As Integer, value As Integer)
Dim v As IntegerValue
v.value = value
Dim bytes As TwoBytes
LSet bytes = v
CopyArray bytes.b, 0, b, start, 2
End Sub
In the code above, CopyArray
is a Private Sub
in the BitConverter
class. There are also two user-defined types referenced: TwoBytes
and IntegerValue
. You can also see that LSet
is used to transfer the data from one UDT instance to another.
GetInteger
uses two bytes in the input buffer b
starting at start
to reconstitute, or deserialize, an Integer
that it returns as its result.
SetInteger
serializes the input value
and places its equivalent two bytes into the destination byte array b
at start
.
No array bound checking is performed inside the BitConverter
module. The library could be expanded to perform this operation. However, since that would slow things down, I left it to the user to either perform these checks or to trust his/her code to not violate the array boundaries. There are also a few more built-in data types that could be supported by the BitConverter
. I did not need them, so I left their implementation to those who do.
Points of Interest
It has been so long since I used VB that I forgot about the limited number of built-in data types. The messages I receive from my TCP server contain some unsigned four byte integers, which are not supported in VB. So, I will need to do some checking to make sure the data I get is non-negative and discard any that is. I suspect that using an unsigned integer was originally intended to prevent the setting of negative data and not to extend the positive range of the variable. If this is the case (I need to confirm this, of course), then I will not need to discard any messages.
History
- Original tip submitted on 8/16/2014