Назад
Chapter 13
336
eventObject = CreateEvent
(IntPtr.Zero,
false,
false,
String.Empty);
HidOverlapped.OffsetLow = 0;
HidOverlapped.OffsetHigh = 0;
HidOverlapped.EventHandle = eventObject;
unManagedBuffer = Marshal.AllocHGlobal(inputReportBuffer.Length);
unManagedOverlapped = Marshal.AllocHGlobal(Marshal.SizeOf(HidOverlapped));
Marshal.StructureToPtr(HidOverlapped, unManagedOverlapped, false);
readHandle = CreateFile
(devicePathName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
0);
success = ReadFile
(readHandle,
unManagedBuffer,
inputReportBuffer.Length,
ref numberOfBytesRead,
unManagedOverlapped);
Human Interface Devices: Host Application
337
// If ReadFile returned true, a report is available. Otherwise, check for completion.
if (!success)
{
result = WaitForSingleObject
(eventObject,
3000);
switch ( result )
{
case WAIT_OBJECT_0:
success = True;
GetOverlappedResult
(readHandle,
unManagedOverlapped,
ref numberOfBytesRead,
false);
break;
case WAIT_TIMEOUT:
CancelIo(readHandle);
break;
default:
CancelIo(readHandle);
break;
}
}
if (success)
{
// A report was received.
// Copy the received data to inputReportBuffer for the application to use.
Marshal.Copy(unManagedBuffer, inputReportBuffer, 0, numberOfBytesRead);
}
Marshal.FreeHGlobal(unManagedOverlapped);
Marshal.FreeHGlobal(unManagedBuffer);
Chapter 13
338
&GVCKNU
The buffer passed to ReadFile should be at least the size reported in the
InputReportByteLength property of the HIDP_CAPS structure returned by
HidP_GetCaps.
The CreateEvent function returns a pointer to an event object that will be set to
the signaled state when the read operation succeeds or the function times out or
returns another error. The call to ReadFile passes the returned pointer in the
HidOverlapped structure. Marshaling allocates memory for the overlapped
structure and the report buffer to ensure that their contents remain accessible
for the life of the overlapped operation.
CreateFile obtains a handle for overlapped I/O by setting the
dwFlagsAndAttributes parameter to FILE_FLAG_OVERLAPPED.
The call to ReadFile passes the handle returned by CreateFile, an array to store
the returned report, the report’s length, a pointer to a variable to hold the num-
ber of bytes read, and a pointer to a NativeOverlapped structure. The struc-
tures EventHandle member is the handle returned by CreateEvent.
ReadFile returns immediately. A return value of true indicates that the function
has retrieved one or more reports. False means that a report wasnt available. To
detect when a report arrives, the application calls WaitForSingleObject, passing
a pointer to the event object and a timeout value in milliseconds.
If WaitForSingleObject returns success (WAIT_OBJECT_0), GetOverlappe-
dResult returns the number of bytes read. The Marshal.Copy method copies
the report data to the managed inputReportBuffer array. The application can
then use the report data as desired and free the memory previously allocated
and no longer needed.
The first byte in inputReportBuffer is the Report ID, and the following bytes
are the report data. If the interface supports only the default Report ID of zero,
the Report ID doesnt transmit on the bus but is still present in the buffer
returned by ReadFile.
A call to ReadFile doesnt initiate traffic on the bus. The host begins requesting
reports when the HID driver loads during enumeration, and the driver stores
received reports in a ring buffer. When the buffer is full and a new report
arrives, the buffer drops the oldest report. A call to ReadFile reads the oldest
report in the buffer. If the drivers buffer is empty, ReadFile waits for a report to
arrive.
Human Interface Devices: Host Application
339
Under Windows 98 SE and later, HidD_SetNumInputBuffers can set the
buffer size. Different Windows editions have different default buffer sizes, rang-
ing from 2 under Windows 98 Gold to 32 under Windows XP.
Each handle with read access to the HID has its own Input buffer, so multiple
applications can read the same reports.
If the application doesnt request reports as frequently as the endpoint sends
them, some reports will be lost. One way to keep from losing reports is to
increase the size of the report buffer passed to ReadFile. If multiple reports are
available, ReadFile returns as many as will fit in the buffer. If you need to be
absolutely sure not to lose a report, use Feature reports instead. Also see the tips
in Chapter 3 about performing time-critical transfers.
The Idle rate introduced in Chapter 11 determines whether or not a device
sends a report if the data hasnt changed since the last transfer.
If ReadFile isnt returning, these are possible reasons:
The HID’s interrupt IN endpoint is NAKing the IN token packets because
the endpoint hasnt been armed to send report data. An endpoints inter-
rupt typically triggers only after endpoint sends data, so the device must
arm the endpoint to send the first report before the first interrupt.
The number of bytes the endpoint is sending doesnt equal the number of
bytes in a report (for HIDs that use the default Report ID) or the number
of bytes in a report + 1 (for HIDs that use other Report IDs).
For HIDs with multiple Report IDs, the first byte doesnt match a valid
Report ID.
9TKVKPIC(GCVWTG4GRQTVVQVJG&GXKEG
To send a Feature report to a device, use HidD_SetFeature, which sends a Set
Report request and a report in a control transfer.
Chapter 13
340
8$ Definitions
<DllImport("hid.dll", SetLastError:=True)> _
Shared Function HidD_SetFeature _
(ByVal HidDeviceObject As SafeFileHandle, _
ByVal lpReportBuffer() As Byte, _
ByVal ReportBufferLength As Int32) _
As Boolean
End Function
Use
Dim outFeatureReportBuffer() As Byte = Nothing
Dim success As Boolean
Array.Resize(outFeatureReportBuffer, Capabilities.FeatureReportByteLength)
' Store the Report ID in the first byte of the buffer:
outFeatureReportBuffer(0) = 0
' Store the report data following the Report ID. Example:
outFeatureReportBuffer(1) = 79
outFeatureReportBuffer(2) = 75
success = HidD_SetFeature _
(deviceHandle, _
outFeatureReportBuffer, _
outFeatureReportBuffer.Length)
8%
Definitions
[ DllImport( "hid.dll", SetLastError=true ) ]
internal static extern Boolean HidD_SetFeature
( SafeFileHandle HidDeviceObject,
Byte lpReportBuffer[],
Int32 ReportBufferLength );
Use
Byte[] outFeatureReportBuffer = null;
Boolean success = false;
Array.Resize(ref outFeatureReportBuffer, Capabilities.FeatureReportByteLength);
Human Interface Devices: Host Application
341
// Store the Report ID in the first byte of the buffer:
outFeaturetReportBuffer[ 0 ] = 0;
// Store the report data following the Report ID. Example:
outFeatureReportBuffer[ 1 ] = 79;
outFeatureReportBuffer[ 2 ] = 75;
success = HidD_SetFeature
(deviceHandle,
outFeatureReportBuffer,
outFeatureReportBuffer.Length);
&GVCKNU
HidD_SetFeature requires a handle to the HID, an array to write, and the
arrays length. The first byte in the outFeatureReportBuffer array is the Report
ID. The arrays length is in the HIDP_CAPS structure retrieved by
HidP_GetCaps.
The function returns true on success. If the device continues to NAK the report
data, the function times out and returns.
A call to HidD_SetOutputReport works in much the same way to send an
Output report using a control transfer. The function passes a handle to the
HID, a pointer to a byte array containing an Output report, and the number of
bytes in the report plus one byte for the Report ID.
4GCFKPIC(GCVWTG4GRQTVHTQOC&GXKEG
To read a Feature report from a device, use HidD_GetFeature, which sends a
Get_Feature request in a control transfer. The endpoint returns the report in
the Data stage.
8$ Definitions
<DllImport("hid.dll", SetLastError:=True)> _
Shared Function HidD_GetFeature _
(ByVal HidDeviceObject As SafeFileHandle, _
ByVal lpReportBuffer() As Byte, _
ByVal ReportBufferLength As Int32) _
As Boolean
End Function
Chapter 13
342
Use
Dim inFeatureReportBuffer() As Byte = Nothing
Dim success As Boolean
Array.Resize(inFeatureReportBuffer, Capabilities.FeatureReportByteLength)
'The first byte in the report buffer is the Report ID:
InFeatureReportBuffer(0) = 0
success = HidD_GetFeature _
(deviceHandle, _
inFeatureReportBuffer, _
inFeatureReportBuffer.Length)
8%
Definitions
[ DllImport( "hid.dll", SetLastError=true ) ]
internal static extern Boolean HidD_GetFeature
( SafeFileHandle HidDeviceObject,
Byte[] lpReportBuffer,
Int32 ReportBufferLength );
Use
Byte[] inFeatureReportBuffer = null;
Boolean success = false;
Array.Resize(ref inFeatureReportBuffer, Capabilities.FeatureReportByteLength);
// The first byte in the report buffer is the Report ID:
inFeatureReportBuffer[0] = 0;
success = HidD_GetFeature
(deviceHandle,
inFeatureReportBuffer,
inFeatureReportBuffer.Length);
&GVCKNU
HidD_GetFeature requires a handle to the HID, an array to hold the retrieved
report(s), and the arrays length. The inFeatureReportBuffer array holds the
retrieved report. The first byte in the array is the Report ID. The arrays length
is in the HIDP_CAPS structure retrieved by HidP_GetCaps.
Human Interface Devices: Host Application
343
The function returns true on success. If the device continues to return NAK in
the Data stage of the transfer, the function times out and returns.
A call to HidD_GetInputReport works in much the same way to request an
Input report using a control transfer. The function passes a handle to the HID,
an array to hold the Input report, and the number of bytes in the report plus
one byte for the Report ID.
%NQUKPI%QOOWPKECVKQPU
When finished communicating, the application should call the Close method
to close any SafeFileHandles opened by CreateFile as described in Chapter 10.
When finished using the PreparsedData buffer that HidD_GetPreparsedData
returned, the application should call HidD_FreePreparsedData.
8$ Definitions
<DllImport("hid.dll", SetLastError:=True)> _
Shared Function HidD_FreePreparsedData _
(ByVal PreparsedData As IntPtr) _
As Boolean
End Function
Use
Dim success As Boolean
success = HidD_FreePreparsedData(preparsedData)
8%
Definitions
[ DllImport( "hid.dll", SetLastError=true ) ]
internal static extern Boolean HidD_FreePreparsedData
( IntPtr PreparsedData );
Use
Boolean success = false;
success = HidD_FreePreparsedData( preparsedData );
345

7UKPI9KP75$HQT
8GPFQT&GHKPGF(WPEVKQPU
An option for devices that perform vendor-specific functions is Microsofts
WinUSB driver. This chapter shows how to develop a device that uses the
WinUSB driver and how to use the WinUSB API to access the device from
applications.
%CRCDKNKVKGUCPF.KOKVU
A device is a candidate for using the WinUSB driver if the device and its host
computer(s) meet the requirements below.
&GXKEG4GSWKTGOGPVU
The device:
Exchanges application data using any combination of control, interrupt,
and bulk endpoints. (The driver doesnt support isochronous transfers.)
Has descriptors that specify a vendor-specific class.