Newly added device are now showing green. Removed devices are showing red.
Other devices are showing black. Devices in unknown state are showing purple.
1 using Microsoft.Win32.SafeHandles;
3 using System.Diagnostics;
4 using System.Globalization;
6 using System.Management;
7 using System.Runtime.InteropServices;
8 using System.Threading;
10 using System.Windows.Forms;
11 using System.Collections.Generic;
17 /// Project: GenericHid
19 /// ***********************************************************************
20 /// Software License Agreement
22 /// Licensor grants any person obtaining a copy of this software ("You")
23 /// a worldwide, royalty-free, non-exclusive license, for the duration of
24 /// the copyright, free of charge, to store and execute the Software in a
25 /// computer system and to incorporate the Software or any portion of it
26 /// in computer programs You write.
28 /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29 /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30 /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
31 /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32 /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33 /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
35 /// ***********************************************************************
40 /// This software was written using Visual Studio Express 2012 for Windows
41 /// Desktop building for the .NET Framework v4.5.
44 /// Demonstrates USB communications with a generic HID-class device
47 /// Windows Vista or later and an attached USB generic Human Interface Device (HID).
48 /// (Does not run on Windows XP or earlier because .NET Framework 4.5 will not install on these OSes.)
51 /// Finds an attached device that matches the vendor and product IDs in the form's
54 /// Retrieves the device's capabilities.
55 /// Sends and requests HID reports.
57 /// Uses the System.Management class and Windows Management Instrumentation (WMI) to detect
58 /// when a device is attached or removed.
60 /// A list box displays the data sent and received along with error and status messages.
61 /// You can select data to send and 1-time or periodic transfers.
63 /// You can change the size of the host's Input report buffer and request to use control
64 /// transfers only to exchange Input and Output reports.
66 /// To view additional debugging messages, in the Visual Studio development environment,
67 /// from the main menu, select Build > Configuration Manager > Active Solution Configuration
68 /// and select Configuration > Debug and from the main menu, select View > Output.
70 /// The application uses asynchronous FileStreams to read Input reports and write Output
71 /// reports so the application's main thread doesn't have to wait for the device to retrieve a
72 /// report when the HID driver's buffer is empty or send a report when the device's endpoint is busy.
74 /// For code that finds a device and opens handles to it, see the FindTheHid routine in frmMain.cs.
75 /// For code that reads from the device, see GetInputReportViaInterruptTransfer,
76 /// GetInputReportViaControlTransfer, and GetFeatureReport in Hid.cs.
77 /// For code that writes to the device, see SendInputReportViaInterruptTransfer,
78 /// SendInputReportViaControlTransfer, and SendFeatureReport in Hid.cs.
80 /// This project includes the following modules:
82 /// GenericHid.cs - runs the application.
83 /// FrmMain.cs - routines specific to the form.
84 /// Hid.cs - routines specific to HID communications.
85 /// DeviceManagement.cs - routine for obtaining a handle to a device from its GUID.
86 /// Debugging.cs - contains a routine for displaying API error messages.
87 /// HidDeclarations.cs - Declarations for API functions used by Hid.cs.
88 /// FileIODeclarations.cs - Declarations for file-related API functions.
89 /// DeviceManagementDeclarations.cs - Declarations for API functions used by DeviceManagement.cs.
90 /// DebuggingDeclarations.cs - Declarations for API functions used by Debugging.cs.
92 /// Companion device firmware for several device CPUs is available from www.Lvr.com/hidpage.htm
93 /// You can use any generic HID (not a system mouse or keyboard) that sends and receives reports.
94 /// This application will not detect or communicate with non-HID-class devices.
96 /// For more information about HIDs and USB, and additional example device firmware to use
97 /// with this application, visit Lakeview Research at http://Lvr.com
98 /// Send comments, bug reports, etc. to jan@Lvr.com or post on my PORTS forum: http://www.lvr.com/forum
102 /// Disabled form buttons when a transfer is in progress.
103 /// Other minor edits for clarity and readability.
104 /// Will NOT run on Windows XP or earlier, see below.
108 /// Uses the .NET System.Management class to detect device arrival and removal with WMI instead of Win32 RegisterDeviceNotification.
109 /// Other minor edits.
110 /// Will NOT run on Windows XP or earlier, see below.
114 /// This version will NOT run on Windows XP or earlier because the code uses .NET Framework 4.5 to support asynchronous FileStreams.
115 /// The .NET Framework 4.5 redistributable is compatible with Windows 8, Windows 7 SP1, Windows Server 2008 R2 SP1,
116 /// Windows Server 2008 SP2, Windows Vista SP2, and Windows Vista SP3.
117 /// For compatibility, replaced ToInt32 with ToInt64 here:
118 /// IntPtr pDevicePathName = new IntPtr(detailDataBuffer.ToInt64() + 4);
120 /// if ((deviceNotificationHandle.ToInt64() == IntPtr.Zero.ToInt64()))
121 /// For compatibility if the charset isn't English, added System.Globalization.CultureInfo.InvariantCulture here:
122 /// if ((String.Compare(DeviceNameString, mydevicePathName, true, System.Globalization.CultureInfo.InvariantCulture) == 0))
123 /// Replaced all Microsoft.VisualBasic namespace code with other .NET equivalents.
124 /// Revised user interface for more flexibility.
125 /// Moved interrupt-transfer and other HID-specific code to Hid.cs.
126 /// Used JetBrains ReSharper to clean up the code: http://www.jetbrains.com/resharper/
130 /// Replaced ReadFile and WriteFile with FileStreams. Thanks to Joe Dunne and John on my Ports forum for tips on this.
131 /// Simplified Hid.cs.
132 /// Replaced the form timer with a system timer.
136 /// Supports Vendor IDs and Product IDs up to FFFFh.
140 /// Changed HIDD_ATTRIBUTES to use UInt16
144 /// Moved Free_ and similar to Finally blocks to ensure they execute.
148 /// Changes to support 64-bit systems, memory management, and other corrections.
149 /// Big thanks to Peter Nielsen.
153 internal class FrmMain
156 #region '"Windows Form Designer generated code "'
160 // This call is required by the Windows Form Designer.
161 InitializeComponent();
163 // Form overrides dispose to clean up the component list.
164 protected override void Dispose(bool Disposing1)
168 if (components != null)
170 components.Dispose();
173 base.Dispose(Disposing1);
176 // Required by the Windows Form Designer
177 private System.ComponentModel.IContainer components;
178 public System.Windows.Forms.ToolTip ToolTip1;
179 public System.Windows.Forms.TextBox TxtBytesReceived;
180 public System.Windows.Forms.GroupBox FraBytesReceived;
181 public System.Windows.Forms.CheckBox ChkAutoincrement;
182 public System.Windows.Forms.ComboBox CboByte1;
183 public System.Windows.Forms.ComboBox CboByte0;
184 public System.Windows.Forms.GroupBox FraBytesToSend;
185 public System.Windows.Forms.ListBox LstResults;
186 // NOTE: The following procedure is required by the Windows Form Designer
187 // It can be modified using the Windows Form Designer.
188 // Do not modify it using the code editor.
189 internal System.Windows.Forms.GroupBox fraInputReportBufferSize;
190 internal System.Windows.Forms.TextBox txtInputReportBufferSize;
191 internal System.Windows.Forms.Button cmdInputReportBufferSize;
192 internal System.Windows.Forms.GroupBox fraDeviceIdentifiers;
193 internal System.Windows.Forms.Label lblVendorID;
194 internal System.Windows.Forms.TextBox txtVendorID;
195 internal System.Windows.Forms.Label lblProductID;
196 internal System.Windows.Forms.TextBox txtProductID;
197 internal System.Windows.Forms.Button cmdFindDevice;
198 private Button cmdGetInputReportInterrupt;
199 public GroupBox fraInterruptTransfers;
200 private Button cmdSendOutputReportControl;
201 private Button cmdGetInputReportControl;
202 public GroupBox fraControlTransfers;
203 private Button cmdGetFeatureReport;
204 private Button cmdSendFeatureReport;
205 private Button cmdPeriodicTransfers;
206 public GroupBox fraSendAndGetContinuous;
207 private RadioButton radFeature;
208 private RadioButton radInputOutputControl;
209 private RadioButton radInputOutputInterrupt;
210 private TreeView treeViewDevices;
211 private Button cmdSendOutputReportInterrupt;
213 [System.Diagnostics.DebuggerStepThrough()]
214 private void InitializeComponent()
216 this.components = new System.ComponentModel.Container();
217 this.ToolTip1 = new System.Windows.Forms.ToolTip(this.components);
218 this.FraBytesReceived = new System.Windows.Forms.GroupBox();
219 this.TxtBytesReceived = new System.Windows.Forms.TextBox();
220 this.FraBytesToSend = new System.Windows.Forms.GroupBox();
221 this.ChkAutoincrement = new System.Windows.Forms.CheckBox();
222 this.CboByte1 = new System.Windows.Forms.ComboBox();
223 this.CboByte0 = new System.Windows.Forms.ComboBox();
224 this.LstResults = new System.Windows.Forms.ListBox();
225 this.fraInputReportBufferSize = new System.Windows.Forms.GroupBox();
226 this.cmdInputReportBufferSize = new System.Windows.Forms.Button();
227 this.txtInputReportBufferSize = new System.Windows.Forms.TextBox();
228 this.fraDeviceIdentifiers = new System.Windows.Forms.GroupBox();
229 this.txtProductID = new System.Windows.Forms.TextBox();
230 this.lblProductID = new System.Windows.Forms.Label();
231 this.txtVendorID = new System.Windows.Forms.TextBox();
232 this.lblVendorID = new System.Windows.Forms.Label();
233 this.cmdFindDevice = new System.Windows.Forms.Button();
234 this.cmdSendOutputReportInterrupt = new System.Windows.Forms.Button();
235 this.cmdGetInputReportInterrupt = new System.Windows.Forms.Button();
236 this.fraInterruptTransfers = new System.Windows.Forms.GroupBox();
237 this.cmdPeriodicTransfers = new System.Windows.Forms.Button();
238 this.cmdSendOutputReportControl = new System.Windows.Forms.Button();
239 this.cmdGetInputReportControl = new System.Windows.Forms.Button();
240 this.fraControlTransfers = new System.Windows.Forms.GroupBox();
241 this.cmdGetFeatureReport = new System.Windows.Forms.Button();
242 this.cmdSendFeatureReport = new System.Windows.Forms.Button();
243 this.fraSendAndGetContinuous = new System.Windows.Forms.GroupBox();
244 this.radFeature = new System.Windows.Forms.RadioButton();
245 this.radInputOutputControl = new System.Windows.Forms.RadioButton();
246 this.radInputOutputInterrupt = new System.Windows.Forms.RadioButton();
247 this.treeViewDevices = new System.Windows.Forms.TreeView();
248 this.FraBytesReceived.SuspendLayout();
249 this.FraBytesToSend.SuspendLayout();
250 this.fraInputReportBufferSize.SuspendLayout();
251 this.fraDeviceIdentifiers.SuspendLayout();
252 this.fraInterruptTransfers.SuspendLayout();
253 this.fraControlTransfers.SuspendLayout();
254 this.fraSendAndGetContinuous.SuspendLayout();
255 this.SuspendLayout();
259 this.FraBytesReceived.BackColor = System.Drawing.SystemColors.Control;
260 this.FraBytesReceived.Controls.Add(this.TxtBytesReceived);
261 this.FraBytesReceived.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
262 this.FraBytesReceived.ForeColor = System.Drawing.SystemColors.ControlText;
263 this.FraBytesReceived.Location = new System.Drawing.Point(495, 353);
264 this.FraBytesReceived.Name = "FraBytesReceived";
265 this.FraBytesReceived.RightToLeft = System.Windows.Forms.RightToLeft.No;
266 this.FraBytesReceived.Size = new System.Drawing.Size(112, 136);
267 this.FraBytesReceived.TabIndex = 4;
268 this.FraBytesReceived.TabStop = false;
269 this.FraBytesReceived.Text = "Bytes Received";
273 this.TxtBytesReceived.AcceptsReturn = true;
274 this.TxtBytesReceived.BackColor = System.Drawing.SystemColors.Window;
275 this.TxtBytesReceived.Cursor = System.Windows.Forms.Cursors.IBeam;
276 this.TxtBytesReceived.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
277 this.TxtBytesReceived.ForeColor = System.Drawing.SystemColors.WindowText;
278 this.TxtBytesReceived.Location = new System.Drawing.Point(18, 24);
279 this.TxtBytesReceived.MaxLength = 0;
280 this.TxtBytesReceived.Multiline = true;
281 this.TxtBytesReceived.Name = "TxtBytesReceived";
282 this.TxtBytesReceived.RightToLeft = System.Windows.Forms.RightToLeft.No;
283 this.TxtBytesReceived.Size = new System.Drawing.Size(72, 96);
284 this.TxtBytesReceived.TabIndex = 5;
288 this.FraBytesToSend.BackColor = System.Drawing.SystemColors.Control;
289 this.FraBytesToSend.Controls.Add(this.ChkAutoincrement);
290 this.FraBytesToSend.Controls.Add(this.CboByte1);
291 this.FraBytesToSend.Controls.Add(this.CboByte0);
292 this.FraBytesToSend.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
293 this.FraBytesToSend.ForeColor = System.Drawing.SystemColors.ControlText;
294 this.FraBytesToSend.Location = new System.Drawing.Point(612, 235);
295 this.FraBytesToSend.Name = "FraBytesToSend";
296 this.FraBytesToSend.RightToLeft = System.Windows.Forms.RightToLeft.No;
297 this.FraBytesToSend.Size = new System.Drawing.Size(160, 136);
298 this.FraBytesToSend.TabIndex = 1;
299 this.FraBytesToSend.TabStop = false;
300 this.FraBytesToSend.Text = "Bytes to Send";
304 this.ChkAutoincrement.BackColor = System.Drawing.SystemColors.Control;
305 this.ChkAutoincrement.Cursor = System.Windows.Forms.Cursors.Default;
306 this.ChkAutoincrement.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
307 this.ChkAutoincrement.ForeColor = System.Drawing.SystemColors.ControlText;
308 this.ChkAutoincrement.Location = new System.Drawing.Point(8, 96);
309 this.ChkAutoincrement.Name = "ChkAutoincrement";
310 this.ChkAutoincrement.RightToLeft = System.Windows.Forms.RightToLeft.No;
311 this.ChkAutoincrement.Size = new System.Drawing.Size(201, 35);
312 this.ChkAutoincrement.TabIndex = 6;
313 this.ChkAutoincrement.Text = "Autoincrement values";
314 this.ChkAutoincrement.UseVisualStyleBackColor = false;
318 this.CboByte1.BackColor = System.Drawing.SystemColors.Window;
319 this.CboByte1.Cursor = System.Windows.Forms.Cursors.Default;
320 this.CboByte1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
321 this.CboByte1.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
322 this.CboByte1.ForeColor = System.Drawing.SystemColors.WindowText;
323 this.CboByte1.Location = new System.Drawing.Point(8, 64);
324 this.CboByte1.Name = "CboByte1";
325 this.CboByte1.RightToLeft = System.Windows.Forms.RightToLeft.No;
326 this.CboByte1.Size = new System.Drawing.Size(101, 22);
327 this.CboByte1.TabIndex = 3;
331 this.CboByte0.BackColor = System.Drawing.SystemColors.Window;
332 this.CboByte0.Cursor = System.Windows.Forms.Cursors.Default;
333 this.CboByte0.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
334 this.CboByte0.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
335 this.CboByte0.ForeColor = System.Drawing.SystemColors.WindowText;
336 this.CboByte0.Location = new System.Drawing.Point(8, 24);
337 this.CboByte0.Name = "CboByte0";
338 this.CboByte0.RightToLeft = System.Windows.Forms.RightToLeft.No;
339 this.CboByte0.Size = new System.Drawing.Size(101, 22);
340 this.CboByte0.TabIndex = 2;
344 this.LstResults.BackColor = System.Drawing.SystemColors.Window;
345 this.LstResults.Cursor = System.Windows.Forms.Cursors.Default;
346 this.LstResults.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
347 this.LstResults.ForeColor = System.Drawing.SystemColors.WindowText;
348 this.LstResults.HorizontalScrollbar = true;
349 this.LstResults.ItemHeight = 14;
350 this.LstResults.Location = new System.Drawing.Point(12, 494);
351 this.LstResults.Name = "LstResults";
352 this.LstResults.RightToLeft = System.Windows.Forms.RightToLeft.No;
353 this.LstResults.Size = new System.Drawing.Size(760, 256);
354 this.LstResults.TabIndex = 0;
356 // fraInputReportBufferSize
358 this.fraInputReportBufferSize.Controls.Add(this.cmdInputReportBufferSize);
359 this.fraInputReportBufferSize.Controls.Add(this.txtInputReportBufferSize);
360 this.fraInputReportBufferSize.Location = new System.Drawing.Point(623, 44);
361 this.fraInputReportBufferSize.Name = "fraInputReportBufferSize";
362 this.fraInputReportBufferSize.Size = new System.Drawing.Size(149, 79);
363 this.fraInputReportBufferSize.TabIndex = 9;
364 this.fraInputReportBufferSize.TabStop = false;
365 this.fraInputReportBufferSize.Text = "Input Report Buffer Size";
367 // cmdInputReportBufferSize
369 this.cmdInputReportBufferSize.Location = new System.Drawing.Point(6, 47);
370 this.cmdInputReportBufferSize.Name = "cmdInputReportBufferSize";
371 this.cmdInputReportBufferSize.Size = new System.Drawing.Size(136, 26);
372 this.cmdInputReportBufferSize.TabIndex = 1;
373 this.cmdInputReportBufferSize.Text = "Change Buffer Size";
374 this.cmdInputReportBufferSize.Click += new System.EventHandler(this.cmdInputReportBufferSize_Click);
376 // txtInputReportBufferSize
378 this.txtInputReportBufferSize.Location = new System.Drawing.Point(6, 21);
379 this.txtInputReportBufferSize.Name = "txtInputReportBufferSize";
380 this.txtInputReportBufferSize.Size = new System.Drawing.Size(56, 20);
381 this.txtInputReportBufferSize.TabIndex = 0;
383 // fraDeviceIdentifiers
385 this.fraDeviceIdentifiers.Controls.Add(this.txtProductID);
386 this.fraDeviceIdentifiers.Controls.Add(this.lblProductID);
387 this.fraDeviceIdentifiers.Controls.Add(this.txtVendorID);
388 this.fraDeviceIdentifiers.Controls.Add(this.lblVendorID);
389 this.fraDeviceIdentifiers.Location = new System.Drawing.Point(409, 12);
390 this.fraDeviceIdentifiers.Name = "fraDeviceIdentifiers";
391 this.fraDeviceIdentifiers.Size = new System.Drawing.Size(208, 96);
392 this.fraDeviceIdentifiers.TabIndex = 10;
393 this.fraDeviceIdentifiers.TabStop = false;
394 this.fraDeviceIdentifiers.Text = "Device Identifiers";
398 this.txtProductID.Location = new System.Drawing.Point(120, 56);
399 this.txtProductID.Name = "txtProductID";
400 this.txtProductID.Size = new System.Drawing.Size(72, 20);
401 this.txtProductID.TabIndex = 3;
402 this.txtProductID.Text = "1299";
403 this.txtProductID.TextChanged += new System.EventHandler(this.txtProductID_TextChanged);
407 this.lblProductID.Location = new System.Drawing.Point(16, 56);
408 this.lblProductID.Name = "lblProductID";
409 this.lblProductID.Size = new System.Drawing.Size(112, 23);
410 this.lblProductID.TabIndex = 2;
411 this.lblProductID.Text = "Product ID (hex):";
415 this.txtVendorID.Location = new System.Drawing.Point(120, 24);
416 this.txtVendorID.Name = "txtVendorID";
417 this.txtVendorID.Size = new System.Drawing.Size(72, 20);
418 this.txtVendorID.TabIndex = 1;
419 this.txtVendorID.Text = "0925";
420 this.txtVendorID.TextChanged += new System.EventHandler(this.txtVendorID_TextChanged);
424 this.lblVendorID.Location = new System.Drawing.Point(16, 24);
425 this.lblVendorID.Name = "lblVendorID";
426 this.lblVendorID.Size = new System.Drawing.Size(112, 23);
427 this.lblVendorID.TabIndex = 0;
428 this.lblVendorID.Text = "Vendor ID (hex):";
432 this.cmdFindDevice.Location = new System.Drawing.Point(636, 12);
433 this.cmdFindDevice.Name = "cmdFindDevice";
434 this.cmdFindDevice.Size = new System.Drawing.Size(136, 26);
435 this.cmdFindDevice.TabIndex = 11;
436 this.cmdFindDevice.Text = "Find My Device";
437 this.cmdFindDevice.Click += new System.EventHandler(this.cmdFindDevice_Click);
439 // cmdSendOutputReportInterrupt
441 this.cmdSendOutputReportInterrupt.Location = new System.Drawing.Point(21, 27);
442 this.cmdSendOutputReportInterrupt.Name = "cmdSendOutputReportInterrupt";
443 this.cmdSendOutputReportInterrupt.Size = new System.Drawing.Size(118, 26);
444 this.cmdSendOutputReportInterrupt.TabIndex = 12;
445 this.cmdSendOutputReportInterrupt.Text = "Send Output Report";
446 this.cmdSendOutputReportInterrupt.UseVisualStyleBackColor = true;
447 this.cmdSendOutputReportInterrupt.Click += new System.EventHandler(this.cmdSendOutputReportInterrupt_Click);
449 // cmdGetInputReportInterrupt
451 this.cmdGetInputReportInterrupt.Location = new System.Drawing.Point(21, 60);
452 this.cmdGetInputReportInterrupt.Name = "cmdGetInputReportInterrupt";
453 this.cmdGetInputReportInterrupt.Size = new System.Drawing.Size(118, 26);
454 this.cmdGetInputReportInterrupt.TabIndex = 13;
455 this.cmdGetInputReportInterrupt.Text = "Get Input Report";
456 this.cmdGetInputReportInterrupt.UseVisualStyleBackColor = true;
457 this.cmdGetInputReportInterrupt.Click += new System.EventHandler(this.cmdGetInputReportInterrupt_Click);
459 // fraInterruptTransfers
461 this.fraInterruptTransfers.BackColor = System.Drawing.SystemColors.Control;
462 this.fraInterruptTransfers.Controls.Add(this.cmdSendOutputReportInterrupt);
463 this.fraInterruptTransfers.Controls.Add(this.cmdGetInputReportInterrupt);
464 this.fraInterruptTransfers.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
465 this.fraInterruptTransfers.ForeColor = System.Drawing.SystemColors.ControlText;
466 this.fraInterruptTransfers.Location = new System.Drawing.Point(338, 129);
467 this.fraInterruptTransfers.Name = "fraInterruptTransfers";
468 this.fraInterruptTransfers.RightToLeft = System.Windows.Forms.RightToLeft.No;
469 this.fraInterruptTransfers.Size = new System.Drawing.Size(151, 100);
470 this.fraInterruptTransfers.TabIndex = 14;
471 this.fraInterruptTransfers.TabStop = false;
472 this.fraInterruptTransfers.Text = "Interrupt Transfers";
474 // cmdPeriodicTransfers
476 this.cmdPeriodicTransfers.Location = new System.Drawing.Point(153, 36);
477 this.cmdPeriodicTransfers.Name = "cmdPeriodicTransfers";
478 this.cmdPeriodicTransfers.Size = new System.Drawing.Size(118, 26);
479 this.cmdPeriodicTransfers.TabIndex = 16;
480 this.cmdPeriodicTransfers.Text = "Start";
481 this.cmdPeriodicTransfers.UseVisualStyleBackColor = true;
482 this.cmdPeriodicTransfers.Click += new System.EventHandler(this.cmdPeriodicTransfers_Click);
484 // cmdSendOutputReportControl
486 this.cmdSendOutputReportControl.Location = new System.Drawing.Point(10, 27);
487 this.cmdSendOutputReportControl.Name = "cmdSendOutputReportControl";
488 this.cmdSendOutputReportControl.Size = new System.Drawing.Size(118, 26);
489 this.cmdSendOutputReportControl.TabIndex = 12;
490 this.cmdSendOutputReportControl.Text = "Send Output Report";
491 this.cmdSendOutputReportControl.UseVisualStyleBackColor = true;
492 this.cmdSendOutputReportControl.Click += new System.EventHandler(this.cmdSendOutputReportControl_Click);
494 // cmdGetInputReportControl
496 this.cmdGetInputReportControl.Location = new System.Drawing.Point(10, 60);
497 this.cmdGetInputReportControl.Name = "cmdGetInputReportControl";
498 this.cmdGetInputReportControl.Size = new System.Drawing.Size(118, 26);
499 this.cmdGetInputReportControl.TabIndex = 13;
500 this.cmdGetInputReportControl.Text = "Get Input Report";
501 this.cmdGetInputReportControl.UseVisualStyleBackColor = true;
502 this.cmdGetInputReportControl.Click += new System.EventHandler(this.cmdGetInputReportControl_Click);
504 // fraControlTransfers
506 this.fraControlTransfers.BackColor = System.Drawing.SystemColors.Control;
507 this.fraControlTransfers.Controls.Add(this.cmdGetFeatureReport);
508 this.fraControlTransfers.Controls.Add(this.cmdSendFeatureReport);
509 this.fraControlTransfers.Controls.Add(this.cmdSendOutputReportControl);
510 this.fraControlTransfers.Controls.Add(this.cmdGetInputReportControl);
511 this.fraControlTransfers.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
512 this.fraControlTransfers.ForeColor = System.Drawing.SystemColors.ControlText;
513 this.fraControlTransfers.Location = new System.Drawing.Point(495, 129);
514 this.fraControlTransfers.Name = "fraControlTransfers";
515 this.fraControlTransfers.RightToLeft = System.Windows.Forms.RightToLeft.No;
516 this.fraControlTransfers.Size = new System.Drawing.Size(277, 100);
517 this.fraControlTransfers.TabIndex = 15;
518 this.fraControlTransfers.TabStop = false;
519 this.fraControlTransfers.Text = "Control Transfers";
521 // cmdGetFeatureReport
523 this.cmdGetFeatureReport.Location = new System.Drawing.Point(141, 60);
524 this.cmdGetFeatureReport.Name = "cmdGetFeatureReport";
525 this.cmdGetFeatureReport.Size = new System.Drawing.Size(118, 26);
526 this.cmdGetFeatureReport.TabIndex = 15;
527 this.cmdGetFeatureReport.Text = "Get Feature Report";
528 this.cmdGetFeatureReport.UseVisualStyleBackColor = true;
529 this.cmdGetFeatureReport.Click += new System.EventHandler(this.cmdGetFeatureReport_Click);
531 // cmdSendFeatureReport
533 this.cmdSendFeatureReport.Location = new System.Drawing.Point(141, 27);
534 this.cmdSendFeatureReport.Name = "cmdSendFeatureReport";
535 this.cmdSendFeatureReport.Size = new System.Drawing.Size(118, 26);
536 this.cmdSendFeatureReport.TabIndex = 14;
537 this.cmdSendFeatureReport.Text = "Send Feature Report";
538 this.cmdSendFeatureReport.UseVisualStyleBackColor = true;
539 this.cmdSendFeatureReport.Click += new System.EventHandler(this.cmdSendFeatureReport_Click);
541 // fraSendAndGetContinuous
543 this.fraSendAndGetContinuous.BackColor = System.Drawing.SystemColors.Control;
544 this.fraSendAndGetContinuous.Controls.Add(this.radFeature);
545 this.fraSendAndGetContinuous.Controls.Add(this.radInputOutputControl);
546 this.fraSendAndGetContinuous.Controls.Add(this.radInputOutputInterrupt);
547 this.fraSendAndGetContinuous.Controls.Add(this.cmdPeriodicTransfers);
548 this.fraSendAndGetContinuous.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
549 this.fraSendAndGetContinuous.ForeColor = System.Drawing.SystemColors.ControlText;
550 this.fraSendAndGetContinuous.Location = new System.Drawing.Point(311, 235);
551 this.fraSendAndGetContinuous.Name = "fraSendAndGetContinuous";
552 this.fraSendAndGetContinuous.RightToLeft = System.Windows.Forms.RightToLeft.No;
553 this.fraSendAndGetContinuous.Size = new System.Drawing.Size(295, 112);
554 this.fraSendAndGetContinuous.TabIndex = 17;
555 this.fraSendAndGetContinuous.TabStop = false;
556 this.fraSendAndGetContinuous.Text = "Send and Get Continuous";
560 this.radFeature.AutoSize = true;
561 this.radFeature.Location = new System.Drawing.Point(17, 76);
562 this.radFeature.Name = "radFeature";
563 this.radFeature.Size = new System.Drawing.Size(62, 18);
564 this.radFeature.TabIndex = 19;
565 this.radFeature.TabStop = true;
566 this.radFeature.Text = "Feature";
567 this.radFeature.UseVisualStyleBackColor = true;
568 this.radFeature.CheckedChanged += new System.EventHandler(this.radFeature_CheckedChanged);
570 // radInputOutputControl
572 this.radInputOutputControl.AutoSize = true;
573 this.radInputOutputControl.Location = new System.Drawing.Point(17, 52);
574 this.radInputOutputControl.Name = "radInputOutputControl";
575 this.radInputOutputControl.Size = new System.Drawing.Size(120, 18);
576 this.radInputOutputControl.TabIndex = 18;
577 this.radInputOutputControl.TabStop = true;
578 this.radInputOutputControl.Text = "Input Output Control";
579 this.radInputOutputControl.UseVisualStyleBackColor = true;
580 this.radInputOutputControl.CheckedChanged += new System.EventHandler(this.radInputOutputControl_CheckedChanged);
582 // radInputOutputInterrupt
584 this.radInputOutputInterrupt.AutoSize = true;
585 this.radInputOutputInterrupt.Location = new System.Drawing.Point(17, 28);
586 this.radInputOutputInterrupt.Name = "radInputOutputInterrupt";
587 this.radInputOutputInterrupt.Size = new System.Drawing.Size(126, 18);
588 this.radInputOutputInterrupt.TabIndex = 17;
589 this.radInputOutputInterrupt.TabStop = true;
590 this.radInputOutputInterrupt.Text = "Input Output Interrupt";
591 this.radInputOutputInterrupt.UseVisualStyleBackColor = true;
592 this.radInputOutputInterrupt.CheckedChanged += new System.EventHandler(this.radInputOutputInterrupt_CheckedChanged);
596 this.treeViewDevices.Location = new System.Drawing.Point(12, 12);
597 this.treeViewDevices.Name = "treeViewDevices";
598 this.treeViewDevices.Size = new System.Drawing.Size(284, 461);
599 this.treeViewDevices.TabIndex = 18;
603 this.ClientSize = new System.Drawing.Size(784, 756);
604 this.Controls.Add(this.treeViewDevices);
605 this.Controls.Add(this.fraSendAndGetContinuous);
606 this.Controls.Add(this.fraControlTransfers);
607 this.Controls.Add(this.fraInterruptTransfers);
608 this.Controls.Add(this.cmdFindDevice);
609 this.Controls.Add(this.fraDeviceIdentifiers);
610 this.Controls.Add(this.fraInputReportBufferSize);
611 this.Controls.Add(this.FraBytesReceived);
612 this.Controls.Add(this.FraBytesToSend);
613 this.Controls.Add(this.LstResults);
614 this.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
615 this.Location = new System.Drawing.Point(21, 28);
616 this.Name = "FrmMain";
617 this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
618 this.Text = "Generic HID Tester";
619 this.Closed += new System.EventHandler(this.frmMain_Closed);
620 this.Load += new System.EventHandler(this.frmMain_Load);
621 this.FraBytesReceived.ResumeLayout(false);
622 this.FraBytesReceived.PerformLayout();
623 this.FraBytesToSend.ResumeLayout(false);
624 this.fraInputReportBufferSize.ResumeLayout(false);
625 this.fraInputReportBufferSize.PerformLayout();
626 this.fraDeviceIdentifiers.ResumeLayout(false);
627 this.fraDeviceIdentifiers.PerformLayout();
628 this.fraInterruptTransfers.ResumeLayout(false);
629 this.fraControlTransfers.ResumeLayout(false);
630 this.fraSendAndGetContinuous.ResumeLayout(false);
631 this.fraSendAndGetContinuous.PerformLayout();
632 this.ResumeLayout(false);
635 #endregion '"Windows Form Designer generated code "'
637 private Boolean _deviceDetected;
638 private IntPtr _deviceNotificationHandle;
639 private FileStream _deviceData;
640 private FormActions _formActions;
641 private SafeFileHandle _hidHandle;
642 private String _hidUsage;
643 private ManagementEventWatcher _deviceArrivedWatcher;
644 private Boolean _deviceHandleObtained;
645 private ManagementEventWatcher _deviceRemovedWatcher;
646 private Int32 _myProductId;
647 private Int32 _myVendorId;
648 private Boolean _periodicTransfersRequested;
649 private ReportReadOrWritten _readOrWritten;
650 private ReportTypes _reportType;
651 private SendOrGet _sendOrGet;
652 private Boolean _transferInProgress;
653 private TransferTypes _transferType;
655 private static System.Timers.Timer _periodicTransfers;
657 private readonly Debugging _myDebugging = new Debugging(); // For viewing results of API calls via Debug.Write.
658 private readonly DeviceManagement _myDeviceManagement = new DeviceManagement();
659 private Hid _myHid = new Hid();
661 private enum FormActions
664 DisableInputReportBufferSize,
665 EnableGetInputReportInterruptTransfer,
666 EnableInputReportBufferSize,
667 EnableSendOutputReportInterrupt,
668 ScrollToBottomOfListBox,
669 SetInputReportBufferSize,
672 SelectDeviceInTreeView,
673 CompleteDeviceTreeView
676 private enum ReportReadOrWritten
682 private enum ReportTypes
689 private enum SendOrGet
695 private enum TransferTypes
701 private enum WmiDeviceProperties
712 internal FrmMain FrmMy;
714 // This delegate has the same parameters as AccessForm.
715 // Used in accessing the application's form from a different thread.
717 private delegate void MarshalDataToForm(FormActions action, params string[] strings);
720 /// Performs various application-specific functions that
721 /// involve accessing the application's form.
724 /// <param name="action"> a FormActions member that names the action to perform on the form</param>
725 /// <param name="formText"> text that the form displays or the code uses for
726 /// another purpose. Actions that don't use text ignore this parameter. </param>
728 private void AccessForm(FormActions action, params string[] strings)
732 // Select an action to perform on the form:
736 case FormActions.AddItemToListBox:
738 LstResults.Items.Add(strings[0]);
741 case FormActions.DisableInputReportBufferSize:
743 cmdInputReportBufferSize.Enabled = false;
746 case FormActions.EnableGetInputReportInterruptTransfer:
748 cmdGetInputReportInterrupt.Enabled = true;
751 case FormActions.EnableInputReportBufferSize:
753 cmdInputReportBufferSize.Enabled = true;
756 case FormActions.EnableSendOutputReportInterrupt:
758 cmdSendOutputReportInterrupt.Enabled = true;
761 case FormActions.ScrollToBottomOfListBox:
763 LstResults.SelectedIndex = LstResults.Items.Count - 1;
766 case FormActions.SetInputReportBufferSize:
768 txtInputReportBufferSize.Text = strings[0];
771 case FormActions.AddDeviceToTreeView:
773 //Try and see if our device is already present
774 TreeNode[] res = treeViewDevices.Nodes.Find(strings[0], false);
775 foreach (TreeNode device in res)
777 if (device.ForeColor == Color.Red)
779 //Device was removed and has now been added back
780 device.ForeColor = Color.Green;
784 //Device was already there set back our device color to black
785 device.ForeColor = Color.Black;
791 //Our device is already there
795 //Build our node from our string array
796 TreeNode newNode = new TreeNode(strings[0]);
797 for (int i = 1; i < strings.Length; i++)
799 newNode.Nodes.Add(strings[i]);
800 if (strings[i].StartsWith("Name: "))
802 //Found our name property, update our node text
803 newNode.Text = strings[i].Substring(6, strings[i].Length - 6);
807 //New device color is green
808 newNode.ForeColor = Color.Green;
809 newNode.Name = strings[0]; //Set ID as name to make sure we can find it
810 treeViewDevices.Nodes.Add(newNode);
814 case FormActions.ResetDeviceTreeView:
816 //Mark all non removed/red device as purple/unknown
817 foreach (TreeNode device in treeViewDevices.Nodes)
819 if (device.ForeColor != Color.Red)
821 device.ForeColor = Color.Purple;
824 //treeViewDevices.Nodes.Clear();
828 case FormActions.CompleteDeviceTreeView:
830 //Our device list is now complete
831 foreach (TreeNode device in treeViewDevices.Nodes)
833 //Purple devices need to be marked as red for removed
834 if (device.ForeColor == Color.Purple)
836 device.ForeColor = Color.Red;
842 case FormActions.SelectDeviceInTreeView:
844 //treeViewDevices.SelectedNode = null;
845 TreeNode[] res = treeViewDevices.Nodes.Find(strings[0], false);
846 foreach (TreeNode device in res)
848 device.ForeColor = Color.Blue;
849 //treeViewDevices.SelectedNode = res[0];
850 //treeViewDevices.SelectedNode.ForeColor = Color.Blue;
860 DisplayException(Name, ex);
866 /// Add a handler to detect arrival of devices using WMI.
869 private void AddDeviceArrivedHandler()
871 const Int32 pollingIntervalSeconds = 3;
872 var scope = new ManagementScope("root\\CIMV2");
873 scope.Options.EnablePrivileges = true;
877 var q = new WqlEventQuery();
878 q.EventClassName = "__InstanceCreationEvent";
879 q.WithinInterval = new TimeSpan(0, 0, pollingIntervalSeconds);
880 q.Condition = @"TargetInstance ISA 'Win32_USBControllerdevice'";
881 _deviceArrivedWatcher = new ManagementEventWatcher(scope, q);
882 _deviceArrivedWatcher.EventArrived += DeviceAdded;
884 _deviceArrivedWatcher.Start();
888 Debug.WriteLine(e.Message);
889 if (_deviceArrivedWatcher != null)
890 _deviceArrivedWatcher.Stop();
895 /// Add a handler to detect removal of devices using WMI.
898 private void AddDeviceRemovedHandler()
900 const Int32 pollingIntervalSeconds = 3;
901 var scope = new ManagementScope("root\\CIMV2");
902 scope.Options.EnablePrivileges = true;
906 var q = new WqlEventQuery();
907 q.EventClassName = "__InstanceDeletionEvent";
908 q.WithinInterval = new TimeSpan(0, 0, pollingIntervalSeconds);
909 q.Condition = @"TargetInstance ISA 'Win32_USBControllerdevice'";
910 _deviceRemovedWatcher = new ManagementEventWatcher(scope, q);
911 _deviceRemovedWatcher.EventArrived += DeviceRemoved;
912 _deviceRemovedWatcher.Start();
916 Debug.WriteLine(e.Message);
917 if (_deviceRemovedWatcher != null)
918 _deviceRemovedWatcher.Stop();
923 /// Close the handle and FileStreams for a device.
926 private void CloseCommunications()
928 if (_deviceData != null)
933 if ((_hidHandle != null) && (!(_hidHandle.IsInvalid)))
938 // The next attempt to communicate will get a new handle and FileStreams.
940 _deviceHandleObtained = false;
944 /// Search for a specific device.
947 private void cmdFindDevice_Click(Object sender, EventArgs e)
951 if (_transferInProgress)
953 DisplayTransferInProgressMessage();
957 _deviceDetected = FindDeviceUsingWmi();
966 DisplayException(Name, ex);
972 /// Request to get a Feature report from the device.
974 /// <param name="sender"></param>
975 /// <param name="e"></param>
977 private void cmdGetFeatureReport_Click(object sender, EventArgs e)
981 if (_transferInProgress)
983 DisplayTransferInProgressMessage();
987 // Don't allow another transfer request until this one completes.
988 // Move the focus away from the button to prevent the focus from
989 // switching to the next control in the tab order on disabling the button.
991 fraControlTransfers.Focus();
992 cmdGetFeatureReport.Enabled = false;
993 _transferType = TransferTypes.Control;
994 RequestToGetFeatureReport();
999 DisplayException(Name, ex);
1005 /// Request to get an Input report from the device using a control transfer.
1007 /// <param name="sender"></param>
1008 /// <param name="e"></param>
1010 private void cmdGetInputReportControl_Click(object sender, EventArgs e)
1014 // Don't allow another transfer request until this one completes.
1015 // Move the focus away from the button to prevent the focus from
1016 // switching to the next control in the tab order on disabling the button.
1018 if (_transferInProgress)
1020 DisplayTransferInProgressMessage();
1024 fraControlTransfers.Focus();
1025 cmdGetInputReportControl.Enabled = false;
1026 _transferType = TransferTypes.Control;
1027 RequestToGetInputReport();
1030 catch (Exception ex)
1032 DisplayException(Name, ex);
1038 /// Request to get an Input report retrieved using interrupt transfers.
1040 /// <param name="sender"></param>
1041 /// <param name="e"></param>
1043 private void cmdGetInputReportInterrupt_Click(object sender, EventArgs e)
1047 if (_transferInProgress)
1049 DisplayTransferInProgressMessage();
1053 // Don't allow another transfer request until this one completes.
1054 // Move the focus away from the button to prevent the focus from
1055 // switching to the next control in the tab order on disabling the button.
1057 fraInterruptTransfers.Focus();
1058 cmdGetInputReportInterrupt.Enabled = false;
1059 _transferType = TransferTypes.Interrupt;
1060 RequestToGetInputReport();
1063 catch (Exception ex)
1065 DisplayException(Name, ex);
1071 /// Set the number of Input reports the HID driver will store.
1074 private void cmdInputReportBufferSize_Click(Object sender, EventArgs e)
1078 if (_transferInProgress)
1080 DisplayTransferInProgressMessage();
1084 SetInputReportBufferSize();
1090 DisplayException(Name, ex);
1096 /// Alternate sending and getting a report.
1098 /// <param name="sender"></param>
1099 /// <param name="e"></param>
1101 private void cmdPeriodicTransfers_Click(object sender, EventArgs e)
1105 if (cmdPeriodicTransfers.Text == "Start")
1107 if (_transferInProgress)
1109 DisplayTransferInProgressMessage();
1113 _sendOrGet = SendOrGet.Send;
1114 PeriodicTransfersStart();
1119 PeriodicTransfersStop();
1122 catch (Exception ex)
1124 DisplayException(Name, ex);
1130 /// Request to send a Feature report using a control transfer.
1132 /// <param name="sender"></param>
1133 /// <param name="e"></param>
1135 private void cmdSendFeatureReport_Click(object sender, EventArgs e)
1139 if (_transferInProgress)
1141 DisplayTransferInProgressMessage();
1145 // Don't allow another transfer request until this one completes.
1146 // Move the focus away from the button to prevent the focus from
1147 // switching to the next control in the tab order on disabling the button.
1149 fraControlTransfers.Focus();
1150 cmdSendFeatureReport.Enabled = false;
1151 _transferType = TransferTypes.Control;
1152 RequestToSendFeatureReport();
1155 catch (Exception ex)
1157 DisplayException(Name, ex);
1163 /// Request to send an Output report using a control transfer.
1165 /// <param name="sender"></param>
1166 /// <param name="e"></param>
1168 private void cmdSendOutputReportControl_Click(object sender, EventArgs e)
1172 if (_transferInProgress)
1174 DisplayTransferInProgressMessage();
1178 // Don't allow another transfer request until this one completes.
1179 // Move the focus away from the button to prevent the focus from
1180 // switching to the next control in the tab order on disabling the button.
1182 fraControlTransfers.Focus();
1183 cmdSendOutputReportControl.Enabled = false;
1184 _transferType = TransferTypes.Control;
1185 RequestToSendOutputReport();
1188 catch (Exception ex)
1190 DisplayException(Name, ex);
1196 /// Request to send an Output report using an interrupt transfer.
1198 /// <param name="sender"></param>
1199 /// <param name="e"></param>
1201 private void cmdSendOutputReportInterrupt_Click(object sender, EventArgs e)
1205 if (_transferInProgress)
1207 DisplayTransferInProgressMessage();
1211 // Don't allow another transfer request until this one completes.
1212 // Move the focus away from the button to prevent the focus from
1213 // switching to the next control in the tab order on disabling the button.
1215 fraInterruptTransfers.Focus();
1216 cmdSendOutputReportInterrupt.Enabled = false;
1217 _transferType = TransferTypes.Interrupt;
1218 RequestToSendOutputReport();
1221 catch (Exception ex)
1223 DisplayException(Name, ex);
1229 /// Called on arrival of any device.
1230 /// Calls a routine that searches to see if the desired device is present.
1233 private void DeviceAdded(object sender, EventArrivedEventArgs e)
1237 Debug.WriteLine("A USB device has been inserted");
1239 _deviceDetected = FindDeviceUsingWmi();
1241 catch (Exception ex)
1243 DisplayException(Name, ex);
1249 /// Called if the user changes the Vendor ID or Product ID in the text box.
1252 private void DeviceHasChanged()
1256 // If a device was previously detected, stop receiving notifications about it.
1258 if (_deviceHandleObtained)
1260 DeviceNotificationsStop();
1262 CloseCommunications();
1264 // Look for a device that matches the Vendor ID and Product ID in the text boxes.
1268 catch (Exception ex)
1270 DisplayException(Name, ex);
1276 /// Add handlers to detect device arrival and removal.
1279 private void DeviceNotificationsStart()
1281 AddDeviceArrivedHandler();
1282 AddDeviceRemovedHandler();
1286 /// Stop receiving notifications about device arrival and removal
1289 private void DeviceNotificationsStop()
1293 if (_deviceArrivedWatcher != null)
1294 _deviceArrivedWatcher.Stop();
1295 if (_deviceRemovedWatcher != null)
1296 _deviceRemovedWatcher.Stop();
1298 catch (Exception ex)
1300 DisplayException(Name, ex);
1306 /// Called on removal of any device.
1307 /// Calls a routine that searches to see if the desired device is still present.
1310 private void DeviceRemoved(object sender, EventArgs e)
1314 Debug.WriteLine("A USB device has been removed");
1316 _deviceDetected = FindDeviceUsingWmi();
1318 if (!_deviceDetected)
1320 _deviceHandleObtained = false;
1321 CloseCommunications();
1324 catch (Exception ex)
1326 DisplayException(Name, ex);
1332 /// Displays received or written report data.
1335 /// <param name="buffer"> contains the report data. </param>
1336 /// <param name="currentReportType" > "Input", "Output", or "Feature"</param>
1337 /// <param name="currentReadOrWritten" > "read" for Input and IN Feature reports, "written" for Output and OUT Feature reports.</param>
1339 private void DisplayReportData(Byte[] buffer, ReportTypes currentReportType, ReportReadOrWritten currentReadOrWritten)
1345 LstResults.Items.Add(currentReportType.ToString() + " report has been " + currentReadOrWritten.ToString().ToLower() + ".");
1347 // Display the report data received in the form's list box.
1349 LstResults.Items.Add(" Report ID: " + String.Format("{0:X2} ", buffer[0]));
1350 LstResults.Items.Add(" Report Data:");
1352 TxtBytesReceived.Text = "";
1354 for (count = 1; count <= buffer.Length - 1; count++)
1356 // Display bytes as 2-character Hex strings.
1358 String byteValue = String.Format("{0:X2} ", buffer[count]);
1360 LstResults.Items.Add(" " + byteValue);
1362 // Display the received bytes in the text box.
1364 TxtBytesReceived.SelectionStart = TxtBytesReceived.Text.Length;
1365 TxtBytesReceived.SelectedText = byteValue + Environment.NewLine;
1367 ScrollToBottomOfListBox();
1369 catch (Exception ex)
1371 DisplayException(Name, ex);
1377 /// Display a message if the user clicks a button when a transfer is in progress.
1380 private void DisplayTransferInProgressMessage()
1382 AccessForm(FormActions.AddItemToListBox, "Command not executed because a transfer is in progress.");
1383 ScrollToBottomOfListBox();
1387 /// Do periodic transfers.
1389 /// <param name="source"></param>
1390 /// <param name="e"></param>
1392 /// The timer is enabled only if continuous (periodic) transfers have been requested.
1395 private void DoPeriodicTransfers(object source, ElapsedEventArgs e)
1399 PeriodicTransfers();
1401 catch (Exception ex)
1403 DisplayException(Name, ex);
1409 /// Enable the command buttons on the form.
1410 /// Needed after attempting a transfer and device not found.
1413 private void EnableFormControls()
1415 cmdGetInputReportInterrupt.Enabled = true;
1416 cmdSendOutputReportControl.Enabled = true;
1417 cmdGetInputReportControl.Enabled = true;
1418 cmdGetFeatureReport.Enabled = true;
1419 cmdSendFeatureReport.Enabled = true;
1420 cmdPeriodicTransfers.Enabled = true;
1421 cmdSendOutputReportInterrupt.Enabled = true;
1425 /// Use the System.Management class to find a device by Vendor ID and Product ID using WMI. If found, display device properties.
1428 /// During debugging, if you stop the firmware but leave the device attached, the device may still be detected as present
1429 /// but will be unable to communicate. The device will show up in Windows Device Manager as well.
1430 /// This situation is unlikely to occur with a final product.
1433 private Boolean FindDeviceUsingWmi()
1437 MyMarshalDataToForm(FormActions.ResetDeviceTreeView);
1438 // Prepend "@" to string below to treat backslash as a normal character (not escape character):
1440 String deviceIdString = @"USB\VID_" + _myVendorId.ToString("X4") + "&PID_" + _myProductId.ToString("X4");
1442 _deviceDetected = false;
1443 var searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_PnPEntity");
1444 int usbDeviceCounter = 0;
1446 foreach (ManagementObject queryObj in searcher.Get())
1448 string deviceId = queryObj["PNPDeviceID"].ToString();
1449 if (deviceId.Contains(deviceIdString))
1451 _deviceDetected = true;
1452 MyMarshalDataToForm(FormActions.AddItemToListBox, "My device found (WMI):");
1454 MyMarshalDataToForm(FormActions.AddItemToListBox, "--------");
1455 List<string> device = new List<string>();
1456 device.Add(deviceId);
1458 // Display device properties.
1459 foreach (WmiDeviceProperties wmiDeviceProperty in Enum.GetValues(typeof(WmiDeviceProperties)))
1461 MyMarshalDataToForm(FormActions.AddItemToListBox, (wmiDeviceProperty.ToString() + ": " + queryObj[wmiDeviceProperty.ToString()]));
1462 device.Add((wmiDeviceProperty.ToString() + ": " + queryObj[wmiDeviceProperty.ToString()]));
1463 Debug.WriteLine(wmiDeviceProperty.ToString() + ": {0}", queryObj[wmiDeviceProperty.ToString()]);
1465 MyMarshalDataToForm(FormActions.AddItemToListBox, "--------");
1466 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1468 MyMarshalDataToForm(FormActions.AddDeviceToTreeView, device.ToArray());
1469 MyMarshalDataToForm(FormActions.SelectDeviceInTreeView, deviceId);
1472 else if (deviceId.StartsWith("USB\\VID"))
1474 List<string> device = new List<string>();
1475 device.Add(deviceId);
1477 // Add device properties.
1478 foreach (WmiDeviceProperties wmiDeviceProperty in Enum.GetValues(typeof(WmiDeviceProperties)))
1480 device.Add((wmiDeviceProperty.ToString() + ": " + queryObj[wmiDeviceProperty.ToString()]));
1481 Debug.WriteLine(wmiDeviceProperty.ToString() + ": {0}", queryObj[wmiDeviceProperty.ToString()]);
1484 MyMarshalDataToForm(FormActions.AddDeviceToTreeView, device.ToArray());
1488 //Complete our device TreeView
1489 MyMarshalDataToForm(FormActions.CompleteDeviceTreeView);
1492 MyMarshalDataToForm(FormActions.AddItemToListBox, "Found " + usbDeviceCounter /*searcher.Get().Count*/ + " USB HID devices");
1494 if (!_deviceDetected)
1496 MyMarshalDataToForm(FormActions.AddItemToListBox, "My device not found (WMI)");
1497 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1499 return _deviceDetected;
1501 catch (Exception ex)
1503 DisplayException(Name, ex);
1509 /// Call HID functions that use Win32 API functions to locate a HID-class device
1510 /// by its Vendor ID and Product ID. Open a handle to the device.
1514 /// True if the device is detected, False if not detected.
1517 private Boolean FindTheHid()
1519 var devicePathName = new String[128];
1520 String myDevicePathName = "";
1524 _deviceHandleObtained = false;
1525 CloseCommunications();
1527 // Get the device's Vendor ID and Product ID from the form's text boxes.
1529 GetVendorAndProductIDsFromTextBoxes(ref _myVendorId, ref _myProductId);
1531 // Get the HID-class GUID.
1533 Guid hidGuid = _myHid.GetHidGuid();
1535 String functionName = "GetHidGuid";
1536 Debug.WriteLine(_myDebugging.ResultOfApiCall(functionName));
1537 Debug.WriteLine(" GUID for system HIDs: " + hidGuid.ToString());
1539 // Fill an array with the device path names of all attached HIDs.
1541 Boolean availableHids = _myDeviceManagement.FindDeviceFromGuid(hidGuid, ref devicePathName);
1543 // If there is at least one HID, attempt to read the Vendor ID and Product ID
1544 // of each device until there is a match or all devices have been examined.
1548 Int32 memberIndex = 0;
1552 // Open the handle without read/write access to enable getting information about any HID, even system keyboards and mice.
1554 _hidHandle = _myHid.OpenHandle(devicePathName[memberIndex], false);
1556 functionName = "CreateFile";
1557 Debug.WriteLine(_myDebugging.ResultOfApiCall(functionName));
1558 Debug.WriteLine(" Returned handle: " + _hidHandle);
1560 if (!_hidHandle.IsInvalid)
1562 // The returned handle is valid,
1563 // so find out if this is the device we're looking for.
1565 _myHid.DeviceAttributes.Size = Marshal.SizeOf(_myHid.DeviceAttributes);
1567 Boolean success = _myHid.GetAttributes(_hidHandle, ref _myHid.DeviceAttributes);
1571 Debug.WriteLine(" HIDD_ATTRIBUTES structure filled without error.");
1572 Debug.WriteLine(" Structure size: " + _myHid.DeviceAttributes.Size);
1573 Debug.WriteLine(" Vendor ID: " + Convert.ToString(_myHid.DeviceAttributes.VendorID, 16));
1574 Debug.WriteLine(" Product ID: " + Convert.ToString(_myHid.DeviceAttributes.ProductID, 16));
1575 Debug.WriteLine(" Version Number: " + Convert.ToString(_myHid.DeviceAttributes.VersionNumber, 16));
1577 if ((_myHid.DeviceAttributes.VendorID == _myVendorId) && (_myHid.DeviceAttributes.ProductID == _myProductId))
1579 Debug.WriteLine(" Handle obtained to my device");
1581 // Display the information in form's list box.
1583 MyMarshalDataToForm(FormActions.AddItemToListBox, "Handle obtained to my device:");
1584 MyMarshalDataToForm(FormActions.AddItemToListBox, " Vendor ID= " + Convert.ToString(_myHid.DeviceAttributes.VendorID, 16));
1585 MyMarshalDataToForm(FormActions.AddItemToListBox, " Product ID = " + Convert.ToString(_myHid.DeviceAttributes.ProductID, 16));
1586 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1588 _deviceHandleObtained = true;
1590 myDevicePathName = devicePathName[memberIndex];
1594 // It's not a match, so close the handle.
1596 _deviceHandleObtained = false;
1602 // There was a problem retrieving the information.
1604 Debug.WriteLine(" Error in filling HIDD_ATTRIBUTES structure.");
1605 _deviceHandleObtained = false;
1610 // Keep looking until we find the device or there are no devices left to examine.
1612 memberIndex = memberIndex + 1;
1614 while (!((_deviceHandleObtained || (memberIndex == devicePathName.Length))));
1617 if (_deviceHandleObtained)
1619 // The device was detected.
1620 // Learn the capabilities of the device.
1622 _myHid.Capabilities = _myHid.GetDeviceCapabilities(_hidHandle);
1624 // Find out if the device is a system mouse or keyboard.
1626 _hidUsage = _myHid.GetHidUsage(_myHid.Capabilities);
1628 // Get the Input report buffer size.
1630 GetInputReportBufferSize();
1631 MyMarshalDataToForm(FormActions.EnableInputReportBufferSize, "");
1633 //Close the handle and reopen it with read/write access.
1637 _hidHandle = _myHid.OpenHandle(myDevicePathName, true);
1639 if (_hidHandle.IsInvalid)
1641 MyMarshalDataToForm(FormActions.AddItemToListBox, "The device is a system " + _hidUsage + ".");
1642 MyMarshalDataToForm(FormActions.AddItemToListBox, "Windows 2000 and later obtain exclusive access to Input and Output reports for this devices.");
1643 MyMarshalDataToForm(FormActions.AddItemToListBox, "Windows 8 also obtains exclusive access to Feature reports.");
1644 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1648 if (_myHid.Capabilities.InputReportByteLength > 0)
1650 // Set the size of the Input report buffer.
1652 var inputReportBuffer = new Byte[_myHid.Capabilities.InputReportByteLength];
1654 _deviceData = new FileStream(_hidHandle, FileAccess.Read | FileAccess.Write, inputReportBuffer.Length, false);
1657 if (_myHid.Capabilities.OutputReportByteLength > 0)
1659 Byte[] outputReportBuffer = null;
1661 // Flush any waiting reports in the input buffer. (optional)
1663 _myHid.FlushQueue(_hidHandle);
1668 MyMarshalDataToForm(FormActions.AddItemToListBox, "Device not found.");
1669 MyMarshalDataToForm(FormActions.DisableInputReportBufferSize, "");
1670 EnableFormControls();
1671 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1673 return _deviceHandleObtained;
1675 catch (Exception ex)
1677 DisplayException(Name, ex);
1683 /// Perform shutdown operations.
1686 private void frmMain_Closed(Object eventSender, EventArgs eventArgs)
1692 catch (Exception ex)
1694 DisplayException(Name, ex);
1700 /// Perform startup operations.
1703 private void frmMain_Load(Object eventSender, EventArgs eventArgs)
1710 catch (Exception ex)
1712 DisplayException(Name, ex);
1717 private void GetBytesToSend()
1721 // Get the bytes to send in a report from the combo boxes.
1722 // Increment the values if the autoincrement check box is selected.
1724 if (ChkAutoincrement.Checked)
1726 if (CboByte0.SelectedIndex < 255)
1728 CboByte0.SelectedIndex = CboByte0.SelectedIndex + 1;
1732 CboByte0.SelectedIndex = 0;
1734 if (CboByte1.SelectedIndex < 255)
1736 CboByte1.SelectedIndex = CboByte1.SelectedIndex + 1;
1740 CboByte1.SelectedIndex = 0;
1744 catch (Exception ex)
1746 DisplayException(Name, ex);
1752 /// Find and display the number of Input buffers
1753 /// (the number of Input reports the HID driver will store).
1756 private void GetInputReportBufferSize()
1758 Int32 numberOfInputBuffers = 0;
1763 // Get the number of input buffers.
1765 _myHid.GetNumberOfInputBuffers(_hidHandle, ref numberOfInputBuffers);
1767 // Display the result in the text box.
1769 MyMarshalDataToForm(FormActions.SetInputReportBufferSize, Convert.ToString(numberOfInputBuffers));
1771 catch (Exception ex)
1773 DisplayException(Name, ex);
1779 /// Retrieve a Vendor ID and Product ID in hexadecimal
1780 /// from the form's text boxes and convert the text to Int32s.
1783 /// <param name="myVendorId"> the Vendor ID</param>
1784 /// <param name="myProductId"> the Product ID</param>
1786 private void GetVendorAndProductIDsFromTextBoxes(ref Int32 myVendorId, ref Int32 myProductId)
1790 myVendorId = Int32.Parse(txtVendorID.Text, NumberStyles.AllowHexSpecifier);
1791 myProductId = Int32.Parse(txtProductID.Text, NumberStyles.AllowHexSpecifier);
1793 catch (Exception ex)
1795 DisplayException(Name, ex);
1801 /// Initialize the elements on the form.
1804 private void InitializeDisplay()
1808 // Create a dropdown list box for each byte to send in a report.
1809 // Display the values as 2-character hex strings.
1812 for (count = 0; count <= 255; count++)
1814 String byteValue = String.Format("{0:X2} ", count);
1815 FrmMy.CboByte0.Items.Insert(count, byteValue);
1816 FrmMy.CboByte1.Items.Insert(count, byteValue);
1819 // Select a default value for each box
1821 FrmMy.CboByte0.SelectedIndex = 0;
1822 FrmMy.CboByte1.SelectedIndex = 128;
1823 FrmMy.radInputOutputInterrupt.Checked = true;
1825 // Check the autoincrement box to increment the values each time a report is sent.
1827 ChkAutoincrement.CheckState = CheckState.Checked;
1829 // Don't allow the user to select an input report buffer size until there is
1830 // a handle to a HID.
1832 cmdInputReportBufferSize.Focus();
1833 cmdInputReportBufferSize.Enabled = false;
1835 LstResults.Items.Add("For a more detailed event log, view debug statements in Visual Studio's Output window:");
1836 LstResults.Items.Add("Click Build > Configuration Manager > Active Solution Configuration > Debug > Close.");
1837 LstResults.Items.Add("Then click View > Output.");
1838 LstResults.Items.Add("");
1840 catch (Exception ex)
1842 DisplayException(Name, ex);
1848 /// Enables accessing a form's controls from another thread
1851 /// <param name="action"> a FormActions member that names the action to perform on the form </param>
1852 /// <param name="textToDisplay"> text that the form displays or the code uses for
1853 /// another purpose. Actions that don't use text ignore this parameter. </param>
1855 private void MyMarshalDataToForm(FormActions action, params string[] strings)
1859 object[] args = { action, strings };
1861 // The AccessForm routine contains the code that accesses the form.
1863 MarshalDataToForm marshalDataToFormDelegate = AccessForm;
1865 // Execute AccessForm, passing the parameters in args.
1867 Invoke(marshalDataToFormDelegate, args);
1869 catch (Exception ex)
1871 DisplayException(Name, ex);
1877 /// Timeout if read via interrupt transfer doesn't return.
1880 private void OnReadTimeout()
1884 MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to read a report timed out.");
1885 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1886 CloseCommunications();
1887 MyMarshalDataToForm(FormActions.EnableGetInputReportInterruptTransfer, "");
1888 _transferInProgress = false;
1889 _sendOrGet = SendOrGet.Send;
1891 catch (Exception ex)
1893 DisplayException(Name, ex);
1899 /// Timeout if write via interrupt transfer doesn't return.
1902 private void OnWriteTimeout()
1906 MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to write a report timed out.");
1907 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1908 CloseCommunications();
1909 MyMarshalDataToForm(FormActions.EnableSendOutputReportInterrupt, "");
1910 _transferInProgress = false;
1911 _sendOrGet = SendOrGet.Get;
1913 catch (Exception ex)
1915 DisplayException(Name, ex);
1921 /// Alternat sending and getting a report.
1924 private void PeriodicTransfers()
1928 if (!_transferInProgress)
1930 if (_reportType == ReportTypes.Feature)
1932 SendOrGetFeatureReport();
1936 // Output and Input reports
1938 SendOutputReportOrGetInputReport();
1942 catch (Exception ex)
1944 DisplayException(Name, ex);
1950 /// Start doing periodic transfers.
1953 private void PeriodicTransfersStart()
1955 // Don't allow changing the transfer type while transfers are in progress.
1957 if (radFeature.Checked)
1959 radInputOutputControl.Enabled = false;
1960 radInputOutputInterrupt.Enabled = false;
1962 else if (radInputOutputControl.Checked)
1964 radFeature.Enabled = false;
1965 radInputOutputInterrupt.Enabled = false;
1967 else if (radInputOutputInterrupt.Checked)
1969 radFeature.Enabled = false;
1970 radInputOutputControl.Enabled = false;
1973 // Change the command button's text.
1975 cmdPeriodicTransfers.Text = "Stop";
1977 // Enable the timer event to trigger a set of transfers.
1979 _periodicTransfers.Start();
1981 cmdPeriodicTransfers.Enabled = true;
1983 if (radInputOutputInterrupt.Checked)
1985 _transferType = TransferTypes.Interrupt;
1986 _reportType = ReportTypes.Output;
1988 else if (radInputOutputControl.Checked)
1990 _transferType = TransferTypes.Control;
1991 _reportType = ReportTypes.Output;
1993 else if (radFeature.Checked)
1995 _transferType = TransferTypes.Control;
1996 _reportType = ReportTypes.Feature;
1998 _periodicTransfersRequested = true;
1999 PeriodicTransfers();
2003 /// Stop doing periodic transfers.
2006 private void PeriodicTransfersStop()
2008 // Stop doing continuous transfers.
2010 _periodicTransfersRequested = false;
2012 // Disable the timer that triggers the transfers.
2014 _periodicTransfers.Stop();
2015 cmdPeriodicTransfers.Enabled = true;
2017 // Change the command button's text.
2019 cmdPeriodicTransfers.Text = "Start";
2021 // Re-allow changing the transfer type.
2023 radFeature.Enabled = true;
2024 radInputOutputControl.Enabled = true;
2025 radInputOutputInterrupt.Enabled = true;
2028 private void radInputOutputControl_CheckedChanged(object sender, EventArgs e)
2032 private void radInputOutputInterrupt_CheckedChanged(object sender, EventArgs e)
2036 private void radFeature_CheckedChanged(object sender, EventArgs e)
2041 /// Request a Feature report.
2042 /// Assumes report ID = 0.
2045 private void RequestToGetFeatureReport()
2047 String byteValue = null;
2051 // If the device hasn't been detected, was removed, or timed out on a previous attempt
2052 // to access it, look for the device.
2054 if (!_deviceHandleObtained)
2056 _deviceHandleObtained = FindTheHid();
2059 if (_deviceHandleObtained)
2061 Byte[] inFeatureReportBuffer = null;
2063 if ((_myHid.Capabilities.FeatureReportByteLength > 0))
2065 // The HID has a Feature report.
2066 // Read a report from the device.
2068 // Set the size of the Feature report buffer.
2070 if ((_myHid.Capabilities.FeatureReportByteLength > 0))
2072 inFeatureReportBuffer = new Byte[_myHid.Capabilities.FeatureReportByteLength];
2077 Boolean success = _myHid.GetFeatureReport(_hidHandle, ref inFeatureReportBuffer);
2081 DisplayReportData(inFeatureReportBuffer, ReportTypes.Feature, ReportReadOrWritten.Read);
2085 CloseCommunications();
2086 MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to read a Feature report failed.");
2087 ScrollToBottomOfListBox();
2092 MyMarshalDataToForm(FormActions.AddItemToListBox, "The HID doesn't have a Feature report.");
2093 ScrollToBottomOfListBox();
2096 _transferInProgress = false;
2097 cmdGetFeatureReport.Enabled = true;
2099 catch (Exception ex)
2101 DisplayException(Name, ex);
2107 /// Request an Input report.
2108 /// Assumes report ID = 0.
2111 private async void RequestToGetInputReport()
2113 const Int32 readTimeout = 5000;
2115 String byteValue = null;
2116 Byte[] inputReportBuffer = null;
2120 Boolean success = false;
2122 // If the device hasn't been detected, was removed, or timed out on a previous attempt
2123 // to access it, look for the device.
2125 if (!_deviceHandleObtained)
2127 _deviceHandleObtained = FindTheHid();
2130 if (_deviceHandleObtained)
2132 // Don't attempt to exchange reports if valid handles aren't available
2133 // (as for a mouse or keyboard under Windows 2000 and later.)
2135 if (!_hidHandle.IsInvalid)
2137 // Read an Input report.
2139 // Don't attempt to send an Input report if the HID has no Input report.
2140 // (The HID spec requires all HIDs to have an interrupt IN endpoint,
2141 // which suggests that all HIDs must support Input reports.)
2143 if (_myHid.Capabilities.InputReportByteLength > 0)
2145 // Set the size of the Input report buffer.
2147 inputReportBuffer = new Byte[_myHid.Capabilities.InputReportByteLength];
2149 if (_transferType.Equals(TransferTypes.Control))
2152 _transferInProgress = true;
2154 // Read a report using a control transfer.
2156 success = _myHid.GetInputReportViaControlTransfer(_hidHandle, ref inputReportBuffer);
2157 cmdGetInputReportControl.Enabled = true;
2158 _transferInProgress = false;
2164 _transferInProgress = true;
2166 // Read a report using interrupt transfers.
2167 // Timeout if no report available.
2168 // To enable reading a report without blocking the calling thread, uses Filestream's ReadAsync method.
2170 // Create a delegate to execute on a timeout.
2172 Action onReadTimeoutAction = OnReadTimeout;
2174 // The CancellationTokenSource specifies the timeout value and the action to take on a timeout.
2176 var cts = new CancellationTokenSource();
2178 // Cancel the read if it hasn't completed after a timeout.
2180 cts.CancelAfter(readTimeout);
2182 // Specify the function to call on a timeout.
2184 cts.Token.Register(onReadTimeoutAction);
2186 // Stops waiting when data is available or on timeout:
2188 Int32 bytesRead = await _myHid.GetInputReportViaInterruptTransfer(_deviceData, inputReportBuffer, cts);
2190 // Arrive here only if the operation completed.
2192 // Dispose to stop the timeout timer.
2196 _transferInProgress = false;
2197 cmdGetInputReportInterrupt.Enabled = true;
2202 Debug.Print("bytes read (includes report ID) = " + Convert.ToString(bytesRead));
2209 MyMarshalDataToForm(FormActions.AddItemToListBox, "No attempt to read an Input report was made.");
2210 MyMarshalDataToForm(FormActions.AddItemToListBox, "The HID doesn't have an Input report.");
2215 MyMarshalDataToForm(FormActions.AddItemToListBox, "Invalid handle.");
2216 MyMarshalDataToForm(FormActions.AddItemToListBox,
2217 "No attempt to write an Output report or read an Input report was made.");
2222 DisplayReportData(inputReportBuffer, ReportTypes.Input, ReportReadOrWritten.Read);
2226 CloseCommunications();
2227 MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to read an Input report has failed.");
2228 ScrollToBottomOfListBox();
2232 catch (Exception ex)
2234 DisplayException(Name, ex);
2240 /// Sends a Feature report.
2241 /// Assumes report ID = 0.
2244 private void RequestToSendFeatureReport()
2246 String byteValue = null;
2250 _transferInProgress = true;
2252 // If the device hasn't been detected, was removed, or timed out on a previous attempt
2253 // to access it, look for the device.
2255 if (!_deviceHandleObtained)
2257 _deviceHandleObtained = FindTheHid();
2260 if (_deviceHandleObtained)
2264 if ((_myHid.Capabilities.FeatureReportByteLength > 0))
2266 // The HID has a Feature report.
2267 // Set the size of the Feature report buffer.
2269 var outFeatureReportBuffer = new Byte[_myHid.Capabilities.FeatureReportByteLength];
2271 // Store the report ID in the buffer.
2273 outFeatureReportBuffer[0] = 0;
2275 // Store the report data following the report ID.
2276 // Use the data in the combo boxes on the form.
2278 outFeatureReportBuffer[1] = Convert.ToByte(CboByte0.SelectedIndex);
2280 if (outFeatureReportBuffer.GetUpperBound(0) > 1)
2282 outFeatureReportBuffer[2] = Convert.ToByte(CboByte1.SelectedIndex);
2285 // Write a report to the device
2287 Boolean success = _myHid.SendFeatureReport(_hidHandle, outFeatureReportBuffer);
2291 DisplayReportData(outFeatureReportBuffer, ReportTypes.Feature, ReportReadOrWritten.Written);
2295 CloseCommunications();
2296 AccessForm(FormActions.AddItemToListBox, "The attempt to send a Feature report failed.");
2297 ScrollToBottomOfListBox();
2302 AccessForm(FormActions.AddItemToListBox, "The HID doesn't have a Feature report.");
2303 ScrollToBottomOfListBox();
2307 _transferInProgress = false;
2308 cmdSendFeatureReport.Enabled = true;
2309 ScrollToBottomOfListBox();
2312 catch (Exception ex)
2314 DisplayException(Name, ex);
2320 /// Sends an Output report.
2321 /// Assumes report ID = 0.
2324 private async void RequestToSendOutputReport()
2326 const Int32 writeTimeout = 5000;
2327 String byteValue = null;
2331 // If the device hasn't been detected, was removed, or timed out on a previous attempt
2332 // to access it, look for the device.
2334 if (!_deviceHandleObtained)
2336 _deviceHandleObtained = FindTheHid();
2339 if (_deviceHandleObtained)
2343 // Don't attempt to exchange reports if valid handles aren't available
2344 // (as for a mouse or keyboard.)
2346 if (!_hidHandle.IsInvalid)
2348 // Don't attempt to send an Output report if the HID has no Output report.
2350 if (_myHid.Capabilities.OutputReportByteLength > 0)
2352 // Set the size of the Output report buffer.
2354 var outputReportBuffer = new Byte[_myHid.Capabilities.OutputReportByteLength];
2356 // Store the report ID in the first byte of the buffer:
2358 outputReportBuffer[0] = 0;
2360 // Store the report data following the report ID.
2361 // Use the data in the combo boxes on the form.
2363 outputReportBuffer[1] = Convert.ToByte(CboByte0.SelectedIndex);
2365 if (outputReportBuffer.GetUpperBound(0) > 1)
2367 outputReportBuffer[2] = Convert.ToByte(CboByte1.SelectedIndex);
2374 if (_transferType.Equals(TransferTypes.Control))
2377 _transferInProgress = true;
2379 // Use a control transfer to send the report,
2380 // even if the HID has an interrupt OUT endpoint.
2382 success = _myHid.SendOutputReportViaControlTransfer(_hidHandle, outputReportBuffer);
2384 _transferInProgress = false;
2385 cmdSendOutputReportControl.Enabled = true;
2390 Debug.Print("interrupt");
2391 _transferInProgress = true;
2393 // The CancellationTokenSource specifies the timeout value and the action to take on a timeout.
2395 var cts = new CancellationTokenSource();
2397 // Create a delegate to execute on a timeout.
2399 Action onWriteTimeoutAction = OnWriteTimeout;
2401 // Cancel the read if it hasn't completed after a timeout.
2403 cts.CancelAfter(writeTimeout);
2405 // Specify the function to call on a timeout.
2407 cts.Token.Register(onWriteTimeoutAction);
2409 // Send an Output report and wait for completion or timeout.
2411 success = await _myHid.SendOutputReportViaInterruptTransfer(_deviceData, _hidHandle, outputReportBuffer, cts);
2413 // Get here only if the operation completes without a timeout.
2415 _transferInProgress = false;
2416 cmdSendOutputReportInterrupt.Enabled = true;
2418 // Dispose to stop the timeout timer.
2424 DisplayReportData(outputReportBuffer, ReportTypes.Output, ReportReadOrWritten.Written);
2428 CloseCommunications();
2429 AccessForm(FormActions.AddItemToListBox, "The attempt to write an Output report failed.");
2430 ScrollToBottomOfListBox();
2436 AccessForm(FormActions.AddItemToListBox, "The HID doesn't have an Output report.");
2439 catch (Exception ex)
2441 DisplayException(Name, ex);
2447 /// Scroll to the bottom of the list box and trim as needed.
2450 private void ScrollToBottomOfListBox()
2454 LstResults.SelectedIndex = LstResults.Items.Count - 1;
2456 // If the list box is getting too large, trim its contents by removing the earliest data.
2458 if (LstResults.Items.Count > 1000)
2461 for (count = 1; count <= 500; count++)
2463 LstResults.Items.RemoveAt(4);
2467 catch (Exception ex)
2469 DisplayException(Name, ex);
2475 /// Request to send or get a Feature report.
2478 private void SendOrGetFeatureReport()
2482 // If the device hasn't been detected, was removed, or timed out on a previous attempt
2483 // to access it, look for the device.
2485 if (!_deviceHandleObtained)
2487 _deviceHandleObtained = FindTheHid();
2490 if (_deviceHandleObtained)
2494 case SendOrGet.Send:
2495 RequestToSendFeatureReport();
2496 _sendOrGet = SendOrGet.Get;
2499 RequestToGetFeatureReport();
2500 _sendOrGet = SendOrGet.Send;
2505 catch (Exception ex)
2507 DisplayException(Name, ex);
2513 /// Request to send an Output report or get an Input report.
2516 private void SendOutputReportOrGetInputReport()
2520 // If the device hasn't been detected, was removed, or timed out on a previous attempt
2521 // to access it, look for the device.
2523 if (!_deviceHandleObtained)
2525 _deviceHandleObtained = FindTheHid();
2528 if (_deviceHandleObtained)
2530 if (_sendOrGet == SendOrGet.Send)
2532 RequestToSendOutputReport();
2533 _sendOrGet = SendOrGet.Get;
2537 RequestToGetInputReport();
2538 _sendOrGet = SendOrGet.Send;
2542 catch (Exception ex)
2544 DisplayException(Name, ex);
2550 /// Set the number of Input buffers (the number of Input reports
2551 /// the host will store) from the value in the text box.
2554 private void SetInputReportBufferSize()
2558 if (!_transferInProgress)
2560 // Get the number of buffers from the text box.
2562 Int32 numberOfInputBuffers = Convert.ToInt32(txtInputReportBufferSize.Text);
2564 // Set the number of buffers.
2566 _myHid.SetNumberOfInputBuffers(_hidHandle, numberOfInputBuffers);
2568 // Verify and display the result.
2570 GetInputReportBufferSize();
2574 DisplayTransferInProgressMessage();
2577 catch (Exception ex)
2579 DisplayException(Name, ex);
2585 /// Perform actions that must execute when the program ends.
2588 private void Shutdown()
2592 CloseCommunications();
2593 DeviceNotificationsStop();
2595 catch (Exception ex)
2597 DisplayException(Name, ex);
2603 /// Perform actions that must execute when the program starts.
2606 private void Startup()
2608 const Int32 periodicTransferInterval = 1000;
2612 InitializeDisplay();
2614 _periodicTransfers = new System.Timers.Timer(periodicTransferInterval);
2615 _periodicTransfers.Elapsed += DoPeriodicTransfers;
2616 _periodicTransfers.Stop();
2617 _periodicTransfers.SynchronizingObject = this;
2619 // Default USB Vendor ID and Product ID:
2621 txtVendorID.Text = "0925";
2622 txtProductID.Text = "7001";
2624 GetVendorAndProductIDsFromTextBoxes(ref _myVendorId, ref _myProductId);
2626 DeviceNotificationsStart();
2627 FindDeviceUsingWmi();
2630 catch (Exception ex)
2632 DisplayException(Name, ex);
2638 /// The Product ID has changed in the text box. Call a routine to handle it.
2641 private void txtProductID_TextChanged(Object sender, EventArgs e)
2647 catch (Exception ex)
2649 DisplayException(Name, ex);
2655 /// The Vendor ID has changed in the text box. Call a routine to handle it.
2658 private void txtVendorID_TextChanged(Object sender, EventArgs e)
2664 catch (Exception ex)
2666 DisplayException(Name, ex);
2672 /// Provides a central mechanism for exception handling.
2673 /// Displays a message box that describes the exception.
2676 /// <param name="moduleName"> the module where the exception occurred. </param>
2677 /// <param name="e"> the exception </param>
2679 internal static void DisplayException(String moduleName, Exception e)
2681 // Create an error message.
2683 String message = "Exception: " + e.Message + Environment.NewLine + "Module: " + moduleName + Environment.NewLine + "Method: " + e.TargetSite.Name;
2685 const String caption = "Unexpected Exception";
2687 MessageBox.Show(message, caption, MessageBoxButtons.OK);
2688 Debug.Write(message);
2690 // Get the last error and display it.
2692 Int32 error = Marshal.GetLastWin32Error();
2694 Debug.WriteLine("The last Win32 Error was: " + error);
2698 internal static void Main() { Application.Run(new FrmMain()); }
2699 private static FrmMain _transDefaultFormFrmMain;
2700 internal static FrmMain TransDefaultFormFrmMain
2704 if (_transDefaultFormFrmMain == null)
2706 _transDefaultFormFrmMain = new FrmMain();
2708 return _transDefaultFormFrmMain;