A short kbmMW history lesson
kbmMW continues to include features and functionality which may be somewhat outside the scope of being a middle ware in its original meaning, where kbmMW provided the framework for making different processes interact with each other in a simple, reusable way.
Before v5, kbmMW focused on being a middleware between:
- Process and databases
- Servers and clients
In other way a fairly traditional sense of being a middleware.
Since v5 kbmMW has focused on extending the middleware meaning inwards into the applications themselves, providing middleware features in process event and data coordination.
Latest release of kbmMW included a new unit, kbmMWHID, which provided features for kbmMW being a middleware between your application and USB connected Human Interface Devices, like magnetic card readers and other things. In next release kbmMWHID will, of license reasons, be split between kbmMWHIDAPI (which is released under MPL) and kbmMWHID which is released under the usual kbmMW license. At the same time kbmMWHID will receive features to read and understand barcode readers and generic magnetic card readers.
Why kbmMW and USB HID support?
“Why?” you may ask? “Why the need for a set of units for getting data from barcode readers or magnetic card readers? We are already using those in our applications… they just send data right into the focused TEdit box”.
Yes… many USB based HID devices, like card readers and barcode readers, can be configured to act as a keyboard, hence simply sending the data directly to the keyboard buffer, thus the data ending up in a focused TEdit. It works fantastically for most things, but it does provide an issue… you do not know if the data entered into the TEdit is coming from a true keyboard or from a barcode reader.
What if you need both to be able to use the keyboard in a usual way, where pressing the button 6 will result in some feature being started, while being able to receive data for a specific purpose from a barcode reader? You really can’t do that if all you are getting is keyboard input.
You will need to be able to separate the keyboard input from the input from the barcode reader.
Another example may be that you do not really want an TEdit field on screen, because you do not want people to be able to manually enter data via a keyboard, for that particular place, but you would still like to be able to trigger something with a barcode reader, or you want the barcode data to always end up in a specific field on the screen regardless which of many onscreen TEdit boxes are focused.
In these cases, you will need the to be able to communicate with the barcode reader as a HID POS configured device rather than configured as a keyboard device.
Enter kbmMWHID to the rescue.
It can be fairly confusing to understand how USB HID devices work, partly because of the terminology used as terms of data items are unfamiliar to most programmers, and each USB HID device may not support (often are not supporting) standard things, but instead provide their own custom support.
The features supported can be queried when connection to a USB HID device has been made, but understanding exactly what features to use and how, are still very much vendor and product specific.
Connecting to an USB HID device
All USB devices are identified by at least a VID (Vendor Identification) and a PID (Product Identification) number. Both numbers are 16 bits wide. Hence there can currently be at most roughly 65500 unique USB vendors on the planet, and each vendor can have at most roughly 65500 different products.
Often the VID and PID are not provided as info in the leaflet included in a purchase of a USB HID device, so you will have to do some research to figure it out as it is crucial information to have to be able to connect to it.
Fortunately it is not too complicated to do that research.
You can look for it in Windows device manager, which you open by pressing the Windows key on your keyboard and type devmgmt.msc followed by the Enter/Return key.
Now you need to plug in your HID device (barcode / card reader), and you may notice that something new shows up in the list of devices.
Notice that to be able to use kbmMWHID, your device needs to be configured as a HID device. Some devices are like that from start, but others may be configured default to act as a keyboard. You will need to read the leaflet to know if your device default comes as a HID device or as a keyboard emulation.
Most barcode readers can easily be configured to one or the other by scanning some special “programming” barcodes, which can be found in the manual for the device.
If you can see your device in the list, you can right click it and look for its properties. On the properties window, look at the details pane and look for a property called “Device instance path” or in Danish “Sti for enhedsforekomst”.
The value shown, is the “address” for that particular device. However make sure that it starts with “HID\”. If it does not, your device may not currently be configured as a HID device.
The “address” consists of multiple parts. The first part states the overall type of device… USB, HID, PCI etc.
The second part contains a vendor ID and a product ID:
The part after the underscore (_) is a 4 digit (16 bit) hexadecimal number, which is the one you will need to use.
The remaining parts identifies that particular instance of the device. You could easily have inserted multiple identical barcode readers why you will need the full address to distinguish which one you want to communicate with.
However if you know you will only have one you can choose just to note down the VID and PID value.
So with the complete address, or the VID/PID values noted down, we can start to talk with the device.
kbmMW contains a HID demo application showing how to use some features. It also shows how you can list potential HID devices found on your computer and easily select one of them, from within your application.
This is a screenshot of it run on my development computer (there are currently no attached barcode readers on it) but a couple of other HID devices like USB keyboard from ROCCAT and a USB receiver for my mouse from Logitech.
kbmMWHID contains the features to make that list happen. The list only contains USB HID devices. You can see the same device being listed multiple times. The reason for that, is that some devices provides multiple addresses for themselves. Usually you can simply pick the first you see.
This is the demo code for filling the combobox.
type TStringTag = class private FTag:string; public constructor Create(ATag:string); property Tag:string read FTag; end; ... constructor TStringTag.Create(ATag:string); begin inherited Create; FTag:=ATag; end; procedure TForm1.BuildCombo; var h:TkbmMWHID; i:integer; s,n:string; begin h:=TkbmMWHID.Create; try h.FetchIDs; for i:=0 to h.IDs.Count-1 do begin try s:=h.IDs.Strings[i]; if h.Connect(s) then begin n:=h.Manufacturer+'.'+h.Product; ComboBox1.AddItem(n,TStringTag.Create(s)); end; except end; end; finally h.Free; end; end;
Obviously you should free the TStringTag items before clearing the combobox later on.
But the point is that getting the relevant addresses is easy using kbmMWHID. Just call FetchIDs and access the IDs property. By connecting to them one by one, you can obtain additional information like manufacturer and product name, and you can query the HidCaps.UsagePage to figure out if the device supports a “standard” set of usages or relies on vendor specific usages.
FetchIDs can also take a VID and optional PID argument, which means only IDs (addresses) matching those particular VID and PID values will be returned.
A usage page is thus a grouping of usages. And a usage is a function (identified by an index) which may be used for retrieving a value or setting a value on an USB HID device.
If UsagePage < $FF00 then it is supposed to be supporting one of the USB standard sets of usages of which numerous are specified.
You can read more about them here: https://usb.org/sites/default/files/hut1_21_0.pdf
A standard barcode reader will tell that it supports usage page $8C, while a magnetic card reader like MagTek only provides a vendor specific usage page $FF00 and thus has it’s own set of non standard usages (function indexes).
Getting data from the USB HID device
USB HID devices are accessed via a standard Windows handle, which means that read and write functions in Windows SDK that takes a handle can be used for communicating with it.
However it is usually advised to use IOCP (IO Completion Ports) to handle the communication why kbmMWHID provides that functionality.
Reading from the HID device is done with kbmMWHID using the function ReadInputReport(const AMaxWaitMS:integer):TBytes
The function will be blocking until data is available or the device is disconnected, or until AMaxWaitMS msecs has passed.
In case data is available from the device, an input report (as it is called in USB HID terminology) is provided. It is a number of bytes, which contain all data received from the device, for a particular usage page, at that point in time. The data is formatted in such a way that you should not attempt to brute force parse it yourself. Even though you might find recognizable data at some offset, you can not be sure that the data will be at that particular offset next time you receive data from the device.
Instead it is here the usage (function indexes) comes into play. You use special Windows functions that takes usage values (function indexes) to extract data from the input report.
For a barcode reader you would extract the data read like this (copied from kbmMWHID.pas):
r:=HIDAPI.HidP_GetUsageValueArray(HidP_Input, UPG_BR, 0, UID_DECODED_DATA_1, @p, l, FPreparsedData, ABuffer, length(ABuffer));
Where ABuffer is the received input report, UPG_BR is the usage page number ($8C in this case), and UID_DECODED_DATA_1 the usage (function index) which is $FE in this case.
Additional arguments are HidP_Input which is a constant indicating it is data to be treated as an input report, p which is a memory buffer large enough to receive the data returned, l which is the length of the memory buffer to receive the data and FPreparsedData which is a value produced by the function HIDAPI.HidD_GetPreparsedData. It is automatically called by kbmMWHID, upon using the Connect method so you will not need to worry about doing that.
This explained in slightly more details how to use a few of the kbmMW HID API low level functions. However kbmMWHID comes with higher level features specifically for handling standard barcode readers etc. which makes life even easier:
function TMyClass.ReadBarcode(const AAddress:string):string; var hid:TkbmMWGenericBarcodeReaderHID; begin hid:=TkbmMWGenericBarcodeReaderHID.Create; try hid.Connect(AAddress); Result:=hid.ReadBarcodeAsString(10000); finally hid.Free; end; end;
It simply connects to the barcode reader, waits for until 10 seconds for a barcode to be available and returns either the barcode or an empty string if nothing was available.
kbmMWHID contains high level support for:
- TkbmMWGenericBarcodeReaderHID (Regular barcode readers supporting usage page $8C)
- TkbmMWGenericMagneticStripeReaderHID (Regular multitrack magnetic stripe readers supporting usage page $8E)
- TkbmMWMagTekMSRHID (MagTek specific multitrack magnetic stripe reader providing additional info on reading, using usage page $FF00)
Other HID devices may be getting high level support later on as it is required.