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;
13 using System.Text.RegularExpressions;
18 /// Project: GenericHid
20 /// ***********************************************************************
21 /// Software License Agreement
23 /// Licensor grants any person obtaining a copy of this software ("You")
24 /// a worldwide, royalty-free, non-exclusive license, for the duration of
25 /// the copyright, free of charge, to store and execute the Software in a
26 /// computer system and to incorporate the Software or any portion of it
27 /// in computer programs You write.
29 /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
36 /// ***********************************************************************
41 /// This software was written using Visual Studio Express 2012 for Windows
42 /// Desktop building for the .NET Framework v4.5.
45 /// Demonstrates USB communications with a generic HID-class device
48 /// Windows Vista or later and an attached USB generic Human Interface Device (HID).
49 /// (Does not run on Windows XP or earlier because .NET Framework 4.5 will not install on these OSes.)
52 /// Finds an attached device that matches the vendor and product IDs in the form's
55 /// Retrieves the device's capabilities.
56 /// Sends and requests HID reports.
58 /// Uses the System.Management class and Windows Management Instrumentation (WMI) to detect
59 /// when a device is attached or removed.
61 /// A list box displays the data sent and received along with error and status messages.
62 /// You can select data to send and 1-time or periodic transfers.
64 /// You can change the size of the host's Input report buffer and request to use control
65 /// transfers only to exchange Input and Output reports.
67 /// To view additional debugging messages, in the Visual Studio development environment,
68 /// from the main menu, select Build > Configuration Manager > Active Solution Configuration
69 /// and select Configuration > Debug and from the main menu, select View > Output.
71 /// The application uses asynchronous FileStreams to read Input reports and write Output
72 /// reports so the application's main thread doesn't have to wait for the device to retrieve a
73 /// report when the HID driver's buffer is empty or send a report when the device's endpoint is busy.
75 /// For code that finds a device and opens handles to it, see the FindTheHid routine in frmMain.cs.
76 /// For code that reads from the device, see GetInputReportViaInterruptTransfer,
77 /// GetInputReportViaControlTransfer, and GetFeatureReport in Hid.cs.
78 /// For code that writes to the device, see SendInputReportViaInterruptTransfer,
79 /// SendInputReportViaControlTransfer, and SendFeatureReport in Hid.cs.
81 /// This project includes the following modules:
83 /// GenericHid.cs - runs the application.
84 /// FrmMain.cs - routines specific to the form.
85 /// Hid.cs - routines specific to HID communications.
86 /// DeviceManagement.cs - routine for obtaining a handle to a device from its GUID.
87 /// Debugging.cs - contains a routine for displaying API error messages.
88 /// HidDeclarations.cs - Declarations for API functions used by Hid.cs.
89 /// FileIODeclarations.cs - Declarations for file-related API functions.
90 /// DeviceManagementDeclarations.cs - Declarations for API functions used by DeviceManagement.cs.
91 /// DebuggingDeclarations.cs - Declarations for API functions used by Debugging.cs.
93 /// Companion device firmware for several device CPUs is available from www.Lvr.com/hidpage.htm
94 /// You can use any generic HID (not a system mouse or keyboard) that sends and receives reports.
95 /// This application will not detect or communicate with non-HID-class devices.
97 /// For more information about HIDs and USB, and additional example device firmware to use
98 /// with this application, visit Lakeview Research at http://Lvr.com
99 /// Send comments, bug reports, etc. to jan@Lvr.com or post on my PORTS forum: http://www.lvr.com/forum
103 /// Disabled form buttons when a transfer is in progress.
104 /// Other minor edits for clarity and readability.
105 /// Will NOT run on Windows XP or earlier, see below.
109 /// Uses the .NET System.Management class to detect device arrival and removal with WMI instead of Win32 RegisterDeviceNotification.
110 /// Other minor edits.
111 /// Will NOT run on Windows XP or earlier, see below.
115 /// This version will NOT run on Windows XP or earlier because the code uses .NET Framework 4.5 to support asynchronous FileStreams.
116 /// The .NET Framework 4.5 redistributable is compatible with Windows 8, Windows 7 SP1, Windows Server 2008 R2 SP1,
117 /// Windows Server 2008 SP2, Windows Vista SP2, and Windows Vista SP3.
118 /// For compatibility, replaced ToInt32 with ToInt64 here:
119 /// IntPtr pDevicePathName = new IntPtr(detailDataBuffer.ToInt64() + 4);
121 /// if ((deviceNotificationHandle.ToInt64() == IntPtr.Zero.ToInt64()))
122 /// For compatibility if the charset isn't English, added System.Globalization.CultureInfo.InvariantCulture here:
123 /// if ((String.Compare(DeviceNameString, mydevicePathName, true, System.Globalization.CultureInfo.InvariantCulture) == 0))
124 /// Replaced all Microsoft.VisualBasic namespace code with other .NET equivalents.
125 /// Revised user interface for more flexibility.
126 /// Moved interrupt-transfer and other HID-specific code to Hid.cs.
127 /// Used JetBrains ReSharper to clean up the code: http://www.jetbrains.com/resharper/
131 /// Replaced ReadFile and WriteFile with FileStreams. Thanks to Joe Dunne and John on my Ports forum for tips on this.
132 /// Simplified Hid.cs.
133 /// Replaced the form timer with a system timer.
137 /// Supports Vendor IDs and Product IDs up to FFFFh.
141 /// Changed HIDD_ATTRIBUTES to use UInt16
145 /// Moved Free_ and similar to Finally blocks to ensure they execute.
149 /// Changes to support 64-bit systems, memory management, and other corrections.
150 /// Big thanks to Peter Nielsen.
154 internal class FrmMain
157 #region '"Windows Form Designer generated code "'
161 // This call is required by the Windows Form Designer.
162 InitializeComponent();
164 // Form overrides dispose to clean up the component list.
165 protected override void Dispose(bool Disposing1)
169 if (components != null)
171 components.Dispose();
174 base.Dispose(Disposing1);
177 // Required by the Windows Form Designer
178 private System.ComponentModel.IContainer components;
179 public System.Windows.Forms.ToolTip ToolTip1;
180 public System.Windows.Forms.TextBox TxtBytesReceived;
181 public System.Windows.Forms.GroupBox FraBytesReceived;
182 public System.Windows.Forms.CheckBox ChkAutoincrement;
183 public System.Windows.Forms.ComboBox CboByte1;
184 public System.Windows.Forms.ComboBox CboByte0;
185 public System.Windows.Forms.GroupBox FraBytesToSend;
186 public System.Windows.Forms.ListBox LstResults;
187 // NOTE: The following procedure is required by the Windows Form Designer
188 // It can be modified using the Windows Form Designer.
189 // Do not modify it using the code editor.
190 internal System.Windows.Forms.GroupBox fraInputReportBufferSize;
191 internal System.Windows.Forms.TextBox txtInputReportBufferSize;
192 internal System.Windows.Forms.Button cmdInputReportBufferSize;
193 internal System.Windows.Forms.GroupBox fraDeviceIdentifiers;
194 internal System.Windows.Forms.Label lblVendorID;
195 internal System.Windows.Forms.TextBox txtVendorID;
196 internal System.Windows.Forms.Label lblProductID;
197 internal System.Windows.Forms.TextBox txtProductID;
198 internal System.Windows.Forms.Button cmdFindDevice;
199 private Button cmdGetInputReportInterrupt;
200 public GroupBox fraInterruptTransfers;
201 private Button cmdSendOutputReportControl;
202 private Button cmdGetInputReportControl;
203 public GroupBox fraControlTransfers;
204 private Button cmdGetFeatureReport;
205 private Button cmdSendFeatureReport;
206 private Button cmdPeriodicTransfers;
207 public GroupBox fraSendAndGetContinuous;
208 private RadioButton radFeature;
209 private RadioButton radInputOutputControl;
210 private RadioButton radInputOutputInterrupt;
211 private TreeView treeViewDevices;
212 private Button cmdSendOutputReportInterrupt;
214 [System.Diagnostics.DebuggerStepThrough()]
215 private void InitializeComponent()
217 this.components = new System.ComponentModel.Container();
218 this.ToolTip1 = new System.Windows.Forms.ToolTip(this.components);
219 this.FraBytesReceived = new System.Windows.Forms.GroupBox();
220 this.TxtBytesReceived = new System.Windows.Forms.TextBox();
221 this.FraBytesToSend = new System.Windows.Forms.GroupBox();
222 this.ChkAutoincrement = new System.Windows.Forms.CheckBox();
223 this.CboByte1 = new System.Windows.Forms.ComboBox();
224 this.CboByte0 = new System.Windows.Forms.ComboBox();
225 this.LstResults = new System.Windows.Forms.ListBox();
226 this.fraInputReportBufferSize = new System.Windows.Forms.GroupBox();
227 this.cmdInputReportBufferSize = new System.Windows.Forms.Button();
228 this.txtInputReportBufferSize = new System.Windows.Forms.TextBox();
229 this.fraDeviceIdentifiers = new System.Windows.Forms.GroupBox();
230 this.txtProductID = new System.Windows.Forms.TextBox();
231 this.lblProductID = new System.Windows.Forms.Label();
232 this.txtVendorID = new System.Windows.Forms.TextBox();
233 this.lblVendorID = new System.Windows.Forms.Label();
234 this.cmdFindDevice = new System.Windows.Forms.Button();
235 this.cmdSendOutputReportInterrupt = new System.Windows.Forms.Button();
236 this.cmdGetInputReportInterrupt = new System.Windows.Forms.Button();
237 this.fraInterruptTransfers = new System.Windows.Forms.GroupBox();
238 this.cmdPeriodicTransfers = new System.Windows.Forms.Button();
239 this.cmdSendOutputReportControl = new System.Windows.Forms.Button();
240 this.cmdGetInputReportControl = new System.Windows.Forms.Button();
241 this.fraControlTransfers = new System.Windows.Forms.GroupBox();
242 this.cmdGetFeatureReport = new System.Windows.Forms.Button();
243 this.cmdSendFeatureReport = new System.Windows.Forms.Button();
244 this.fraSendAndGetContinuous = new System.Windows.Forms.GroupBox();
245 this.radFeature = new System.Windows.Forms.RadioButton();
246 this.radInputOutputControl = new System.Windows.Forms.RadioButton();
247 this.radInputOutputInterrupt = new System.Windows.Forms.RadioButton();
248 this.treeViewDevices = new System.Windows.Forms.TreeView();
249 this.FraBytesReceived.SuspendLayout();
250 this.FraBytesToSend.SuspendLayout();
251 this.fraInputReportBufferSize.SuspendLayout();
252 this.fraDeviceIdentifiers.SuspendLayout();
253 this.fraInterruptTransfers.SuspendLayout();
254 this.fraControlTransfers.SuspendLayout();
255 this.fraSendAndGetContinuous.SuspendLayout();
256 this.SuspendLayout();
260 this.FraBytesReceived.BackColor = System.Drawing.SystemColors.Control;
261 this.FraBytesReceived.Controls.Add(this.TxtBytesReceived);
262 this.FraBytesReceived.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
263 this.FraBytesReceived.ForeColor = System.Drawing.SystemColors.ControlText;
264 this.FraBytesReceived.Location = new System.Drawing.Point(658, 353);
265 this.FraBytesReceived.Name = "FraBytesReceived";
266 this.FraBytesReceived.RightToLeft = System.Windows.Forms.RightToLeft.No;
267 this.FraBytesReceived.Size = new System.Drawing.Size(112, 136);
268 this.FraBytesReceived.TabIndex = 4;
269 this.FraBytesReceived.TabStop = false;
270 this.FraBytesReceived.Text = "Bytes Received";
274 this.TxtBytesReceived.AcceptsReturn = true;
275 this.TxtBytesReceived.BackColor = System.Drawing.SystemColors.Window;
276 this.TxtBytesReceived.Cursor = System.Windows.Forms.Cursors.IBeam;
277 this.TxtBytesReceived.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
278 this.TxtBytesReceived.ForeColor = System.Drawing.SystemColors.WindowText;
279 this.TxtBytesReceived.Location = new System.Drawing.Point(18, 24);
280 this.TxtBytesReceived.MaxLength = 0;
281 this.TxtBytesReceived.Multiline = true;
282 this.TxtBytesReceived.Name = "TxtBytesReceived";
283 this.TxtBytesReceived.RightToLeft = System.Windows.Forms.RightToLeft.No;
284 this.TxtBytesReceived.Size = new System.Drawing.Size(72, 96);
285 this.TxtBytesReceived.TabIndex = 5;
289 this.FraBytesToSend.BackColor = System.Drawing.SystemColors.Control;
290 this.FraBytesToSend.Controls.Add(this.ChkAutoincrement);
291 this.FraBytesToSend.Controls.Add(this.CboByte1);
292 this.FraBytesToSend.Controls.Add(this.CboByte0);
293 this.FraBytesToSend.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
294 this.FraBytesToSend.ForeColor = System.Drawing.SystemColors.ControlText;
295 this.FraBytesToSend.Location = new System.Drawing.Point(776, 235);
296 this.FraBytesToSend.Name = "FraBytesToSend";
297 this.FraBytesToSend.RightToLeft = System.Windows.Forms.RightToLeft.No;
298 this.FraBytesToSend.Size = new System.Drawing.Size(160, 136);
299 this.FraBytesToSend.TabIndex = 1;
300 this.FraBytesToSend.TabStop = false;
301 this.FraBytesToSend.Text = "Bytes to Send";
305 this.ChkAutoincrement.BackColor = System.Drawing.SystemColors.Control;
306 this.ChkAutoincrement.Cursor = System.Windows.Forms.Cursors.Default;
307 this.ChkAutoincrement.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
308 this.ChkAutoincrement.ForeColor = System.Drawing.SystemColors.ControlText;
309 this.ChkAutoincrement.Location = new System.Drawing.Point(8, 96);
310 this.ChkAutoincrement.Name = "ChkAutoincrement";
311 this.ChkAutoincrement.RightToLeft = System.Windows.Forms.RightToLeft.No;
312 this.ChkAutoincrement.Size = new System.Drawing.Size(201, 35);
313 this.ChkAutoincrement.TabIndex = 6;
314 this.ChkAutoincrement.Text = "Autoincrement values";
315 this.ChkAutoincrement.UseVisualStyleBackColor = false;
319 this.CboByte1.BackColor = System.Drawing.SystemColors.Window;
320 this.CboByte1.Cursor = System.Windows.Forms.Cursors.Default;
321 this.CboByte1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
322 this.CboByte1.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
323 this.CboByte1.ForeColor = System.Drawing.SystemColors.WindowText;
324 this.CboByte1.Location = new System.Drawing.Point(8, 64);
325 this.CboByte1.Name = "CboByte1";
326 this.CboByte1.RightToLeft = System.Windows.Forms.RightToLeft.No;
327 this.CboByte1.Size = new System.Drawing.Size(101, 22);
328 this.CboByte1.TabIndex = 3;
332 this.CboByte0.BackColor = System.Drawing.SystemColors.Window;
333 this.CboByte0.Cursor = System.Windows.Forms.Cursors.Default;
334 this.CboByte0.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
335 this.CboByte0.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
336 this.CboByte0.ForeColor = System.Drawing.SystemColors.WindowText;
337 this.CboByte0.Location = new System.Drawing.Point(8, 24);
338 this.CboByte0.Name = "CboByte0";
339 this.CboByte0.RightToLeft = System.Windows.Forms.RightToLeft.No;
340 this.CboByte0.Size = new System.Drawing.Size(101, 22);
341 this.CboByte0.TabIndex = 2;
345 this.LstResults.BackColor = System.Drawing.SystemColors.Window;
346 this.LstResults.Cursor = System.Windows.Forms.Cursors.Default;
347 this.LstResults.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
348 this.LstResults.ForeColor = System.Drawing.SystemColors.WindowText;
349 this.LstResults.HorizontalScrollbar = true;
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(916, 251);
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(787, 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(573, 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(800, 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(502, 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(659, 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(475, 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(457, 461);
599 this.treeViewDevices.TabIndex = 18;
600 this.treeViewDevices.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeViewDevices_AfterSelect);
604 this.ClientSize = new System.Drawing.Size(940, 756);
605 this.Controls.Add(this.treeViewDevices);
606 this.Controls.Add(this.fraSendAndGetContinuous);
607 this.Controls.Add(this.fraControlTransfers);
608 this.Controls.Add(this.fraInterruptTransfers);
609 this.Controls.Add(this.cmdFindDevice);
610 this.Controls.Add(this.fraDeviceIdentifiers);
611 this.Controls.Add(this.fraInputReportBufferSize);
612 this.Controls.Add(this.FraBytesReceived);
613 this.Controls.Add(this.FraBytesToSend);
614 this.Controls.Add(this.LstResults);
615 this.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
616 this.Location = new System.Drawing.Point(21, 28);
617 this.Name = "FrmMain";
618 this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
619 this.Text = "Generic HID Tester";
620 this.Closed += new System.EventHandler(this.frmMain_Closed);
621 this.Load += new System.EventHandler(this.frmMain_Load);
622 this.FraBytesReceived.ResumeLayout(false);
623 this.FraBytesReceived.PerformLayout();
624 this.FraBytesToSend.ResumeLayout(false);
625 this.fraInputReportBufferSize.ResumeLayout(false);
626 this.fraInputReportBufferSize.PerformLayout();
627 this.fraDeviceIdentifiers.ResumeLayout(false);
628 this.fraDeviceIdentifiers.PerformLayout();
629 this.fraInterruptTransfers.ResumeLayout(false);
630 this.fraControlTransfers.ResumeLayout(false);
631 this.fraSendAndGetContinuous.ResumeLayout(false);
632 this.fraSendAndGetContinuous.PerformLayout();
633 this.ResumeLayout(false);
636 #endregion '"Windows Form Designer generated code "'
638 private Boolean _deviceDetected;
639 private IntPtr _deviceNotificationHandle;
640 private FileStream _deviceData;
641 private FormActions _formActions;
642 private SafeFileHandle _hidHandle;
643 private String _hidUsage;
644 private ManagementEventWatcher _deviceArrivedWatcher;
645 private Boolean _deviceHandleObtained;
646 private ManagementEventWatcher _deviceRemovedWatcher;
647 private Int32 _myProductId;
648 private Int32 _myVendorId;
649 private Boolean _periodicTransfersRequested;
650 private ReportReadOrWritten _readOrWritten;
651 private ReportTypes _reportType;
652 private SendOrGet _sendOrGet;
653 private Boolean _transferInProgress;
654 private TransferTypes _transferType;
656 private static System.Timers.Timer _periodicTransfers;
658 private readonly Debugging _myDebugging = new Debugging(); // For viewing results of API calls via Debug.Write.
659 private readonly DeviceManagement _myDeviceManagement = new DeviceManagement();
660 private Hid _myHid = new Hid();
662 private enum FormActions
665 DisableInputReportBufferSize,
666 EnableGetInputReportInterruptTransfer,
667 EnableInputReportBufferSize,
668 EnableSendOutputReportInterrupt,
669 ScrollToBottomOfListBox,
670 SetInputReportBufferSize,
673 SelectDeviceInTreeView,
674 CompleteDeviceTreeView
677 private enum ReportReadOrWritten
683 private enum ReportTypes
690 private enum SendOrGet
696 private enum TransferTypes
702 private enum WmiDeviceProperties
713 internal FrmMain FrmMy;
715 // This delegate has the same parameters as AccessForm.
716 // Used in accessing the application's form from a different thread.
718 private delegate void MarshalDataToForm(FormActions action, params string[] strings);
721 /// Performs various application-specific functions that
722 /// involve accessing the application's form.
725 /// <param name="action"> a FormActions member that names the action to perform on the form</param>
726 /// <param name="formText"> text that the form displays or the code uses for
727 /// another purpose. Actions that don't use text ignore this parameter. </param>
729 private void AccessForm(FormActions action, params string[] strings)
733 // Select an action to perform on the form:
737 case FormActions.AddItemToListBox:
739 LstResults.Items.Add(strings[0]);
742 case FormActions.DisableInputReportBufferSize:
744 cmdInputReportBufferSize.Enabled = false;
747 case FormActions.EnableGetInputReportInterruptTransfer:
749 cmdGetInputReportInterrupt.Enabled = true;
752 case FormActions.EnableInputReportBufferSize:
754 cmdInputReportBufferSize.Enabled = true;
757 case FormActions.EnableSendOutputReportInterrupt:
759 cmdSendOutputReportInterrupt.Enabled = true;
762 case FormActions.ScrollToBottomOfListBox:
764 LstResults.SelectedIndex = LstResults.Items.Count - 1;
767 case FormActions.SetInputReportBufferSize:
769 txtInputReportBufferSize.Text = strings[0];
772 case FormActions.AddDeviceToTreeView:
774 //Try and see if our device is already present
775 TreeNode[] res = treeViewDevices.Nodes.Find(strings[0], false);
776 foreach (TreeNode device in res)
778 if (device.ForeColor == Color.Red)
780 //Device was removed and has now been added back
781 device.ForeColor = Color.Green;
785 //Device was already there set back our device color to black
786 device.ForeColor = Color.Black;
792 //Our device is already there
796 //Build our node from our string array
797 TreeNode newNode = new TreeNode(strings[0]);
798 for (int i = 1; i < strings.Length; i++)
800 newNode.Nodes.Add(strings[i]);
801 if (strings[i].StartsWith("Name: "))
803 //Found our name property, update our node text
804 newNode.Text = strings[i].Substring(6, strings[i].Length - 6);
808 //New device color is green
809 newNode.ForeColor = Color.Green;
810 newNode.Name = strings[0]; //Set ID as name to make sure we can find it
811 treeViewDevices.Nodes.Add(newNode);
815 case FormActions.ResetDeviceTreeView:
817 //Mark all non removed/red device as purple/unknown
818 foreach (TreeNode device in treeViewDevices.Nodes)
820 if (device.ForeColor != Color.Red)
822 device.ForeColor = Color.Purple;
825 //treeViewDevices.Nodes.Clear();
829 case FormActions.CompleteDeviceTreeView:
831 //Our device list is now complete
832 foreach (TreeNode device in treeViewDevices.Nodes)
834 //Purple devices need to be marked as red for removed
835 if (device.ForeColor == Color.Purple)
837 device.ForeColor = Color.Red;
843 case FormActions.SelectDeviceInTreeView:
845 //treeViewDevices.SelectedNode = null;
846 TreeNode[] res = treeViewDevices.Nodes.Find(strings[0], false);
847 foreach (TreeNode device in res)
849 device.ForeColor = Color.Blue;
850 //treeViewDevices.SelectedNode = res[0];
851 //treeViewDevices.SelectedNode.ForeColor = Color.Blue;
861 DisplayException(Name, ex);
867 /// Add a handler to detect arrival of devices using WMI.
870 private void AddDeviceArrivedHandler()
872 const Int32 pollingIntervalSeconds = 3;
873 var scope = new ManagementScope("root\\CIMV2");
874 scope.Options.EnablePrivileges = true;
878 var q = new WqlEventQuery();
879 q.EventClassName = "__InstanceCreationEvent";
880 q.WithinInterval = new TimeSpan(0, 0, pollingIntervalSeconds);
881 q.Condition = @"TargetInstance ISA 'Win32_USBControllerdevice'";
882 _deviceArrivedWatcher = new ManagementEventWatcher(scope, q);
883 _deviceArrivedWatcher.EventArrived += DeviceAdded;
885 _deviceArrivedWatcher.Start();
889 Debug.WriteLine(e.Message);
890 if (_deviceArrivedWatcher != null)
891 _deviceArrivedWatcher.Stop();
896 /// Add a handler to detect removal of devices using WMI.
899 private void AddDeviceRemovedHandler()
901 const Int32 pollingIntervalSeconds = 3;
902 var scope = new ManagementScope("root\\CIMV2");
903 scope.Options.EnablePrivileges = true;
907 var q = new WqlEventQuery();
908 q.EventClassName = "__InstanceDeletionEvent";
909 q.WithinInterval = new TimeSpan(0, 0, pollingIntervalSeconds);
910 q.Condition = @"TargetInstance ISA 'Win32_USBControllerdevice'";
911 _deviceRemovedWatcher = new ManagementEventWatcher(scope, q);
912 _deviceRemovedWatcher.EventArrived += DeviceRemoved;
913 _deviceRemovedWatcher.Start();
917 Debug.WriteLine(e.Message);
918 if (_deviceRemovedWatcher != null)
919 _deviceRemovedWatcher.Stop();
924 /// Close the handle and FileStreams for a device.
927 private void CloseCommunications()
929 if (_deviceData != null)
934 if ((_hidHandle != null) && (!(_hidHandle.IsInvalid)))
939 // The next attempt to communicate will get a new handle and FileStreams.
941 _deviceHandleObtained = false;
945 /// Search for a specific device.
948 private void cmdFindDevice_Click(Object sender, EventArgs e)
952 if (_transferInProgress)
954 DisplayTransferInProgressMessage();
958 _deviceDetected = FindDeviceUsingWmi();
967 DisplayException(Name, ex);
973 /// Request to get a Feature report from the device.
975 /// <param name="sender"></param>
976 /// <param name="e"></param>
978 private void cmdGetFeatureReport_Click(object sender, EventArgs e)
982 if (_transferInProgress)
984 DisplayTransferInProgressMessage();
988 // Don't allow another transfer request until this one completes.
989 // Move the focus away from the button to prevent the focus from
990 // switching to the next control in the tab order on disabling the button.
992 fraControlTransfers.Focus();
993 cmdGetFeatureReport.Enabled = false;
994 _transferType = TransferTypes.Control;
995 RequestToGetFeatureReport();
1000 DisplayException(Name, ex);
1006 /// Request to get an Input report from the device using a control transfer.
1008 /// <param name="sender"></param>
1009 /// <param name="e"></param>
1011 private void cmdGetInputReportControl_Click(object sender, EventArgs e)
1015 // Don't allow another transfer request until this one completes.
1016 // Move the focus away from the button to prevent the focus from
1017 // switching to the next control in the tab order on disabling the button.
1019 if (_transferInProgress)
1021 DisplayTransferInProgressMessage();
1025 fraControlTransfers.Focus();
1026 cmdGetInputReportControl.Enabled = false;
1027 _transferType = TransferTypes.Control;
1028 RequestToGetInputReport();
1031 catch (Exception ex)
1033 DisplayException(Name, ex);
1039 /// Request to get an Input report retrieved using interrupt transfers.
1041 /// <param name="sender"></param>
1042 /// <param name="e"></param>
1044 private void cmdGetInputReportInterrupt_Click(object sender, EventArgs e)
1048 if (_transferInProgress)
1050 DisplayTransferInProgressMessage();
1054 // Don't allow another transfer request until this one completes.
1055 // Move the focus away from the button to prevent the focus from
1056 // switching to the next control in the tab order on disabling the button.
1058 fraInterruptTransfers.Focus();
1059 cmdGetInputReportInterrupt.Enabled = false;
1060 _transferType = TransferTypes.Interrupt;
1061 RequestToGetInputReport();
1064 catch (Exception ex)
1066 DisplayException(Name, ex);
1072 /// Set the number of Input reports the HID driver will store.
1075 private void cmdInputReportBufferSize_Click(Object sender, EventArgs e)
1079 if (_transferInProgress)
1081 DisplayTransferInProgressMessage();
1085 SetInputReportBufferSize();
1091 DisplayException(Name, ex);
1097 /// Alternate sending and getting a report.
1099 /// <param name="sender"></param>
1100 /// <param name="e"></param>
1102 private void cmdPeriodicTransfers_Click(object sender, EventArgs e)
1106 if (cmdPeriodicTransfers.Text == "Start")
1108 if (_transferInProgress)
1110 DisplayTransferInProgressMessage();
1114 _sendOrGet = SendOrGet.Send;
1115 PeriodicTransfersStart();
1120 PeriodicTransfersStop();
1123 catch (Exception ex)
1125 DisplayException(Name, ex);
1131 /// Request to send a Feature report using a control transfer.
1133 /// <param name="sender"></param>
1134 /// <param name="e"></param>
1136 private void cmdSendFeatureReport_Click(object sender, EventArgs e)
1140 if (_transferInProgress)
1142 DisplayTransferInProgressMessage();
1146 // Don't allow another transfer request until this one completes.
1147 // Move the focus away from the button to prevent the focus from
1148 // switching to the next control in the tab order on disabling the button.
1150 fraControlTransfers.Focus();
1151 cmdSendFeatureReport.Enabled = false;
1152 _transferType = TransferTypes.Control;
1153 RequestToSendFeatureReport();
1156 catch (Exception ex)
1158 DisplayException(Name, ex);
1164 /// Request to send an Output report using a control transfer.
1166 /// <param name="sender"></param>
1167 /// <param name="e"></param>
1169 private void cmdSendOutputReportControl_Click(object sender, EventArgs e)
1173 if (_transferInProgress)
1175 DisplayTransferInProgressMessage();
1179 // Don't allow another transfer request until this one completes.
1180 // Move the focus away from the button to prevent the focus from
1181 // switching to the next control in the tab order on disabling the button.
1183 fraControlTransfers.Focus();
1184 cmdSendOutputReportControl.Enabled = false;
1185 _transferType = TransferTypes.Control;
1186 RequestToSendOutputReport();
1189 catch (Exception ex)
1191 DisplayException(Name, ex);
1197 /// Request to send an Output report using an interrupt transfer.
1199 /// <param name="sender"></param>
1200 /// <param name="e"></param>
1202 private void cmdSendOutputReportInterrupt_Click(object sender, EventArgs e)
1206 if (_transferInProgress)
1208 DisplayTransferInProgressMessage();
1212 // Don't allow another transfer request until this one completes.
1213 // Move the focus away from the button to prevent the focus from
1214 // switching to the next control in the tab order on disabling the button.
1216 fraInterruptTransfers.Focus();
1217 cmdSendOutputReportInterrupt.Enabled = false;
1218 _transferType = TransferTypes.Interrupt;
1219 RequestToSendOutputReport();
1222 catch (Exception ex)
1224 DisplayException(Name, ex);
1230 /// Called on arrival of any device.
1231 /// Calls a routine that searches to see if the desired device is present.
1234 private void DeviceAdded(object sender, EventArrivedEventArgs e)
1238 Debug.WriteLine("A USB device has been inserted");
1240 _deviceDetected = FindDeviceUsingWmi();
1242 catch (Exception ex)
1244 DisplayException(Name, ex);
1250 /// Called if the user changes the Vendor ID or Product ID in the text box.
1253 private void DeviceHasChanged()
1257 // If a device was previously detected, stop receiving notifications about it.
1259 if (_deviceHandleObtained)
1261 DeviceNotificationsStop();
1263 CloseCommunications();
1265 // Look for a device that matches the Vendor ID and Product ID in the text boxes.
1269 catch (Exception ex)
1271 DisplayException(Name, ex);
1277 /// Add handlers to detect device arrival and removal.
1280 private void DeviceNotificationsStart()
1282 AddDeviceArrivedHandler();
1283 AddDeviceRemovedHandler();
1287 /// Stop receiving notifications about device arrival and removal
1290 private void DeviceNotificationsStop()
1294 if (_deviceArrivedWatcher != null)
1295 _deviceArrivedWatcher.Stop();
1296 if (_deviceRemovedWatcher != null)
1297 _deviceRemovedWatcher.Stop();
1299 catch (Exception ex)
1301 DisplayException(Name, ex);
1307 /// Called on removal of any device.
1308 /// Calls a routine that searches to see if the desired device is still present.
1311 private void DeviceRemoved(object sender, EventArgs e)
1315 Debug.WriteLine("A USB device has been removed");
1317 _deviceDetected = FindDeviceUsingWmi();
1319 if (!_deviceDetected)
1321 _deviceHandleObtained = false;
1322 CloseCommunications();
1325 catch (Exception ex)
1327 DisplayException(Name, ex);
1333 /// Displays received or written report data.
1336 /// <param name="buffer"> contains the report data. </param>
1337 /// <param name="currentReportType" > "Input", "Output", or "Feature"</param>
1338 /// <param name="currentReadOrWritten" > "read" for Input and IN Feature reports, "written" for Output and OUT Feature reports.</param>
1340 private void DisplayReportData(Byte[] buffer, ReportTypes currentReportType, ReportReadOrWritten currentReadOrWritten)
1346 LstResults.Items.Add(currentReportType.ToString() + " report has been " + currentReadOrWritten.ToString().ToLower() + ".");
1348 // Display the report data received in the form's list box.
1350 LstResults.Items.Add(" Report ID: " + String.Format("{0:X2} ", buffer[0]));
1351 LstResults.Items.Add(" Report Data Size: " + (buffer.Length-1)); //Exclude one byte for the report ID
1352 LstResults.Items.Add(" Report Data:");
1354 TxtBytesReceived.Text = "";
1356 String byteString = "";
1358 for (count = 1; count <= buffer.Length - 1; count++)
1360 // Display bytes as 2-character Hex strings.
1362 byteString += String.Format(" {0:X2} ", buffer[count]);
1364 if (count % 16 == 0)
1366 //Add a line to our list box every 16 bytes
1367 LstResults.Items.Add(byteString);
1373 // Display the received bytes in the text box.
1374 //TxtBytesReceived.SelectionStart = TxtBytesReceived.Text.Length;
1375 //TxtBytesReceived.SelectedText = byteValue + Environment.NewLine;
1378 //Add the rests of our data
1379 LstResults.Items.Add(byteString);
1381 ScrollToBottomOfListBox();
1383 catch (Exception ex)
1385 DisplayException(Name, ex);
1391 /// Display a message if the user clicks a button when a transfer is in progress.
1394 private void DisplayTransferInProgressMessage()
1396 AccessForm(FormActions.AddItemToListBox, "Command not executed because a transfer is in progress.");
1397 ScrollToBottomOfListBox();
1401 /// Do periodic transfers.
1403 /// <param name="source"></param>
1404 /// <param name="e"></param>
1406 /// The timer is enabled only if continuous (periodic) transfers have been requested.
1409 private void DoPeriodicTransfers(object source, ElapsedEventArgs e)
1413 PeriodicTransfers();
1415 catch (Exception ex)
1417 DisplayException(Name, ex);
1423 /// Enable the command buttons on the form.
1424 /// Needed after attempting a transfer and device not found.
1427 private void EnableFormControls()
1429 cmdGetInputReportInterrupt.Enabled = true;
1430 cmdSendOutputReportControl.Enabled = true;
1431 cmdGetInputReportControl.Enabled = true;
1432 cmdGetFeatureReport.Enabled = true;
1433 cmdSendFeatureReport.Enabled = true;
1434 cmdPeriodicTransfers.Enabled = true;
1435 cmdSendOutputReportInterrupt.Enabled = true;
1439 /// Use the System.Management class to find a device by Vendor ID and Product ID using WMI. If found, display device properties.
1442 /// During debugging, if you stop the firmware but leave the device attached, the device may still be detected as present
1443 /// but will be unable to communicate. The device will show up in Windows Device Manager as well.
1444 /// This situation is unlikely to occur with a final product.
1447 private Boolean FindDeviceUsingWmi()
1451 MyMarshalDataToForm(FormActions.ResetDeviceTreeView);
1452 // Prepend "@" to string below to treat backslash as a normal character (not escape character):
1454 String deviceIdString = @"USB\VID_" + _myVendorId.ToString("X4") + "&PID_" + _myProductId.ToString("X4");
1456 _deviceDetected = false;
1457 var searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_PnPEntity");
1458 int usbDeviceCounter = 0;
1460 foreach (ManagementObject queryObj in searcher.Get())
1462 string deviceId = queryObj["PNPDeviceID"].ToString();
1463 if (deviceId.Contains(deviceIdString))
1465 _deviceDetected = true;
1466 MyMarshalDataToForm(FormActions.AddItemToListBox, "My device found (WMI):");
1468 MyMarshalDataToForm(FormActions.AddItemToListBox, "--------");
1469 List<string> device = new List<string>();
1470 device.Add(deviceId);
1472 // Display device properties.
1473 foreach (WmiDeviceProperties wmiDeviceProperty in Enum.GetValues(typeof(WmiDeviceProperties)))
1475 MyMarshalDataToForm(FormActions.AddItemToListBox, (wmiDeviceProperty.ToString() + ": " + queryObj[wmiDeviceProperty.ToString()]));
1476 device.Add((wmiDeviceProperty.ToString() + ": " + queryObj[wmiDeviceProperty.ToString()]));
1477 Debug.WriteLine(wmiDeviceProperty.ToString() + ": {0}", queryObj[wmiDeviceProperty.ToString()]);
1479 MyMarshalDataToForm(FormActions.AddItemToListBox, "--------");
1480 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1482 MyMarshalDataToForm(FormActions.AddDeviceToTreeView, device.ToArray());
1483 MyMarshalDataToForm(FormActions.SelectDeviceInTreeView, deviceId);
1486 else if (deviceId.StartsWith("USB\\VID"))
1488 List<string> device = new List<string>();
1489 device.Add(deviceId);
1491 // Add device properties.
1492 foreach (WmiDeviceProperties wmiDeviceProperty in Enum.GetValues(typeof(WmiDeviceProperties)))
1494 device.Add((wmiDeviceProperty.ToString() + ": " + queryObj[wmiDeviceProperty.ToString()]));
1495 Debug.WriteLine(wmiDeviceProperty.ToString() + ": {0}", queryObj[wmiDeviceProperty.ToString()]);
1498 MyMarshalDataToForm(FormActions.AddDeviceToTreeView, device.ToArray());
1502 //Complete our device TreeView
1503 MyMarshalDataToForm(FormActions.CompleteDeviceTreeView);
1506 MyMarshalDataToForm(FormActions.AddItemToListBox, "Found " + usbDeviceCounter /*searcher.Get().Count*/ + " USB HID devices");
1508 if (!_deviceDetected)
1510 MyMarshalDataToForm(FormActions.AddItemToListBox, "My device not found (WMI)");
1511 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1513 return _deviceDetected;
1515 catch (Exception ex)
1517 DisplayException(Name, ex);
1523 /// Call HID functions that use Win32 API functions to locate a HID-class device
1524 /// by its Vendor ID and Product ID. Open a handle to the device.
1528 /// True if the device is detected, False if not detected.
1531 private Boolean FindTheHid()
1533 var devicePathName = new String[128];
1534 String myDevicePathName = "";
1538 _deviceHandleObtained = false;
1539 CloseCommunications();
1541 // Get the device's Vendor ID and Product ID from the form's text boxes.
1543 GetVendorAndProductIDsFromTextBoxes(ref _myVendorId, ref _myProductId);
1545 // Get the HID-class GUID.
1547 Guid hidGuid = _myHid.GetHidGuid();
1549 String functionName = "GetHidGuid";
1550 Debug.WriteLine(_myDebugging.ResultOfApiCall(functionName));
1551 Debug.WriteLine(" GUID for system HIDs: " + hidGuid.ToString());
1553 // Fill an array with the device path names of all attached HIDs.
1555 Boolean availableHids = _myDeviceManagement.FindDeviceFromGuid(hidGuid, ref devicePathName);
1557 // If there is at least one HID, attempt to read the Vendor ID and Product ID
1558 // of each device until there is a match or all devices have been examined.
1562 Int32 memberIndex = 0;
1566 // Open the handle without read/write access to enable getting information about any HID, even system keyboards and mice.
1568 _hidHandle = _myHid.OpenHandle(devicePathName[memberIndex], false);
1570 functionName = "CreateFile";
1571 Debug.WriteLine(_myDebugging.ResultOfApiCall(functionName));
1572 Debug.WriteLine(" Returned handle: " + _hidHandle);
1574 if (!_hidHandle.IsInvalid)
1576 // The returned handle is valid,
1577 // so find out if this is the device we're looking for.
1579 _myHid.DeviceAttributes.Size = Marshal.SizeOf(_myHid.DeviceAttributes);
1581 Boolean success = _myHid.GetAttributes(_hidHandle, ref _myHid.DeviceAttributes);
1585 Debug.WriteLine(" HIDD_ATTRIBUTES structure filled without error.");
1586 Debug.WriteLine(" Structure size: " + _myHid.DeviceAttributes.Size);
1587 Debug.WriteLine(" Vendor ID: " + Convert.ToString(_myHid.DeviceAttributes.VendorID, 16));
1588 Debug.WriteLine(" Product ID: " + Convert.ToString(_myHid.DeviceAttributes.ProductID, 16));
1589 Debug.WriteLine(" Version Number: " + Convert.ToString(_myHid.DeviceAttributes.VersionNumber, 16));
1591 if ((_myHid.DeviceAttributes.VendorID == _myVendorId) && (_myHid.DeviceAttributes.ProductID == _myProductId))
1593 Debug.WriteLine(" Handle obtained to my device");
1595 // Display the information in form's list box.
1597 MyMarshalDataToForm(FormActions.AddItemToListBox, "Handle obtained to my device:");
1598 MyMarshalDataToForm(FormActions.AddItemToListBox, " Vendor ID= " + Convert.ToString(_myHid.DeviceAttributes.VendorID, 16));
1599 MyMarshalDataToForm(FormActions.AddItemToListBox, " Product ID = " + Convert.ToString(_myHid.DeviceAttributes.ProductID, 16));
1600 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1602 _deviceHandleObtained = true;
1604 myDevicePathName = devicePathName[memberIndex];
1608 // It's not a match, so close the handle.
1610 _deviceHandleObtained = false;
1616 // There was a problem retrieving the information.
1618 Debug.WriteLine(" Error in filling HIDD_ATTRIBUTES structure.");
1619 _deviceHandleObtained = false;
1624 // Keep looking until we find the device or there are no devices left to examine.
1626 memberIndex = memberIndex + 1;
1628 while (!((_deviceHandleObtained || (memberIndex == devicePathName.Length))));
1631 if (_deviceHandleObtained)
1633 // The device was detected.
1634 // Learn the capabilities of the device.
1636 _myHid.Capabilities = _myHid.GetDeviceCapabilities(_hidHandle);
1638 // Find out if the device is a system mouse or keyboard.
1640 _hidUsage = _myHid.GetHidUsage(_myHid.Capabilities);
1642 // Get the Input report buffer size.
1644 GetInputReportBufferSize();
1645 MyMarshalDataToForm(FormActions.EnableInputReportBufferSize, "");
1647 //Close the handle and reopen it with read/write access.
1651 _hidHandle = _myHid.OpenHandle(myDevicePathName, true);
1653 if (_hidHandle.IsInvalid)
1655 MyMarshalDataToForm(FormActions.AddItemToListBox, "The device is a system " + _hidUsage + ".");
1656 MyMarshalDataToForm(FormActions.AddItemToListBox, "Windows 2000 and later obtain exclusive access to Input and Output reports for this devices.");
1657 MyMarshalDataToForm(FormActions.AddItemToListBox, "Windows 8 also obtains exclusive access to Feature reports.");
1658 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1662 if (_myHid.Capabilities.InputReportByteLength > 0)
1664 // Set the size of the Input report buffer.
1666 var inputReportBuffer = new Byte[_myHid.Capabilities.InputReportByteLength];
1668 _deviceData = new FileStream(_hidHandle, FileAccess.Read | FileAccess.Write, inputReportBuffer.Length, false);
1671 if (_myHid.Capabilities.OutputReportByteLength > 0)
1673 Byte[] outputReportBuffer = null;
1675 // Flush any waiting reports in the input buffer. (optional)
1677 _myHid.FlushQueue(_hidHandle);
1682 MyMarshalDataToForm(FormActions.AddItemToListBox, "Device not found.");
1683 MyMarshalDataToForm(FormActions.DisableInputReportBufferSize, "");
1684 EnableFormControls();
1685 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1687 return _deviceHandleObtained;
1689 catch (Exception ex)
1691 DisplayException(Name, ex);
1697 /// Perform shutdown operations.
1700 private void frmMain_Closed(Object eventSender, EventArgs eventArgs)
1706 catch (Exception ex)
1708 DisplayException(Name, ex);
1714 /// Perform startup operations.
1717 private void frmMain_Load(Object eventSender, EventArgs eventArgs)
1724 catch (Exception ex)
1726 DisplayException(Name, ex);
1731 private void GetBytesToSend()
1735 // Get the bytes to send in a report from the combo boxes.
1736 // Increment the values if the autoincrement check box is selected.
1738 if (ChkAutoincrement.Checked)
1740 if (CboByte0.SelectedIndex < 255)
1742 CboByte0.SelectedIndex = CboByte0.SelectedIndex + 1;
1746 CboByte0.SelectedIndex = 0;
1748 if (CboByte1.SelectedIndex < 255)
1750 CboByte1.SelectedIndex = CboByte1.SelectedIndex + 1;
1754 CboByte1.SelectedIndex = 0;
1758 catch (Exception ex)
1760 DisplayException(Name, ex);
1766 /// Find and display the number of Input buffers
1767 /// (the number of Input reports the HID driver will store).
1770 private void GetInputReportBufferSize()
1772 Int32 numberOfInputBuffers = 0;
1777 // Get the number of input buffers.
1779 _myHid.GetNumberOfInputBuffers(_hidHandle, ref numberOfInputBuffers);
1781 // Display the result in the text box.
1783 MyMarshalDataToForm(FormActions.SetInputReportBufferSize, Convert.ToString(numberOfInputBuffers));
1785 catch (Exception ex)
1787 DisplayException(Name, ex);
1793 /// Retrieve a Vendor ID and Product ID in hexadecimal
1794 /// from the form's text boxes and convert the text to Int32s.
1797 /// <param name="myVendorId"> the Vendor ID</param>
1798 /// <param name="myProductId"> the Product ID</param>
1800 private void GetVendorAndProductIDsFromTextBoxes(ref Int32 myVendorId, ref Int32 myProductId)
1804 myVendorId = Int32.Parse(txtVendorID.Text, NumberStyles.AllowHexSpecifier);
1805 myProductId = Int32.Parse(txtProductID.Text, NumberStyles.AllowHexSpecifier);
1807 catch (Exception ex)
1809 DisplayException(Name, ex);
1815 /// Initialize the elements on the form.
1818 private void InitializeDisplay()
1822 // Create a dropdown list box for each byte to send in a report.
1823 // Display the values as 2-character hex strings.
1826 for (count = 0; count <= 255; count++)
1828 String byteValue = String.Format("{0:X2} ", count);
1829 FrmMy.CboByte0.Items.Insert(count, byteValue);
1830 FrmMy.CboByte1.Items.Insert(count, byteValue);
1833 // Select a default value for each box
1835 FrmMy.CboByte0.SelectedIndex = 0;
1836 FrmMy.CboByte1.SelectedIndex = 128;
1837 FrmMy.radInputOutputInterrupt.Checked = true;
1839 // Check the autoincrement box to increment the values each time a report is sent.
1841 ChkAutoincrement.CheckState = CheckState.Checked;
1843 // Don't allow the user to select an input report buffer size until there is
1844 // a handle to a HID.
1846 cmdInputReportBufferSize.Focus();
1847 cmdInputReportBufferSize.Enabled = false;
1849 LstResults.Items.Add("For a more detailed event log, view debug statements in Visual Studio's Output window:");
1850 LstResults.Items.Add("Click Build > Configuration Manager > Active Solution Configuration > Debug > Close.");
1851 LstResults.Items.Add("Then click View > Output.");
1852 LstResults.Items.Add("");
1854 catch (Exception ex)
1856 DisplayException(Name, ex);
1862 /// Enables accessing a form's controls from another thread
1865 /// <param name="action"> a FormActions member that names the action to perform on the form </param>
1866 /// <param name="textToDisplay"> text that the form displays or the code uses for
1867 /// another purpose. Actions that don't use text ignore this parameter. </param>
1869 private void MyMarshalDataToForm(FormActions action, params string[] strings)
1873 object[] args = { action, strings };
1875 // The AccessForm routine contains the code that accesses the form.
1877 MarshalDataToForm marshalDataToFormDelegate = AccessForm;
1879 // Execute AccessForm, passing the parameters in args.
1881 Invoke(marshalDataToFormDelegate, args);
1883 catch (Exception ex)
1885 DisplayException(Name, ex);
1891 /// Timeout if read via interrupt transfer doesn't return.
1894 private void OnReadTimeout()
1898 MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to read a report timed out.");
1899 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1900 CloseCommunications();
1901 MyMarshalDataToForm(FormActions.EnableGetInputReportInterruptTransfer, "");
1902 _transferInProgress = false;
1903 _sendOrGet = SendOrGet.Send;
1905 catch (Exception ex)
1907 DisplayException(Name, ex);
1913 /// Timeout if write via interrupt transfer doesn't return.
1916 private void OnWriteTimeout()
1920 MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to write a report timed out.");
1921 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1922 CloseCommunications();
1923 MyMarshalDataToForm(FormActions.EnableSendOutputReportInterrupt, "");
1924 _transferInProgress = false;
1925 _sendOrGet = SendOrGet.Get;
1927 catch (Exception ex)
1929 DisplayException(Name, ex);
1935 /// Alternat sending and getting a report.
1938 private void PeriodicTransfers()
1942 if (!_transferInProgress)
1944 if (_reportType == ReportTypes.Feature)
1946 SendOrGetFeatureReport();
1950 // Output and Input reports
1952 SendOutputReportOrGetInputReport();
1956 catch (Exception ex)
1958 DisplayException(Name, ex);
1964 /// Start doing periodic transfers.
1967 private void PeriodicTransfersStart()
1969 // Don't allow changing the transfer type while transfers are in progress.
1971 if (radFeature.Checked)
1973 radInputOutputControl.Enabled = false;
1974 radInputOutputInterrupt.Enabled = false;
1976 else if (radInputOutputControl.Checked)
1978 radFeature.Enabled = false;
1979 radInputOutputInterrupt.Enabled = false;
1981 else if (radInputOutputInterrupt.Checked)
1983 radFeature.Enabled = false;
1984 radInputOutputControl.Enabled = false;
1987 // Change the command button's text.
1989 cmdPeriodicTransfers.Text = "Stop";
1991 // Enable the timer event to trigger a set of transfers.
1993 _periodicTransfers.Start();
1995 cmdPeriodicTransfers.Enabled = true;
1997 if (radInputOutputInterrupt.Checked)
1999 _transferType = TransferTypes.Interrupt;
2000 _reportType = ReportTypes.Output;
2002 else if (radInputOutputControl.Checked)
2004 _transferType = TransferTypes.Control;
2005 _reportType = ReportTypes.Output;
2007 else if (radFeature.Checked)
2009 _transferType = TransferTypes.Control;
2010 _reportType = ReportTypes.Feature;
2012 _periodicTransfersRequested = true;
2013 PeriodicTransfers();
2017 /// Stop doing periodic transfers.
2020 private void PeriodicTransfersStop()
2022 // Stop doing continuous transfers.
2024 _periodicTransfersRequested = false;
2026 // Disable the timer that triggers the transfers.
2028 _periodicTransfers.Stop();
2029 cmdPeriodicTransfers.Enabled = true;
2031 // Change the command button's text.
2033 cmdPeriodicTransfers.Text = "Start";
2035 // Re-allow changing the transfer type.
2037 radFeature.Enabled = true;
2038 radInputOutputControl.Enabled = true;
2039 radInputOutputInterrupt.Enabled = true;
2042 private void radInputOutputControl_CheckedChanged(object sender, EventArgs e)
2046 private void radInputOutputInterrupt_CheckedChanged(object sender, EventArgs e)
2050 private void radFeature_CheckedChanged(object sender, EventArgs e)
2055 /// Request a Feature report.
2056 /// Assumes report ID = 0.
2059 private void RequestToGetFeatureReport()
2061 String byteValue = null;
2065 // If the device hasn't been detected, was removed, or timed out on a previous attempt
2066 // to access it, look for the device.
2068 if (!_deviceHandleObtained)
2070 _deviceHandleObtained = FindTheHid();
2073 if (_deviceHandleObtained)
2075 Byte[] inFeatureReportBuffer = null;
2077 if ((_myHid.Capabilities.FeatureReportByteLength > 0))
2079 // The HID has a Feature report.
2080 // Read a report from the device.
2082 // Set the size of the Feature report buffer.
2084 if ((_myHid.Capabilities.FeatureReportByteLength > 0))
2086 inFeatureReportBuffer = new Byte[_myHid.Capabilities.FeatureReportByteLength];
2091 Boolean success = _myHid.GetFeatureReport(_hidHandle, ref inFeatureReportBuffer);
2095 DisplayReportData(inFeatureReportBuffer, ReportTypes.Feature, ReportReadOrWritten.Read);
2099 CloseCommunications();
2100 MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to read a Feature report failed.");
2101 ScrollToBottomOfListBox();
2106 MyMarshalDataToForm(FormActions.AddItemToListBox, "The HID doesn't have a Feature report.");
2107 ScrollToBottomOfListBox();
2110 _transferInProgress = false;
2111 cmdGetFeatureReport.Enabled = true;
2113 catch (Exception ex)
2115 DisplayException(Name, ex);
2121 /// Request an Input report.
2122 /// Assumes report ID = 0.
2125 private async void RequestToGetInputReport()
2127 const Int32 readTimeout = 5000;
2129 String byteValue = null;
2130 Byte[] inputReportBuffer = null;
2134 Boolean success = false;
2136 // If the device hasn't been detected, was removed, or timed out on a previous attempt
2137 // to access it, look for the device.
2139 if (!_deviceHandleObtained)
2141 _deviceHandleObtained = FindTheHid();
2144 if (_deviceHandleObtained)
2146 // Don't attempt to exchange reports if valid handles aren't available
2147 // (as for a mouse or keyboard under Windows 2000 and later.)
2149 if (!_hidHandle.IsInvalid)
2151 // Read an Input report.
2153 // Don't attempt to send an Input report if the HID has no Input report.
2154 // (The HID spec requires all HIDs to have an interrupt IN endpoint,
2155 // which suggests that all HIDs must support Input reports.)
2157 if (_myHid.Capabilities.InputReportByteLength > 0)
2159 // Set the size of the Input report buffer.
2161 inputReportBuffer = new Byte[_myHid.Capabilities.InputReportByteLength];
2163 if (_transferType.Equals(TransferTypes.Control))
2166 _transferInProgress = true;
2168 // Read a report using a control transfer.
2170 success = _myHid.GetInputReportViaControlTransfer(_hidHandle, ref inputReportBuffer);
2171 cmdGetInputReportControl.Enabled = true;
2172 _transferInProgress = false;
2178 _transferInProgress = true;
2180 // Read a report using interrupt transfers.
2181 // Timeout if no report available.
2182 // To enable reading a report without blocking the calling thread, uses Filestream's ReadAsync method.
2184 // Create a delegate to execute on a timeout.
2186 Action onReadTimeoutAction = OnReadTimeout;
2188 // The CancellationTokenSource specifies the timeout value and the action to take on a timeout.
2190 var cts = new CancellationTokenSource();
2192 // Cancel the read if it hasn't completed after a timeout.
2194 cts.CancelAfter(readTimeout);
2196 // Specify the function to call on a timeout.
2198 cts.Token.Register(onReadTimeoutAction);
2200 // Stops waiting when data is available or on timeout:
2202 Int32 bytesRead = await _myHid.GetInputReportViaInterruptTransfer(_deviceData, inputReportBuffer, cts);
2204 // Arrive here only if the operation completed.
2206 // Dispose to stop the timeout timer.
2210 _transferInProgress = false;
2211 cmdGetInputReportInterrupt.Enabled = true;
2216 Debug.Print("bytes read (includes report ID) = " + Convert.ToString(bytesRead));
2223 MyMarshalDataToForm(FormActions.AddItemToListBox, "No attempt to read an Input report was made.");
2224 MyMarshalDataToForm(FormActions.AddItemToListBox, "The HID doesn't have an Input report.");
2229 MyMarshalDataToForm(FormActions.AddItemToListBox, "Invalid handle.");
2230 MyMarshalDataToForm(FormActions.AddItemToListBox,
2231 "No attempt to write an Output report or read an Input report was made.");
2236 DisplayReportData(inputReportBuffer, ReportTypes.Input, ReportReadOrWritten.Read);
2240 CloseCommunications();
2241 MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to read an Input report has failed.");
2242 ScrollToBottomOfListBox();
2246 catch (Exception ex)
2248 DisplayException(Name, ex);
2254 /// Sends a Feature report.
2255 /// Assumes report ID = 0.
2258 private void RequestToSendFeatureReport()
2260 String byteValue = null;
2264 _transferInProgress = true;
2266 // If the device hasn't been detected, was removed, or timed out on a previous attempt
2267 // to access it, look for the device.
2269 if (!_deviceHandleObtained)
2271 _deviceHandleObtained = FindTheHid();
2274 if (_deviceHandleObtained)
2278 if ((_myHid.Capabilities.FeatureReportByteLength > 0))
2280 // The HID has a Feature report.
2281 // Set the size of the Feature report buffer.
2283 var outFeatureReportBuffer = new Byte[_myHid.Capabilities.FeatureReportByteLength];
2285 // Store the report ID in the buffer.
2287 outFeatureReportBuffer[0] = 0;
2289 // Store the report data following the report ID.
2290 // Use the data in the combo boxes on the form.
2292 outFeatureReportBuffer[1] = Convert.ToByte(CboByte0.SelectedIndex);
2294 if (outFeatureReportBuffer.GetUpperBound(0) > 1)
2296 outFeatureReportBuffer[2] = Convert.ToByte(CboByte1.SelectedIndex);
2299 // Write a report to the device
2301 Boolean success = _myHid.SendFeatureReport(_hidHandle, outFeatureReportBuffer);
2305 DisplayReportData(outFeatureReportBuffer, ReportTypes.Feature, ReportReadOrWritten.Written);
2309 CloseCommunications();
2310 AccessForm(FormActions.AddItemToListBox, "The attempt to send a Feature report failed.");
2311 ScrollToBottomOfListBox();
2316 AccessForm(FormActions.AddItemToListBox, "The HID doesn't have a Feature report.");
2317 ScrollToBottomOfListBox();
2321 _transferInProgress = false;
2322 cmdSendFeatureReport.Enabled = true;
2323 ScrollToBottomOfListBox();
2326 catch (Exception ex)
2328 DisplayException(Name, ex);
2336 public static void Fill<T>(T[] arr, T value)
2338 for (int i = 0; i < arr.Length; i++)
2345 /// Sends an Output report.
2346 /// Assumes report ID = 0.
2349 private async void RequestToSendOutputReport()
2351 const Int32 writeTimeout = 5000;
2352 String byteValue = null;
2356 // If the device hasn't been detected, was removed, or timed out on a previous attempt
2357 // to access it, look for the device.
2359 if (!_deviceHandleObtained)
2361 _deviceHandleObtained = FindTheHid();
2364 if (_deviceHandleObtained)
2368 // Don't attempt to exchange reports if valid handles aren't available
2369 // (as for a mouse or keyboard.)
2371 if (!_hidHandle.IsInvalid)
2373 // Don't attempt to send an Output report if the HID has no Output report.
2375 if (_myHid.Capabilities.OutputReportByteLength > 0)
2377 // Set the size of the Output report buffer.
2379 Byte[] outputReportBuffer = new Byte[_myHid.Capabilities.OutputReportByteLength];
2380 Fill<Byte>(outputReportBuffer,0);
2382 // Store the report ID in the first byte of the buffer:
2384 outputReportBuffer[0] = 0;
2386 // Store the report data following the report ID.
2387 // Use the data in the combo boxes on the form.
2389 outputReportBuffer[1] = Convert.ToByte(CboByte0.SelectedIndex);
2391 if (outputReportBuffer.GetUpperBound(0) > 1)
2393 outputReportBuffer[2] = Convert.ToByte(CboByte1.SelectedIndex);
2399 outputReportBuffer[0] = 0; //Report ID
2400 outputReportBuffer[1] = 4; //Size of report, as per Futaba specs
2401 outputReportBuffer[2] = 0x1B; //Clear screen code
2402 outputReportBuffer[3] = 0x5B;
2403 outputReportBuffer[4] = 0x32;
2404 outputReportBuffer[5] = 0x4A;
2412 if (_transferType.Equals(TransferTypes.Control))
2415 _transferInProgress = true;
2417 // Use a control transfer to send the report,
2418 // even if the HID has an interrupt OUT endpoint.
2420 success = _myHid.SendOutputReportViaControlTransfer(_hidHandle, outputReportBuffer);
2422 _transferInProgress = false;
2423 cmdSendOutputReportControl.Enabled = true;
2428 Debug.Print("interrupt");
2429 _transferInProgress = true;
2431 // The CancellationTokenSource specifies the timeout value and the action to take on a timeout.
2433 var cts = new CancellationTokenSource();
2435 // Create a delegate to execute on a timeout.
2437 Action onWriteTimeoutAction = OnWriteTimeout;
2439 // Cancel the read if it hasn't completed after a timeout.
2441 cts.CancelAfter(writeTimeout);
2443 // Specify the function to call on a timeout.
2445 cts.Token.Register(onWriteTimeoutAction);
2447 // Send an Output report and wait for completion or timeout.
2449 success = await _myHid.SendOutputReportViaInterruptTransfer(_deviceData, _hidHandle, outputReportBuffer, cts);
2451 // Get here only if the operation completes without a timeout.
2453 _transferInProgress = false;
2454 cmdSendOutputReportInterrupt.Enabled = true;
2456 // Dispose to stop the timeout timer.
2462 DisplayReportData(outputReportBuffer, ReportTypes.Output, ReportReadOrWritten.Written);
2466 CloseCommunications();
2467 AccessForm(FormActions.AddItemToListBox, "The attempt to write an Output report failed.");
2468 ScrollToBottomOfListBox();
2474 AccessForm(FormActions.AddItemToListBox, "The HID doesn't have an Output report.");
2477 catch (Exception ex)
2479 DisplayException(Name, ex);
2485 /// Scroll to the bottom of the list box and trim as needed.
2488 private void ScrollToBottomOfListBox()
2492 LstResults.SelectedIndex = LstResults.Items.Count - 1;
2494 // If the list box is getting too large, trim its contents by removing the earliest data.
2496 if (LstResults.Items.Count > 1000)
2499 for (count = 1; count <= 500; count++)
2501 LstResults.Items.RemoveAt(4);
2505 catch (Exception ex)
2507 DisplayException(Name, ex);
2513 /// Request to send or get a Feature report.
2516 private void SendOrGetFeatureReport()
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)
2532 case SendOrGet.Send:
2533 RequestToSendFeatureReport();
2534 _sendOrGet = SendOrGet.Get;
2537 RequestToGetFeatureReport();
2538 _sendOrGet = SendOrGet.Send;
2543 catch (Exception ex)
2545 DisplayException(Name, ex);
2551 /// Request to send an Output report or get an Input report.
2554 private void SendOutputReportOrGetInputReport()
2558 // If the device hasn't been detected, was removed, or timed out on a previous attempt
2559 // to access it, look for the device.
2561 if (!_deviceHandleObtained)
2563 _deviceHandleObtained = FindTheHid();
2566 if (_deviceHandleObtained)
2568 if (_sendOrGet == SendOrGet.Send)
2570 RequestToSendOutputReport();
2571 _sendOrGet = SendOrGet.Get;
2575 RequestToGetInputReport();
2576 _sendOrGet = SendOrGet.Send;
2580 catch (Exception ex)
2582 DisplayException(Name, ex);
2588 /// Set the number of Input buffers (the number of Input reports
2589 /// the host will store) from the value in the text box.
2592 private void SetInputReportBufferSize()
2596 if (!_transferInProgress)
2598 // Get the number of buffers from the text box.
2600 Int32 numberOfInputBuffers = Convert.ToInt32(txtInputReportBufferSize.Text);
2602 // Set the number of buffers.
2604 _myHid.SetNumberOfInputBuffers(_hidHandle, numberOfInputBuffers);
2606 // Verify and display the result.
2608 GetInputReportBufferSize();
2612 DisplayTransferInProgressMessage();
2615 catch (Exception ex)
2617 DisplayException(Name, ex);
2623 /// Perform actions that must execute when the program ends.
2626 private void Shutdown()
2630 CloseCommunications();
2631 DeviceNotificationsStop();
2633 catch (Exception ex)
2635 DisplayException(Name, ex);
2641 /// Perform actions that must execute when the program starts.
2644 private void Startup()
2646 const Int32 periodicTransferInterval = 1000;
2650 InitializeDisplay();
2652 _periodicTransfers = new System.Timers.Timer(periodicTransferInterval);
2653 _periodicTransfers.Elapsed += DoPeriodicTransfers;
2654 _periodicTransfers.Stop();
2655 _periodicTransfers.SynchronizingObject = this;
2657 // Default USB Vendor ID and Product ID:
2659 txtVendorID.Text = "0925";
2660 txtProductID.Text = "7001";
2662 GetVendorAndProductIDsFromTextBoxes(ref _myVendorId, ref _myProductId);
2664 DeviceNotificationsStart();
2665 FindDeviceUsingWmi();
2668 catch (Exception ex)
2670 DisplayException(Name, ex);
2676 /// The Product ID has changed in the text box. Call a routine to handle it.
2679 private void txtProductID_TextChanged(Object sender, EventArgs e)
2683 //DeviceHasChanged();
2685 catch (Exception ex)
2687 DisplayException(Name, ex);
2693 /// The Vendor ID has changed in the text box. Call a routine to handle it.
2696 private void txtVendorID_TextChanged(Object sender, EventArgs e)
2700 //DeviceHasChanged();
2702 catch (Exception ex)
2704 DisplayException(Name, ex);
2710 /// Provides a central mechanism for exception handling.
2711 /// Displays a message box that describes the exception.
2714 /// <param name="moduleName"> the module where the exception occurred. </param>
2715 /// <param name="e"> the exception </param>
2717 internal static void DisplayException(String moduleName, Exception e)
2719 // Create an error message.
2721 String message = "Exception: " + e.Message + Environment.NewLine + "Module: " + moduleName + Environment.NewLine + "Method: " + e.TargetSite.Name;
2723 const String caption = "Unexpected Exception";
2725 MessageBox.Show(message, caption, MessageBoxButtons.OK);
2726 Debug.Write(message);
2728 // Get the last error and display it.
2730 Int32 error = Marshal.GetLastWin32Error();
2732 Debug.WriteLine("The last Win32 Error was: " + error);
2736 internal static void Main() { Application.Run(new FrmMain()); }
2737 private static FrmMain _transDefaultFormFrmMain;
2738 internal static FrmMain TransDefaultFormFrmMain
2742 if (_transDefaultFormFrmMain == null)
2744 _transDefaultFormFrmMain = new FrmMain();
2746 return _transDefaultFormFrmMain;
2750 private void treeViewDevices_AfterSelect(object sender, TreeViewEventArgs e)
2752 MyMarshalDataToForm(FormActions.AddItemToListBox, "------------------------------------------------------------------------");
2753 //Node selected in our TreeView
2754 //Extract vendor and product IDs
2755 string deviceId = treeViewDevices.SelectedNode.Name;
2756 Regex regex = new Regex(@"VID_(....)&PID_(....).*");
2757 Match match = regex.Match(deviceId);
2760 //Take matches from each capturing group here. match.Groups[n].Value;
2761 //Put vendor and product ID in our text fields.
2762 txtVendorID.Text = match.Groups[1].Value;
2763 txtProductID.Text = match.Groups[2].Value;