Not searching device on every character input from our vendor and product text field.
Selecting a device in our tree now triggers a search for it.
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(495, 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(612, 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("Arial", 8F, 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.ItemHeight = 14;
351 this.LstResults.Location = new System.Drawing.Point(12, 494);
352 this.LstResults.Name = "LstResults";
353 this.LstResults.RightToLeft = System.Windows.Forms.RightToLeft.No;
354 this.LstResults.Size = new System.Drawing.Size(760, 256);
355 this.LstResults.TabIndex = 0;
357 // fraInputReportBufferSize
359 this.fraInputReportBufferSize.Controls.Add(this.cmdInputReportBufferSize);
360 this.fraInputReportBufferSize.Controls.Add(this.txtInputReportBufferSize);
361 this.fraInputReportBufferSize.Location = new System.Drawing.Point(623, 44);
362 this.fraInputReportBufferSize.Name = "fraInputReportBufferSize";
363 this.fraInputReportBufferSize.Size = new System.Drawing.Size(149, 79);
364 this.fraInputReportBufferSize.TabIndex = 9;
365 this.fraInputReportBufferSize.TabStop = false;
366 this.fraInputReportBufferSize.Text = "Input Report Buffer Size";
368 // cmdInputReportBufferSize
370 this.cmdInputReportBufferSize.Location = new System.Drawing.Point(6, 47);
371 this.cmdInputReportBufferSize.Name = "cmdInputReportBufferSize";
372 this.cmdInputReportBufferSize.Size = new System.Drawing.Size(136, 26);
373 this.cmdInputReportBufferSize.TabIndex = 1;
374 this.cmdInputReportBufferSize.Text = "Change Buffer Size";
375 this.cmdInputReportBufferSize.Click += new System.EventHandler(this.cmdInputReportBufferSize_Click);
377 // txtInputReportBufferSize
379 this.txtInputReportBufferSize.Location = new System.Drawing.Point(6, 21);
380 this.txtInputReportBufferSize.Name = "txtInputReportBufferSize";
381 this.txtInputReportBufferSize.Size = new System.Drawing.Size(56, 20);
382 this.txtInputReportBufferSize.TabIndex = 0;
384 // fraDeviceIdentifiers
386 this.fraDeviceIdentifiers.Controls.Add(this.txtProductID);
387 this.fraDeviceIdentifiers.Controls.Add(this.lblProductID);
388 this.fraDeviceIdentifiers.Controls.Add(this.txtVendorID);
389 this.fraDeviceIdentifiers.Controls.Add(this.lblVendorID);
390 this.fraDeviceIdentifiers.Location = new System.Drawing.Point(409, 12);
391 this.fraDeviceIdentifiers.Name = "fraDeviceIdentifiers";
392 this.fraDeviceIdentifiers.Size = new System.Drawing.Size(208, 96);
393 this.fraDeviceIdentifiers.TabIndex = 10;
394 this.fraDeviceIdentifiers.TabStop = false;
395 this.fraDeviceIdentifiers.Text = "Device Identifiers";
399 this.txtProductID.Location = new System.Drawing.Point(120, 56);
400 this.txtProductID.Name = "txtProductID";
401 this.txtProductID.Size = new System.Drawing.Size(72, 20);
402 this.txtProductID.TabIndex = 3;
403 this.txtProductID.Text = "1299";
404 this.txtProductID.TextChanged += new System.EventHandler(this.txtProductID_TextChanged);
408 this.lblProductID.Location = new System.Drawing.Point(16, 56);
409 this.lblProductID.Name = "lblProductID";
410 this.lblProductID.Size = new System.Drawing.Size(112, 23);
411 this.lblProductID.TabIndex = 2;
412 this.lblProductID.Text = "Product ID (hex):";
416 this.txtVendorID.Location = new System.Drawing.Point(120, 24);
417 this.txtVendorID.Name = "txtVendorID";
418 this.txtVendorID.Size = new System.Drawing.Size(72, 20);
419 this.txtVendorID.TabIndex = 1;
420 this.txtVendorID.Text = "0925";
421 this.txtVendorID.TextChanged += new System.EventHandler(this.txtVendorID_TextChanged);
425 this.lblVendorID.Location = new System.Drawing.Point(16, 24);
426 this.lblVendorID.Name = "lblVendorID";
427 this.lblVendorID.Size = new System.Drawing.Size(112, 23);
428 this.lblVendorID.TabIndex = 0;
429 this.lblVendorID.Text = "Vendor ID (hex):";
433 this.cmdFindDevice.Location = new System.Drawing.Point(636, 12);
434 this.cmdFindDevice.Name = "cmdFindDevice";
435 this.cmdFindDevice.Size = new System.Drawing.Size(136, 26);
436 this.cmdFindDevice.TabIndex = 11;
437 this.cmdFindDevice.Text = "Find My Device";
438 this.cmdFindDevice.Click += new System.EventHandler(this.cmdFindDevice_Click);
440 // cmdSendOutputReportInterrupt
442 this.cmdSendOutputReportInterrupt.Location = new System.Drawing.Point(21, 27);
443 this.cmdSendOutputReportInterrupt.Name = "cmdSendOutputReportInterrupt";
444 this.cmdSendOutputReportInterrupt.Size = new System.Drawing.Size(118, 26);
445 this.cmdSendOutputReportInterrupt.TabIndex = 12;
446 this.cmdSendOutputReportInterrupt.Text = "Send Output Report";
447 this.cmdSendOutputReportInterrupt.UseVisualStyleBackColor = true;
448 this.cmdSendOutputReportInterrupt.Click += new System.EventHandler(this.cmdSendOutputReportInterrupt_Click);
450 // cmdGetInputReportInterrupt
452 this.cmdGetInputReportInterrupt.Location = new System.Drawing.Point(21, 60);
453 this.cmdGetInputReportInterrupt.Name = "cmdGetInputReportInterrupt";
454 this.cmdGetInputReportInterrupt.Size = new System.Drawing.Size(118, 26);
455 this.cmdGetInputReportInterrupt.TabIndex = 13;
456 this.cmdGetInputReportInterrupt.Text = "Get Input Report";
457 this.cmdGetInputReportInterrupt.UseVisualStyleBackColor = true;
458 this.cmdGetInputReportInterrupt.Click += new System.EventHandler(this.cmdGetInputReportInterrupt_Click);
460 // fraInterruptTransfers
462 this.fraInterruptTransfers.BackColor = System.Drawing.SystemColors.Control;
463 this.fraInterruptTransfers.Controls.Add(this.cmdSendOutputReportInterrupt);
464 this.fraInterruptTransfers.Controls.Add(this.cmdGetInputReportInterrupt);
465 this.fraInterruptTransfers.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
466 this.fraInterruptTransfers.ForeColor = System.Drawing.SystemColors.ControlText;
467 this.fraInterruptTransfers.Location = new System.Drawing.Point(338, 129);
468 this.fraInterruptTransfers.Name = "fraInterruptTransfers";
469 this.fraInterruptTransfers.RightToLeft = System.Windows.Forms.RightToLeft.No;
470 this.fraInterruptTransfers.Size = new System.Drawing.Size(151, 100);
471 this.fraInterruptTransfers.TabIndex = 14;
472 this.fraInterruptTransfers.TabStop = false;
473 this.fraInterruptTransfers.Text = "Interrupt Transfers";
475 // cmdPeriodicTransfers
477 this.cmdPeriodicTransfers.Location = new System.Drawing.Point(153, 36);
478 this.cmdPeriodicTransfers.Name = "cmdPeriodicTransfers";
479 this.cmdPeriodicTransfers.Size = new System.Drawing.Size(118, 26);
480 this.cmdPeriodicTransfers.TabIndex = 16;
481 this.cmdPeriodicTransfers.Text = "Start";
482 this.cmdPeriodicTransfers.UseVisualStyleBackColor = true;
483 this.cmdPeriodicTransfers.Click += new System.EventHandler(this.cmdPeriodicTransfers_Click);
485 // cmdSendOutputReportControl
487 this.cmdSendOutputReportControl.Location = new System.Drawing.Point(10, 27);
488 this.cmdSendOutputReportControl.Name = "cmdSendOutputReportControl";
489 this.cmdSendOutputReportControl.Size = new System.Drawing.Size(118, 26);
490 this.cmdSendOutputReportControl.TabIndex = 12;
491 this.cmdSendOutputReportControl.Text = "Send Output Report";
492 this.cmdSendOutputReportControl.UseVisualStyleBackColor = true;
493 this.cmdSendOutputReportControl.Click += new System.EventHandler(this.cmdSendOutputReportControl_Click);
495 // cmdGetInputReportControl
497 this.cmdGetInputReportControl.Location = new System.Drawing.Point(10, 60);
498 this.cmdGetInputReportControl.Name = "cmdGetInputReportControl";
499 this.cmdGetInputReportControl.Size = new System.Drawing.Size(118, 26);
500 this.cmdGetInputReportControl.TabIndex = 13;
501 this.cmdGetInputReportControl.Text = "Get Input Report";
502 this.cmdGetInputReportControl.UseVisualStyleBackColor = true;
503 this.cmdGetInputReportControl.Click += new System.EventHandler(this.cmdGetInputReportControl_Click);
505 // fraControlTransfers
507 this.fraControlTransfers.BackColor = System.Drawing.SystemColors.Control;
508 this.fraControlTransfers.Controls.Add(this.cmdGetFeatureReport);
509 this.fraControlTransfers.Controls.Add(this.cmdSendFeatureReport);
510 this.fraControlTransfers.Controls.Add(this.cmdSendOutputReportControl);
511 this.fraControlTransfers.Controls.Add(this.cmdGetInputReportControl);
512 this.fraControlTransfers.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
513 this.fraControlTransfers.ForeColor = System.Drawing.SystemColors.ControlText;
514 this.fraControlTransfers.Location = new System.Drawing.Point(495, 129);
515 this.fraControlTransfers.Name = "fraControlTransfers";
516 this.fraControlTransfers.RightToLeft = System.Windows.Forms.RightToLeft.No;
517 this.fraControlTransfers.Size = new System.Drawing.Size(277, 100);
518 this.fraControlTransfers.TabIndex = 15;
519 this.fraControlTransfers.TabStop = false;
520 this.fraControlTransfers.Text = "Control Transfers";
522 // cmdGetFeatureReport
524 this.cmdGetFeatureReport.Location = new System.Drawing.Point(141, 60);
525 this.cmdGetFeatureReport.Name = "cmdGetFeatureReport";
526 this.cmdGetFeatureReport.Size = new System.Drawing.Size(118, 26);
527 this.cmdGetFeatureReport.TabIndex = 15;
528 this.cmdGetFeatureReport.Text = "Get Feature Report";
529 this.cmdGetFeatureReport.UseVisualStyleBackColor = true;
530 this.cmdGetFeatureReport.Click += new System.EventHandler(this.cmdGetFeatureReport_Click);
532 // cmdSendFeatureReport
534 this.cmdSendFeatureReport.Location = new System.Drawing.Point(141, 27);
535 this.cmdSendFeatureReport.Name = "cmdSendFeatureReport";
536 this.cmdSendFeatureReport.Size = new System.Drawing.Size(118, 26);
537 this.cmdSendFeatureReport.TabIndex = 14;
538 this.cmdSendFeatureReport.Text = "Send Feature Report";
539 this.cmdSendFeatureReport.UseVisualStyleBackColor = true;
540 this.cmdSendFeatureReport.Click += new System.EventHandler(this.cmdSendFeatureReport_Click);
542 // fraSendAndGetContinuous
544 this.fraSendAndGetContinuous.BackColor = System.Drawing.SystemColors.Control;
545 this.fraSendAndGetContinuous.Controls.Add(this.radFeature);
546 this.fraSendAndGetContinuous.Controls.Add(this.radInputOutputControl);
547 this.fraSendAndGetContinuous.Controls.Add(this.radInputOutputInterrupt);
548 this.fraSendAndGetContinuous.Controls.Add(this.cmdPeriodicTransfers);
549 this.fraSendAndGetContinuous.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
550 this.fraSendAndGetContinuous.ForeColor = System.Drawing.SystemColors.ControlText;
551 this.fraSendAndGetContinuous.Location = new System.Drawing.Point(311, 235);
552 this.fraSendAndGetContinuous.Name = "fraSendAndGetContinuous";
553 this.fraSendAndGetContinuous.RightToLeft = System.Windows.Forms.RightToLeft.No;
554 this.fraSendAndGetContinuous.Size = new System.Drawing.Size(295, 112);
555 this.fraSendAndGetContinuous.TabIndex = 17;
556 this.fraSendAndGetContinuous.TabStop = false;
557 this.fraSendAndGetContinuous.Text = "Send and Get Continuous";
561 this.radFeature.AutoSize = true;
562 this.radFeature.Location = new System.Drawing.Point(17, 76);
563 this.radFeature.Name = "radFeature";
564 this.radFeature.Size = new System.Drawing.Size(62, 18);
565 this.radFeature.TabIndex = 19;
566 this.radFeature.TabStop = true;
567 this.radFeature.Text = "Feature";
568 this.radFeature.UseVisualStyleBackColor = true;
569 this.radFeature.CheckedChanged += new System.EventHandler(this.radFeature_CheckedChanged);
571 // radInputOutputControl
573 this.radInputOutputControl.AutoSize = true;
574 this.radInputOutputControl.Location = new System.Drawing.Point(17, 52);
575 this.radInputOutputControl.Name = "radInputOutputControl";
576 this.radInputOutputControl.Size = new System.Drawing.Size(120, 18);
577 this.radInputOutputControl.TabIndex = 18;
578 this.radInputOutputControl.TabStop = true;
579 this.radInputOutputControl.Text = "Input Output Control";
580 this.radInputOutputControl.UseVisualStyleBackColor = true;
581 this.radInputOutputControl.CheckedChanged += new System.EventHandler(this.radInputOutputControl_CheckedChanged);
583 // radInputOutputInterrupt
585 this.radInputOutputInterrupt.AutoSize = true;
586 this.radInputOutputInterrupt.Location = new System.Drawing.Point(17, 28);
587 this.radInputOutputInterrupt.Name = "radInputOutputInterrupt";
588 this.radInputOutputInterrupt.Size = new System.Drawing.Size(126, 18);
589 this.radInputOutputInterrupt.TabIndex = 17;
590 this.radInputOutputInterrupt.TabStop = true;
591 this.radInputOutputInterrupt.Text = "Input Output Interrupt";
592 this.radInputOutputInterrupt.UseVisualStyleBackColor = true;
593 this.radInputOutputInterrupt.CheckedChanged += new System.EventHandler(this.radInputOutputInterrupt_CheckedChanged);
597 this.treeViewDevices.Location = new System.Drawing.Point(12, 12);
598 this.treeViewDevices.Name = "treeViewDevices";
599 this.treeViewDevices.Size = new System.Drawing.Size(284, 461);
600 this.treeViewDevices.TabIndex = 18;
601 this.treeViewDevices.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeViewDevices_AfterSelect);
605 this.ClientSize = new System.Drawing.Size(784, 756);
606 this.Controls.Add(this.treeViewDevices);
607 this.Controls.Add(this.fraSendAndGetContinuous);
608 this.Controls.Add(this.fraControlTransfers);
609 this.Controls.Add(this.fraInterruptTransfers);
610 this.Controls.Add(this.cmdFindDevice);
611 this.Controls.Add(this.fraDeviceIdentifiers);
612 this.Controls.Add(this.fraInputReportBufferSize);
613 this.Controls.Add(this.FraBytesReceived);
614 this.Controls.Add(this.FraBytesToSend);
615 this.Controls.Add(this.LstResults);
616 this.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
617 this.Location = new System.Drawing.Point(21, 28);
618 this.Name = "FrmMain";
619 this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
620 this.Text = "Generic HID Tester";
621 this.Closed += new System.EventHandler(this.frmMain_Closed);
622 this.Load += new System.EventHandler(this.frmMain_Load);
623 this.FraBytesReceived.ResumeLayout(false);
624 this.FraBytesReceived.PerformLayout();
625 this.FraBytesToSend.ResumeLayout(false);
626 this.fraInputReportBufferSize.ResumeLayout(false);
627 this.fraInputReportBufferSize.PerformLayout();
628 this.fraDeviceIdentifiers.ResumeLayout(false);
629 this.fraDeviceIdentifiers.PerformLayout();
630 this.fraInterruptTransfers.ResumeLayout(false);
631 this.fraControlTransfers.ResumeLayout(false);
632 this.fraSendAndGetContinuous.ResumeLayout(false);
633 this.fraSendAndGetContinuous.PerformLayout();
634 this.ResumeLayout(false);
637 #endregion '"Windows Form Designer generated code "'
639 private Boolean _deviceDetected;
640 private IntPtr _deviceNotificationHandle;
641 private FileStream _deviceData;
642 private FormActions _formActions;
643 private SafeFileHandle _hidHandle;
644 private String _hidUsage;
645 private ManagementEventWatcher _deviceArrivedWatcher;
646 private Boolean _deviceHandleObtained;
647 private ManagementEventWatcher _deviceRemovedWatcher;
648 private Int32 _myProductId;
649 private Int32 _myVendorId;
650 private Boolean _periodicTransfersRequested;
651 private ReportReadOrWritten _readOrWritten;
652 private ReportTypes _reportType;
653 private SendOrGet _sendOrGet;
654 private Boolean _transferInProgress;
655 private TransferTypes _transferType;
657 private static System.Timers.Timer _periodicTransfers;
659 private readonly Debugging _myDebugging = new Debugging(); // For viewing results of API calls via Debug.Write.
660 private readonly DeviceManagement _myDeviceManagement = new DeviceManagement();
661 private Hid _myHid = new Hid();
663 private enum FormActions
666 DisableInputReportBufferSize,
667 EnableGetInputReportInterruptTransfer,
668 EnableInputReportBufferSize,
669 EnableSendOutputReportInterrupt,
670 ScrollToBottomOfListBox,
671 SetInputReportBufferSize,
674 SelectDeviceInTreeView,
675 CompleteDeviceTreeView
678 private enum ReportReadOrWritten
684 private enum ReportTypes
691 private enum SendOrGet
697 private enum TransferTypes
703 private enum WmiDeviceProperties
714 internal FrmMain FrmMy;
716 // This delegate has the same parameters as AccessForm.
717 // Used in accessing the application's form from a different thread.
719 private delegate void MarshalDataToForm(FormActions action, params string[] strings);
722 /// Performs various application-specific functions that
723 /// involve accessing the application's form.
726 /// <param name="action"> a FormActions member that names the action to perform on the form</param>
727 /// <param name="formText"> text that the form displays or the code uses for
728 /// another purpose. Actions that don't use text ignore this parameter. </param>
730 private void AccessForm(FormActions action, params string[] strings)
734 // Select an action to perform on the form:
738 case FormActions.AddItemToListBox:
740 LstResults.Items.Add(strings[0]);
743 case FormActions.DisableInputReportBufferSize:
745 cmdInputReportBufferSize.Enabled = false;
748 case FormActions.EnableGetInputReportInterruptTransfer:
750 cmdGetInputReportInterrupt.Enabled = true;
753 case FormActions.EnableInputReportBufferSize:
755 cmdInputReportBufferSize.Enabled = true;
758 case FormActions.EnableSendOutputReportInterrupt:
760 cmdSendOutputReportInterrupt.Enabled = true;
763 case FormActions.ScrollToBottomOfListBox:
765 LstResults.SelectedIndex = LstResults.Items.Count - 1;
768 case FormActions.SetInputReportBufferSize:
770 txtInputReportBufferSize.Text = strings[0];
773 case FormActions.AddDeviceToTreeView:
775 //Try and see if our device is already present
776 TreeNode[] res = treeViewDevices.Nodes.Find(strings[0], false);
777 foreach (TreeNode device in res)
779 if (device.ForeColor == Color.Red)
781 //Device was removed and has now been added back
782 device.ForeColor = Color.Green;
786 //Device was already there set back our device color to black
787 device.ForeColor = Color.Black;
793 //Our device is already there
797 //Build our node from our string array
798 TreeNode newNode = new TreeNode(strings[0]);
799 for (int i = 1; i < strings.Length; i++)
801 newNode.Nodes.Add(strings[i]);
802 if (strings[i].StartsWith("Name: "))
804 //Found our name property, update our node text
805 newNode.Text = strings[i].Substring(6, strings[i].Length - 6);
809 //New device color is green
810 newNode.ForeColor = Color.Green;
811 newNode.Name = strings[0]; //Set ID as name to make sure we can find it
812 treeViewDevices.Nodes.Add(newNode);
816 case FormActions.ResetDeviceTreeView:
818 //Mark all non removed/red device as purple/unknown
819 foreach (TreeNode device in treeViewDevices.Nodes)
821 if (device.ForeColor != Color.Red)
823 device.ForeColor = Color.Purple;
826 //treeViewDevices.Nodes.Clear();
830 case FormActions.CompleteDeviceTreeView:
832 //Our device list is now complete
833 foreach (TreeNode device in treeViewDevices.Nodes)
835 //Purple devices need to be marked as red for removed
836 if (device.ForeColor == Color.Purple)
838 device.ForeColor = Color.Red;
844 case FormActions.SelectDeviceInTreeView:
846 //treeViewDevices.SelectedNode = null;
847 TreeNode[] res = treeViewDevices.Nodes.Find(strings[0], false);
848 foreach (TreeNode device in res)
850 device.ForeColor = Color.Blue;
851 //treeViewDevices.SelectedNode = res[0];
852 //treeViewDevices.SelectedNode.ForeColor = Color.Blue;
862 DisplayException(Name, ex);
868 /// Add a handler to detect arrival of devices using WMI.
871 private void AddDeviceArrivedHandler()
873 const Int32 pollingIntervalSeconds = 3;
874 var scope = new ManagementScope("root\\CIMV2");
875 scope.Options.EnablePrivileges = true;
879 var q = new WqlEventQuery();
880 q.EventClassName = "__InstanceCreationEvent";
881 q.WithinInterval = new TimeSpan(0, 0, pollingIntervalSeconds);
882 q.Condition = @"TargetInstance ISA 'Win32_USBControllerdevice'";
883 _deviceArrivedWatcher = new ManagementEventWatcher(scope, q);
884 _deviceArrivedWatcher.EventArrived += DeviceAdded;
886 _deviceArrivedWatcher.Start();
890 Debug.WriteLine(e.Message);
891 if (_deviceArrivedWatcher != null)
892 _deviceArrivedWatcher.Stop();
897 /// Add a handler to detect removal of devices using WMI.
900 private void AddDeviceRemovedHandler()
902 const Int32 pollingIntervalSeconds = 3;
903 var scope = new ManagementScope("root\\CIMV2");
904 scope.Options.EnablePrivileges = true;
908 var q = new WqlEventQuery();
909 q.EventClassName = "__InstanceDeletionEvent";
910 q.WithinInterval = new TimeSpan(0, 0, pollingIntervalSeconds);
911 q.Condition = @"TargetInstance ISA 'Win32_USBControllerdevice'";
912 _deviceRemovedWatcher = new ManagementEventWatcher(scope, q);
913 _deviceRemovedWatcher.EventArrived += DeviceRemoved;
914 _deviceRemovedWatcher.Start();
918 Debug.WriteLine(e.Message);
919 if (_deviceRemovedWatcher != null)
920 _deviceRemovedWatcher.Stop();
925 /// Close the handle and FileStreams for a device.
928 private void CloseCommunications()
930 if (_deviceData != null)
935 if ((_hidHandle != null) && (!(_hidHandle.IsInvalid)))
940 // The next attempt to communicate will get a new handle and FileStreams.
942 _deviceHandleObtained = false;
946 /// Search for a specific device.
949 private void cmdFindDevice_Click(Object sender, EventArgs e)
953 if (_transferInProgress)
955 DisplayTransferInProgressMessage();
959 _deviceDetected = FindDeviceUsingWmi();
968 DisplayException(Name, ex);
974 /// Request to get a Feature report from the device.
976 /// <param name="sender"></param>
977 /// <param name="e"></param>
979 private void cmdGetFeatureReport_Click(object sender, EventArgs e)
983 if (_transferInProgress)
985 DisplayTransferInProgressMessage();
989 // Don't allow another transfer request until this one completes.
990 // Move the focus away from the button to prevent the focus from
991 // switching to the next control in the tab order on disabling the button.
993 fraControlTransfers.Focus();
994 cmdGetFeatureReport.Enabled = false;
995 _transferType = TransferTypes.Control;
996 RequestToGetFeatureReport();
1001 DisplayException(Name, ex);
1007 /// Request to get an Input report from the device using a control transfer.
1009 /// <param name="sender"></param>
1010 /// <param name="e"></param>
1012 private void cmdGetInputReportControl_Click(object sender, EventArgs e)
1016 // Don't allow another transfer request until this one completes.
1017 // Move the focus away from the button to prevent the focus from
1018 // switching to the next control in the tab order on disabling the button.
1020 if (_transferInProgress)
1022 DisplayTransferInProgressMessage();
1026 fraControlTransfers.Focus();
1027 cmdGetInputReportControl.Enabled = false;
1028 _transferType = TransferTypes.Control;
1029 RequestToGetInputReport();
1032 catch (Exception ex)
1034 DisplayException(Name, ex);
1040 /// Request to get an Input report retrieved using interrupt transfers.
1042 /// <param name="sender"></param>
1043 /// <param name="e"></param>
1045 private void cmdGetInputReportInterrupt_Click(object sender, EventArgs e)
1049 if (_transferInProgress)
1051 DisplayTransferInProgressMessage();
1055 // Don't allow another transfer request until this one completes.
1056 // Move the focus away from the button to prevent the focus from
1057 // switching to the next control in the tab order on disabling the button.
1059 fraInterruptTransfers.Focus();
1060 cmdGetInputReportInterrupt.Enabled = false;
1061 _transferType = TransferTypes.Interrupt;
1062 RequestToGetInputReport();
1065 catch (Exception ex)
1067 DisplayException(Name, ex);
1073 /// Set the number of Input reports the HID driver will store.
1076 private void cmdInputReportBufferSize_Click(Object sender, EventArgs e)
1080 if (_transferInProgress)
1082 DisplayTransferInProgressMessage();
1086 SetInputReportBufferSize();
1092 DisplayException(Name, ex);
1098 /// Alternate sending and getting a report.
1100 /// <param name="sender"></param>
1101 /// <param name="e"></param>
1103 private void cmdPeriodicTransfers_Click(object sender, EventArgs e)
1107 if (cmdPeriodicTransfers.Text == "Start")
1109 if (_transferInProgress)
1111 DisplayTransferInProgressMessage();
1115 _sendOrGet = SendOrGet.Send;
1116 PeriodicTransfersStart();
1121 PeriodicTransfersStop();
1124 catch (Exception ex)
1126 DisplayException(Name, ex);
1132 /// Request to send a Feature report using a control transfer.
1134 /// <param name="sender"></param>
1135 /// <param name="e"></param>
1137 private void cmdSendFeatureReport_Click(object sender, EventArgs e)
1141 if (_transferInProgress)
1143 DisplayTransferInProgressMessage();
1147 // Don't allow another transfer request until this one completes.
1148 // Move the focus away from the button to prevent the focus from
1149 // switching to the next control in the tab order on disabling the button.
1151 fraControlTransfers.Focus();
1152 cmdSendFeatureReport.Enabled = false;
1153 _transferType = TransferTypes.Control;
1154 RequestToSendFeatureReport();
1157 catch (Exception ex)
1159 DisplayException(Name, ex);
1165 /// Request to send an Output report using a control transfer.
1167 /// <param name="sender"></param>
1168 /// <param name="e"></param>
1170 private void cmdSendOutputReportControl_Click(object sender, EventArgs e)
1174 if (_transferInProgress)
1176 DisplayTransferInProgressMessage();
1180 // Don't allow another transfer request until this one completes.
1181 // Move the focus away from the button to prevent the focus from
1182 // switching to the next control in the tab order on disabling the button.
1184 fraControlTransfers.Focus();
1185 cmdSendOutputReportControl.Enabled = false;
1186 _transferType = TransferTypes.Control;
1187 RequestToSendOutputReport();
1190 catch (Exception ex)
1192 DisplayException(Name, ex);
1198 /// Request to send an Output report using an interrupt transfer.
1200 /// <param name="sender"></param>
1201 /// <param name="e"></param>
1203 private void cmdSendOutputReportInterrupt_Click(object sender, EventArgs e)
1207 if (_transferInProgress)
1209 DisplayTransferInProgressMessage();
1213 // Don't allow another transfer request until this one completes.
1214 // Move the focus away from the button to prevent the focus from
1215 // switching to the next control in the tab order on disabling the button.
1217 fraInterruptTransfers.Focus();
1218 cmdSendOutputReportInterrupt.Enabled = false;
1219 _transferType = TransferTypes.Interrupt;
1220 RequestToSendOutputReport();
1223 catch (Exception ex)
1225 DisplayException(Name, ex);
1231 /// Called on arrival of any device.
1232 /// Calls a routine that searches to see if the desired device is present.
1235 private void DeviceAdded(object sender, EventArrivedEventArgs e)
1239 Debug.WriteLine("A USB device has been inserted");
1241 _deviceDetected = FindDeviceUsingWmi();
1243 catch (Exception ex)
1245 DisplayException(Name, ex);
1251 /// Called if the user changes the Vendor ID or Product ID in the text box.
1254 private void DeviceHasChanged()
1258 // If a device was previously detected, stop receiving notifications about it.
1260 if (_deviceHandleObtained)
1262 DeviceNotificationsStop();
1264 CloseCommunications();
1266 // Look for a device that matches the Vendor ID and Product ID in the text boxes.
1270 catch (Exception ex)
1272 DisplayException(Name, ex);
1278 /// Add handlers to detect device arrival and removal.
1281 private void DeviceNotificationsStart()
1283 AddDeviceArrivedHandler();
1284 AddDeviceRemovedHandler();
1288 /// Stop receiving notifications about device arrival and removal
1291 private void DeviceNotificationsStop()
1295 if (_deviceArrivedWatcher != null)
1296 _deviceArrivedWatcher.Stop();
1297 if (_deviceRemovedWatcher != null)
1298 _deviceRemovedWatcher.Stop();
1300 catch (Exception ex)
1302 DisplayException(Name, ex);
1308 /// Called on removal of any device.
1309 /// Calls a routine that searches to see if the desired device is still present.
1312 private void DeviceRemoved(object sender, EventArgs e)
1316 Debug.WriteLine("A USB device has been removed");
1318 _deviceDetected = FindDeviceUsingWmi();
1320 if (!_deviceDetected)
1322 _deviceHandleObtained = false;
1323 CloseCommunications();
1326 catch (Exception ex)
1328 DisplayException(Name, ex);
1334 /// Displays received or written report data.
1337 /// <param name="buffer"> contains the report data. </param>
1338 /// <param name="currentReportType" > "Input", "Output", or "Feature"</param>
1339 /// <param name="currentReadOrWritten" > "read" for Input and IN Feature reports, "written" for Output and OUT Feature reports.</param>
1341 private void DisplayReportData(Byte[] buffer, ReportTypes currentReportType, ReportReadOrWritten currentReadOrWritten)
1347 LstResults.Items.Add(currentReportType.ToString() + " report has been " + currentReadOrWritten.ToString().ToLower() + ".");
1349 // Display the report data received in the form's list box.
1351 LstResults.Items.Add(" Report ID: " + String.Format("{0:X2} ", buffer[0]));
1352 LstResults.Items.Add(" Report Data:");
1354 TxtBytesReceived.Text = "";
1356 for (count = 1; count <= buffer.Length - 1; count++)
1358 // Display bytes as 2-character Hex strings.
1360 String byteValue = String.Format("{0:X2} ", buffer[count]);
1362 LstResults.Items.Add(" " + byteValue);
1364 // Display the received bytes in the text box.
1366 TxtBytesReceived.SelectionStart = TxtBytesReceived.Text.Length;
1367 TxtBytesReceived.SelectedText = byteValue + Environment.NewLine;
1369 ScrollToBottomOfListBox();
1371 catch (Exception ex)
1373 DisplayException(Name, ex);
1379 /// Display a message if the user clicks a button when a transfer is in progress.
1382 private void DisplayTransferInProgressMessage()
1384 AccessForm(FormActions.AddItemToListBox, "Command not executed because a transfer is in progress.");
1385 ScrollToBottomOfListBox();
1389 /// Do periodic transfers.
1391 /// <param name="source"></param>
1392 /// <param name="e"></param>
1394 /// The timer is enabled only if continuous (periodic) transfers have been requested.
1397 private void DoPeriodicTransfers(object source, ElapsedEventArgs e)
1401 PeriodicTransfers();
1403 catch (Exception ex)
1405 DisplayException(Name, ex);
1411 /// Enable the command buttons on the form.
1412 /// Needed after attempting a transfer and device not found.
1415 private void EnableFormControls()
1417 cmdGetInputReportInterrupt.Enabled = true;
1418 cmdSendOutputReportControl.Enabled = true;
1419 cmdGetInputReportControl.Enabled = true;
1420 cmdGetFeatureReport.Enabled = true;
1421 cmdSendFeatureReport.Enabled = true;
1422 cmdPeriodicTransfers.Enabled = true;
1423 cmdSendOutputReportInterrupt.Enabled = true;
1427 /// Use the System.Management class to find a device by Vendor ID and Product ID using WMI. If found, display device properties.
1430 /// During debugging, if you stop the firmware but leave the device attached, the device may still be detected as present
1431 /// but will be unable to communicate. The device will show up in Windows Device Manager as well.
1432 /// This situation is unlikely to occur with a final product.
1435 private Boolean FindDeviceUsingWmi()
1439 MyMarshalDataToForm(FormActions.ResetDeviceTreeView);
1440 // Prepend "@" to string below to treat backslash as a normal character (not escape character):
1442 String deviceIdString = @"USB\VID_" + _myVendorId.ToString("X4") + "&PID_" + _myProductId.ToString("X4");
1444 _deviceDetected = false;
1445 var searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_PnPEntity");
1446 int usbDeviceCounter = 0;
1448 foreach (ManagementObject queryObj in searcher.Get())
1450 string deviceId = queryObj["PNPDeviceID"].ToString();
1451 if (deviceId.Contains(deviceIdString))
1453 _deviceDetected = true;
1454 MyMarshalDataToForm(FormActions.AddItemToListBox, "My device found (WMI):");
1456 MyMarshalDataToForm(FormActions.AddItemToListBox, "--------");
1457 List<string> device = new List<string>();
1458 device.Add(deviceId);
1460 // Display device properties.
1461 foreach (WmiDeviceProperties wmiDeviceProperty in Enum.GetValues(typeof(WmiDeviceProperties)))
1463 MyMarshalDataToForm(FormActions.AddItemToListBox, (wmiDeviceProperty.ToString() + ": " + queryObj[wmiDeviceProperty.ToString()]));
1464 device.Add((wmiDeviceProperty.ToString() + ": " + queryObj[wmiDeviceProperty.ToString()]));
1465 Debug.WriteLine(wmiDeviceProperty.ToString() + ": {0}", queryObj[wmiDeviceProperty.ToString()]);
1467 MyMarshalDataToForm(FormActions.AddItemToListBox, "--------");
1468 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1470 MyMarshalDataToForm(FormActions.AddDeviceToTreeView, device.ToArray());
1471 MyMarshalDataToForm(FormActions.SelectDeviceInTreeView, deviceId);
1474 else if (deviceId.StartsWith("USB\\VID"))
1476 List<string> device = new List<string>();
1477 device.Add(deviceId);
1479 // Add device properties.
1480 foreach (WmiDeviceProperties wmiDeviceProperty in Enum.GetValues(typeof(WmiDeviceProperties)))
1482 device.Add((wmiDeviceProperty.ToString() + ": " + queryObj[wmiDeviceProperty.ToString()]));
1483 Debug.WriteLine(wmiDeviceProperty.ToString() + ": {0}", queryObj[wmiDeviceProperty.ToString()]);
1486 MyMarshalDataToForm(FormActions.AddDeviceToTreeView, device.ToArray());
1490 //Complete our device TreeView
1491 MyMarshalDataToForm(FormActions.CompleteDeviceTreeView);
1494 MyMarshalDataToForm(FormActions.AddItemToListBox, "Found " + usbDeviceCounter /*searcher.Get().Count*/ + " USB HID devices");
1496 if (!_deviceDetected)
1498 MyMarshalDataToForm(FormActions.AddItemToListBox, "My device not found (WMI)");
1499 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1501 return _deviceDetected;
1503 catch (Exception ex)
1505 DisplayException(Name, ex);
1511 /// Call HID functions that use Win32 API functions to locate a HID-class device
1512 /// by its Vendor ID and Product ID. Open a handle to the device.
1516 /// True if the device is detected, False if not detected.
1519 private Boolean FindTheHid()
1521 var devicePathName = new String[128];
1522 String myDevicePathName = "";
1526 _deviceHandleObtained = false;
1527 CloseCommunications();
1529 // Get the device's Vendor ID and Product ID from the form's text boxes.
1531 GetVendorAndProductIDsFromTextBoxes(ref _myVendorId, ref _myProductId);
1533 // Get the HID-class GUID.
1535 Guid hidGuid = _myHid.GetHidGuid();
1537 String functionName = "GetHidGuid";
1538 Debug.WriteLine(_myDebugging.ResultOfApiCall(functionName));
1539 Debug.WriteLine(" GUID for system HIDs: " + hidGuid.ToString());
1541 // Fill an array with the device path names of all attached HIDs.
1543 Boolean availableHids = _myDeviceManagement.FindDeviceFromGuid(hidGuid, ref devicePathName);
1545 // If there is at least one HID, attempt to read the Vendor ID and Product ID
1546 // of each device until there is a match or all devices have been examined.
1550 Int32 memberIndex = 0;
1554 // Open the handle without read/write access to enable getting information about any HID, even system keyboards and mice.
1556 _hidHandle = _myHid.OpenHandle(devicePathName[memberIndex], false);
1558 functionName = "CreateFile";
1559 Debug.WriteLine(_myDebugging.ResultOfApiCall(functionName));
1560 Debug.WriteLine(" Returned handle: " + _hidHandle);
1562 if (!_hidHandle.IsInvalid)
1564 // The returned handle is valid,
1565 // so find out if this is the device we're looking for.
1567 _myHid.DeviceAttributes.Size = Marshal.SizeOf(_myHid.DeviceAttributes);
1569 Boolean success = _myHid.GetAttributes(_hidHandle, ref _myHid.DeviceAttributes);
1573 Debug.WriteLine(" HIDD_ATTRIBUTES structure filled without error.");
1574 Debug.WriteLine(" Structure size: " + _myHid.DeviceAttributes.Size);
1575 Debug.WriteLine(" Vendor ID: " + Convert.ToString(_myHid.DeviceAttributes.VendorID, 16));
1576 Debug.WriteLine(" Product ID: " + Convert.ToString(_myHid.DeviceAttributes.ProductID, 16));
1577 Debug.WriteLine(" Version Number: " + Convert.ToString(_myHid.DeviceAttributes.VersionNumber, 16));
1579 if ((_myHid.DeviceAttributes.VendorID == _myVendorId) && (_myHid.DeviceAttributes.ProductID == _myProductId))
1581 Debug.WriteLine(" Handle obtained to my device");
1583 // Display the information in form's list box.
1585 MyMarshalDataToForm(FormActions.AddItemToListBox, "Handle obtained to my device:");
1586 MyMarshalDataToForm(FormActions.AddItemToListBox, " Vendor ID= " + Convert.ToString(_myHid.DeviceAttributes.VendorID, 16));
1587 MyMarshalDataToForm(FormActions.AddItemToListBox, " Product ID = " + Convert.ToString(_myHid.DeviceAttributes.ProductID, 16));
1588 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1590 _deviceHandleObtained = true;
1592 myDevicePathName = devicePathName[memberIndex];
1596 // It's not a match, so close the handle.
1598 _deviceHandleObtained = false;
1604 // There was a problem retrieving the information.
1606 Debug.WriteLine(" Error in filling HIDD_ATTRIBUTES structure.");
1607 _deviceHandleObtained = false;
1612 // Keep looking until we find the device or there are no devices left to examine.
1614 memberIndex = memberIndex + 1;
1616 while (!((_deviceHandleObtained || (memberIndex == devicePathName.Length))));
1619 if (_deviceHandleObtained)
1621 // The device was detected.
1622 // Learn the capabilities of the device.
1624 _myHid.Capabilities = _myHid.GetDeviceCapabilities(_hidHandle);
1626 // Find out if the device is a system mouse or keyboard.
1628 _hidUsage = _myHid.GetHidUsage(_myHid.Capabilities);
1630 // Get the Input report buffer size.
1632 GetInputReportBufferSize();
1633 MyMarshalDataToForm(FormActions.EnableInputReportBufferSize, "");
1635 //Close the handle and reopen it with read/write access.
1639 _hidHandle = _myHid.OpenHandle(myDevicePathName, true);
1641 if (_hidHandle.IsInvalid)
1643 MyMarshalDataToForm(FormActions.AddItemToListBox, "The device is a system " + _hidUsage + ".");
1644 MyMarshalDataToForm(FormActions.AddItemToListBox, "Windows 2000 and later obtain exclusive access to Input and Output reports for this devices.");
1645 MyMarshalDataToForm(FormActions.AddItemToListBox, "Windows 8 also obtains exclusive access to Feature reports.");
1646 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1650 if (_myHid.Capabilities.InputReportByteLength > 0)
1652 // Set the size of the Input report buffer.
1654 var inputReportBuffer = new Byte[_myHid.Capabilities.InputReportByteLength];
1656 _deviceData = new FileStream(_hidHandle, FileAccess.Read | FileAccess.Write, inputReportBuffer.Length, false);
1659 if (_myHid.Capabilities.OutputReportByteLength > 0)
1661 Byte[] outputReportBuffer = null;
1663 // Flush any waiting reports in the input buffer. (optional)
1665 _myHid.FlushQueue(_hidHandle);
1670 MyMarshalDataToForm(FormActions.AddItemToListBox, "Device not found.");
1671 MyMarshalDataToForm(FormActions.DisableInputReportBufferSize, "");
1672 EnableFormControls();
1673 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1675 return _deviceHandleObtained;
1677 catch (Exception ex)
1679 DisplayException(Name, ex);
1685 /// Perform shutdown operations.
1688 private void frmMain_Closed(Object eventSender, EventArgs eventArgs)
1694 catch (Exception ex)
1696 DisplayException(Name, ex);
1702 /// Perform startup operations.
1705 private void frmMain_Load(Object eventSender, EventArgs eventArgs)
1712 catch (Exception ex)
1714 DisplayException(Name, ex);
1719 private void GetBytesToSend()
1723 // Get the bytes to send in a report from the combo boxes.
1724 // Increment the values if the autoincrement check box is selected.
1726 if (ChkAutoincrement.Checked)
1728 if (CboByte0.SelectedIndex < 255)
1730 CboByte0.SelectedIndex = CboByte0.SelectedIndex + 1;
1734 CboByte0.SelectedIndex = 0;
1736 if (CboByte1.SelectedIndex < 255)
1738 CboByte1.SelectedIndex = CboByte1.SelectedIndex + 1;
1742 CboByte1.SelectedIndex = 0;
1746 catch (Exception ex)
1748 DisplayException(Name, ex);
1754 /// Find and display the number of Input buffers
1755 /// (the number of Input reports the HID driver will store).
1758 private void GetInputReportBufferSize()
1760 Int32 numberOfInputBuffers = 0;
1765 // Get the number of input buffers.
1767 _myHid.GetNumberOfInputBuffers(_hidHandle, ref numberOfInputBuffers);
1769 // Display the result in the text box.
1771 MyMarshalDataToForm(FormActions.SetInputReportBufferSize, Convert.ToString(numberOfInputBuffers));
1773 catch (Exception ex)
1775 DisplayException(Name, ex);
1781 /// Retrieve a Vendor ID and Product ID in hexadecimal
1782 /// from the form's text boxes and convert the text to Int32s.
1785 /// <param name="myVendorId"> the Vendor ID</param>
1786 /// <param name="myProductId"> the Product ID</param>
1788 private void GetVendorAndProductIDsFromTextBoxes(ref Int32 myVendorId, ref Int32 myProductId)
1792 myVendorId = Int32.Parse(txtVendorID.Text, NumberStyles.AllowHexSpecifier);
1793 myProductId = Int32.Parse(txtProductID.Text, NumberStyles.AllowHexSpecifier);
1795 catch (Exception ex)
1797 DisplayException(Name, ex);
1803 /// Initialize the elements on the form.
1806 private void InitializeDisplay()
1810 // Create a dropdown list box for each byte to send in a report.
1811 // Display the values as 2-character hex strings.
1814 for (count = 0; count <= 255; count++)
1816 String byteValue = String.Format("{0:X2} ", count);
1817 FrmMy.CboByte0.Items.Insert(count, byteValue);
1818 FrmMy.CboByte1.Items.Insert(count, byteValue);
1821 // Select a default value for each box
1823 FrmMy.CboByte0.SelectedIndex = 0;
1824 FrmMy.CboByte1.SelectedIndex = 128;
1825 FrmMy.radInputOutputInterrupt.Checked = true;
1827 // Check the autoincrement box to increment the values each time a report is sent.
1829 ChkAutoincrement.CheckState = CheckState.Checked;
1831 // Don't allow the user to select an input report buffer size until there is
1832 // a handle to a HID.
1834 cmdInputReportBufferSize.Focus();
1835 cmdInputReportBufferSize.Enabled = false;
1837 LstResults.Items.Add("For a more detailed event log, view debug statements in Visual Studio's Output window:");
1838 LstResults.Items.Add("Click Build > Configuration Manager > Active Solution Configuration > Debug > Close.");
1839 LstResults.Items.Add("Then click View > Output.");
1840 LstResults.Items.Add("");
1842 catch (Exception ex)
1844 DisplayException(Name, ex);
1850 /// Enables accessing a form's controls from another thread
1853 /// <param name="action"> a FormActions member that names the action to perform on the form </param>
1854 /// <param name="textToDisplay"> text that the form displays or the code uses for
1855 /// another purpose. Actions that don't use text ignore this parameter. </param>
1857 private void MyMarshalDataToForm(FormActions action, params string[] strings)
1861 object[] args = { action, strings };
1863 // The AccessForm routine contains the code that accesses the form.
1865 MarshalDataToForm marshalDataToFormDelegate = AccessForm;
1867 // Execute AccessForm, passing the parameters in args.
1869 Invoke(marshalDataToFormDelegate, args);
1871 catch (Exception ex)
1873 DisplayException(Name, ex);
1879 /// Timeout if read via interrupt transfer doesn't return.
1882 private void OnReadTimeout()
1886 MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to read a report timed out.");
1887 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1888 CloseCommunications();
1889 MyMarshalDataToForm(FormActions.EnableGetInputReportInterruptTransfer, "");
1890 _transferInProgress = false;
1891 _sendOrGet = SendOrGet.Send;
1893 catch (Exception ex)
1895 DisplayException(Name, ex);
1901 /// Timeout if write via interrupt transfer doesn't return.
1904 private void OnWriteTimeout()
1908 MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to write a report timed out.");
1909 MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
1910 CloseCommunications();
1911 MyMarshalDataToForm(FormActions.EnableSendOutputReportInterrupt, "");
1912 _transferInProgress = false;
1913 _sendOrGet = SendOrGet.Get;
1915 catch (Exception ex)
1917 DisplayException(Name, ex);
1923 /// Alternat sending and getting a report.
1926 private void PeriodicTransfers()
1930 if (!_transferInProgress)
1932 if (_reportType == ReportTypes.Feature)
1934 SendOrGetFeatureReport();
1938 // Output and Input reports
1940 SendOutputReportOrGetInputReport();
1944 catch (Exception ex)
1946 DisplayException(Name, ex);
1952 /// Start doing periodic transfers.
1955 private void PeriodicTransfersStart()
1957 // Don't allow changing the transfer type while transfers are in progress.
1959 if (radFeature.Checked)
1961 radInputOutputControl.Enabled = false;
1962 radInputOutputInterrupt.Enabled = false;
1964 else if (radInputOutputControl.Checked)
1966 radFeature.Enabled = false;
1967 radInputOutputInterrupt.Enabled = false;
1969 else if (radInputOutputInterrupt.Checked)
1971 radFeature.Enabled = false;
1972 radInputOutputControl.Enabled = false;
1975 // Change the command button's text.
1977 cmdPeriodicTransfers.Text = "Stop";
1979 // Enable the timer event to trigger a set of transfers.
1981 _periodicTransfers.Start();
1983 cmdPeriodicTransfers.Enabled = true;
1985 if (radInputOutputInterrupt.Checked)
1987 _transferType = TransferTypes.Interrupt;
1988 _reportType = ReportTypes.Output;
1990 else if (radInputOutputControl.Checked)
1992 _transferType = TransferTypes.Control;
1993 _reportType = ReportTypes.Output;
1995 else if (radFeature.Checked)
1997 _transferType = TransferTypes.Control;
1998 _reportType = ReportTypes.Feature;
2000 _periodicTransfersRequested = true;
2001 PeriodicTransfers();
2005 /// Stop doing periodic transfers.
2008 private void PeriodicTransfersStop()
2010 // Stop doing continuous transfers.
2012 _periodicTransfersRequested = false;
2014 // Disable the timer that triggers the transfers.
2016 _periodicTransfers.Stop();
2017 cmdPeriodicTransfers.Enabled = true;
2019 // Change the command button's text.
2021 cmdPeriodicTransfers.Text = "Start";
2023 // Re-allow changing the transfer type.
2025 radFeature.Enabled = true;
2026 radInputOutputControl.Enabled = true;
2027 radInputOutputInterrupt.Enabled = true;
2030 private void radInputOutputControl_CheckedChanged(object sender, EventArgs e)
2034 private void radInputOutputInterrupt_CheckedChanged(object sender, EventArgs e)
2038 private void radFeature_CheckedChanged(object sender, EventArgs e)
2043 /// Request a Feature report.
2044 /// Assumes report ID = 0.
2047 private void RequestToGetFeatureReport()
2049 String byteValue = null;
2053 // If the device hasn't been detected, was removed, or timed out on a previous attempt
2054 // to access it, look for the device.
2056 if (!_deviceHandleObtained)
2058 _deviceHandleObtained = FindTheHid();
2061 if (_deviceHandleObtained)
2063 Byte[] inFeatureReportBuffer = null;
2065 if ((_myHid.Capabilities.FeatureReportByteLength > 0))
2067 // The HID has a Feature report.
2068 // Read a report from the device.
2070 // Set the size of the Feature report buffer.
2072 if ((_myHid.Capabilities.FeatureReportByteLength > 0))
2074 inFeatureReportBuffer = new Byte[_myHid.Capabilities.FeatureReportByteLength];
2079 Boolean success = _myHid.GetFeatureReport(_hidHandle, ref inFeatureReportBuffer);
2083 DisplayReportData(inFeatureReportBuffer, ReportTypes.Feature, ReportReadOrWritten.Read);
2087 CloseCommunications();
2088 MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to read a Feature report failed.");
2089 ScrollToBottomOfListBox();
2094 MyMarshalDataToForm(FormActions.AddItemToListBox, "The HID doesn't have a Feature report.");
2095 ScrollToBottomOfListBox();
2098 _transferInProgress = false;
2099 cmdGetFeatureReport.Enabled = true;
2101 catch (Exception ex)
2103 DisplayException(Name, ex);
2109 /// Request an Input report.
2110 /// Assumes report ID = 0.
2113 private async void RequestToGetInputReport()
2115 const Int32 readTimeout = 5000;
2117 String byteValue = null;
2118 Byte[] inputReportBuffer = null;
2122 Boolean success = false;
2124 // If the device hasn't been detected, was removed, or timed out on a previous attempt
2125 // to access it, look for the device.
2127 if (!_deviceHandleObtained)
2129 _deviceHandleObtained = FindTheHid();
2132 if (_deviceHandleObtained)
2134 // Don't attempt to exchange reports if valid handles aren't available
2135 // (as for a mouse or keyboard under Windows 2000 and later.)
2137 if (!_hidHandle.IsInvalid)
2139 // Read an Input report.
2141 // Don't attempt to send an Input report if the HID has no Input report.
2142 // (The HID spec requires all HIDs to have an interrupt IN endpoint,
2143 // which suggests that all HIDs must support Input reports.)
2145 if (_myHid.Capabilities.InputReportByteLength > 0)
2147 // Set the size of the Input report buffer.
2149 inputReportBuffer = new Byte[_myHid.Capabilities.InputReportByteLength];
2151 if (_transferType.Equals(TransferTypes.Control))
2154 _transferInProgress = true;
2156 // Read a report using a control transfer.
2158 success = _myHid.GetInputReportViaControlTransfer(_hidHandle, ref inputReportBuffer);
2159 cmdGetInputReportControl.Enabled = true;
2160 _transferInProgress = false;
2166 _transferInProgress = true;
2168 // Read a report using interrupt transfers.
2169 // Timeout if no report available.
2170 // To enable reading a report without blocking the calling thread, uses Filestream's ReadAsync method.
2172 // Create a delegate to execute on a timeout.
2174 Action onReadTimeoutAction = OnReadTimeout;
2176 // The CancellationTokenSource specifies the timeout value and the action to take on a timeout.
2178 var cts = new CancellationTokenSource();
2180 // Cancel the read if it hasn't completed after a timeout.
2182 cts.CancelAfter(readTimeout);
2184 // Specify the function to call on a timeout.
2186 cts.Token.Register(onReadTimeoutAction);
2188 // Stops waiting when data is available or on timeout:
2190 Int32 bytesRead = await _myHid.GetInputReportViaInterruptTransfer(_deviceData, inputReportBuffer, cts);
2192 // Arrive here only if the operation completed.
2194 // Dispose to stop the timeout timer.
2198 _transferInProgress = false;
2199 cmdGetInputReportInterrupt.Enabled = true;
2204 Debug.Print("bytes read (includes report ID) = " + Convert.ToString(bytesRead));
2211 MyMarshalDataToForm(FormActions.AddItemToListBox, "No attempt to read an Input report was made.");
2212 MyMarshalDataToForm(FormActions.AddItemToListBox, "The HID doesn't have an Input report.");
2217 MyMarshalDataToForm(FormActions.AddItemToListBox, "Invalid handle.");
2218 MyMarshalDataToForm(FormActions.AddItemToListBox,
2219 "No attempt to write an Output report or read an Input report was made.");
2224 DisplayReportData(inputReportBuffer, ReportTypes.Input, ReportReadOrWritten.Read);
2228 CloseCommunications();
2229 MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to read an Input report has failed.");
2230 ScrollToBottomOfListBox();
2234 catch (Exception ex)
2236 DisplayException(Name, ex);
2242 /// Sends a Feature report.
2243 /// Assumes report ID = 0.
2246 private void RequestToSendFeatureReport()
2248 String byteValue = null;
2252 _transferInProgress = true;
2254 // If the device hasn't been detected, was removed, or timed out on a previous attempt
2255 // to access it, look for the device.
2257 if (!_deviceHandleObtained)
2259 _deviceHandleObtained = FindTheHid();
2262 if (_deviceHandleObtained)
2266 if ((_myHid.Capabilities.FeatureReportByteLength > 0))
2268 // The HID has a Feature report.
2269 // Set the size of the Feature report buffer.
2271 var outFeatureReportBuffer = new Byte[_myHid.Capabilities.FeatureReportByteLength];
2273 // Store the report ID in the buffer.
2275 outFeatureReportBuffer[0] = 0;
2277 // Store the report data following the report ID.
2278 // Use the data in the combo boxes on the form.
2280 outFeatureReportBuffer[1] = Convert.ToByte(CboByte0.SelectedIndex);
2282 if (outFeatureReportBuffer.GetUpperBound(0) > 1)
2284 outFeatureReportBuffer[2] = Convert.ToByte(CboByte1.SelectedIndex);
2287 // Write a report to the device
2289 Boolean success = _myHid.SendFeatureReport(_hidHandle, outFeatureReportBuffer);
2293 DisplayReportData(outFeatureReportBuffer, ReportTypes.Feature, ReportReadOrWritten.Written);
2297 CloseCommunications();
2298 AccessForm(FormActions.AddItemToListBox, "The attempt to send a Feature report failed.");
2299 ScrollToBottomOfListBox();
2304 AccessForm(FormActions.AddItemToListBox, "The HID doesn't have a Feature report.");
2305 ScrollToBottomOfListBox();
2309 _transferInProgress = false;
2310 cmdSendFeatureReport.Enabled = true;
2311 ScrollToBottomOfListBox();
2314 catch (Exception ex)
2316 DisplayException(Name, ex);
2322 /// Sends an Output report.
2323 /// Assumes report ID = 0.
2326 private async void RequestToSendOutputReport()
2328 const Int32 writeTimeout = 5000;
2329 String byteValue = null;
2333 // If the device hasn't been detected, was removed, or timed out on a previous attempt
2334 // to access it, look for the device.
2336 if (!_deviceHandleObtained)
2338 _deviceHandleObtained = FindTheHid();
2341 if (_deviceHandleObtained)
2345 // Don't attempt to exchange reports if valid handles aren't available
2346 // (as for a mouse or keyboard.)
2348 if (!_hidHandle.IsInvalid)
2350 // Don't attempt to send an Output report if the HID has no Output report.
2352 if (_myHid.Capabilities.OutputReportByteLength > 0)
2354 // Set the size of the Output report buffer.
2356 var outputReportBuffer = new Byte[_myHid.Capabilities.OutputReportByteLength];
2358 // Store the report ID in the first byte of the buffer:
2360 outputReportBuffer[0] = 0;
2362 // Store the report data following the report ID.
2363 // Use the data in the combo boxes on the form.
2365 outputReportBuffer[1] = Convert.ToByte(CboByte0.SelectedIndex);
2367 if (outputReportBuffer.GetUpperBound(0) > 1)
2369 outputReportBuffer[2] = Convert.ToByte(CboByte1.SelectedIndex);
2376 if (_transferType.Equals(TransferTypes.Control))
2379 _transferInProgress = true;
2381 // Use a control transfer to send the report,
2382 // even if the HID has an interrupt OUT endpoint.
2384 success = _myHid.SendOutputReportViaControlTransfer(_hidHandle, outputReportBuffer);
2386 _transferInProgress = false;
2387 cmdSendOutputReportControl.Enabled = true;
2392 Debug.Print("interrupt");
2393 _transferInProgress = true;
2395 // The CancellationTokenSource specifies the timeout value and the action to take on a timeout.
2397 var cts = new CancellationTokenSource();
2399 // Create a delegate to execute on a timeout.
2401 Action onWriteTimeoutAction = OnWriteTimeout;
2403 // Cancel the read if it hasn't completed after a timeout.
2405 cts.CancelAfter(writeTimeout);
2407 // Specify the function to call on a timeout.
2409 cts.Token.Register(onWriteTimeoutAction);
2411 // Send an Output report and wait for completion or timeout.
2413 success = await _myHid.SendOutputReportViaInterruptTransfer(_deviceData, _hidHandle, outputReportBuffer, cts);
2415 // Get here only if the operation completes without a timeout.
2417 _transferInProgress = false;
2418 cmdSendOutputReportInterrupt.Enabled = true;
2420 // Dispose to stop the timeout timer.
2426 DisplayReportData(outputReportBuffer, ReportTypes.Output, ReportReadOrWritten.Written);
2430 CloseCommunications();
2431 AccessForm(FormActions.AddItemToListBox, "The attempt to write an Output report failed.");
2432 ScrollToBottomOfListBox();
2438 AccessForm(FormActions.AddItemToListBox, "The HID doesn't have an Output report.");
2441 catch (Exception ex)
2443 DisplayException(Name, ex);
2449 /// Scroll to the bottom of the list box and trim as needed.
2452 private void ScrollToBottomOfListBox()
2456 LstResults.SelectedIndex = LstResults.Items.Count - 1;
2458 // If the list box is getting too large, trim its contents by removing the earliest data.
2460 if (LstResults.Items.Count > 1000)
2463 for (count = 1; count <= 500; count++)
2465 LstResults.Items.RemoveAt(4);
2469 catch (Exception ex)
2471 DisplayException(Name, ex);
2477 /// Request to send or get a Feature report.
2480 private void SendOrGetFeatureReport()
2484 // If the device hasn't been detected, was removed, or timed out on a previous attempt
2485 // to access it, look for the device.
2487 if (!_deviceHandleObtained)
2489 _deviceHandleObtained = FindTheHid();
2492 if (_deviceHandleObtained)
2496 case SendOrGet.Send:
2497 RequestToSendFeatureReport();
2498 _sendOrGet = SendOrGet.Get;
2501 RequestToGetFeatureReport();
2502 _sendOrGet = SendOrGet.Send;
2507 catch (Exception ex)
2509 DisplayException(Name, ex);
2515 /// Request to send an Output report or get an Input report.
2518 private void SendOutputReportOrGetInputReport()
2522 // If the device hasn't been detected, was removed, or timed out on a previous attempt
2523 // to access it, look for the device.
2525 if (!_deviceHandleObtained)
2527 _deviceHandleObtained = FindTheHid();
2530 if (_deviceHandleObtained)
2532 if (_sendOrGet == SendOrGet.Send)
2534 RequestToSendOutputReport();
2535 _sendOrGet = SendOrGet.Get;
2539 RequestToGetInputReport();
2540 _sendOrGet = SendOrGet.Send;
2544 catch (Exception ex)
2546 DisplayException(Name, ex);
2552 /// Set the number of Input buffers (the number of Input reports
2553 /// the host will store) from the value in the text box.
2556 private void SetInputReportBufferSize()
2560 if (!_transferInProgress)
2562 // Get the number of buffers from the text box.
2564 Int32 numberOfInputBuffers = Convert.ToInt32(txtInputReportBufferSize.Text);
2566 // Set the number of buffers.
2568 _myHid.SetNumberOfInputBuffers(_hidHandle, numberOfInputBuffers);
2570 // Verify and display the result.
2572 GetInputReportBufferSize();
2576 DisplayTransferInProgressMessage();
2579 catch (Exception ex)
2581 DisplayException(Name, ex);
2587 /// Perform actions that must execute when the program ends.
2590 private void Shutdown()
2594 CloseCommunications();
2595 DeviceNotificationsStop();
2597 catch (Exception ex)
2599 DisplayException(Name, ex);
2605 /// Perform actions that must execute when the program starts.
2608 private void Startup()
2610 const Int32 periodicTransferInterval = 1000;
2614 InitializeDisplay();
2616 _periodicTransfers = new System.Timers.Timer(periodicTransferInterval);
2617 _periodicTransfers.Elapsed += DoPeriodicTransfers;
2618 _periodicTransfers.Stop();
2619 _periodicTransfers.SynchronizingObject = this;
2621 // Default USB Vendor ID and Product ID:
2623 txtVendorID.Text = "0925";
2624 txtProductID.Text = "7001";
2626 GetVendorAndProductIDsFromTextBoxes(ref _myVendorId, ref _myProductId);
2628 DeviceNotificationsStart();
2629 FindDeviceUsingWmi();
2632 catch (Exception ex)
2634 DisplayException(Name, ex);
2640 /// The Product ID has changed in the text box. Call a routine to handle it.
2643 private void txtProductID_TextChanged(Object sender, EventArgs e)
2647 //DeviceHasChanged();
2649 catch (Exception ex)
2651 DisplayException(Name, ex);
2657 /// The Vendor ID has changed in the text box. Call a routine to handle it.
2660 private void txtVendorID_TextChanged(Object sender, EventArgs e)
2664 //DeviceHasChanged();
2666 catch (Exception ex)
2668 DisplayException(Name, ex);
2674 /// Provides a central mechanism for exception handling.
2675 /// Displays a message box that describes the exception.
2678 /// <param name="moduleName"> the module where the exception occurred. </param>
2679 /// <param name="e"> the exception </param>
2681 internal static void DisplayException(String moduleName, Exception e)
2683 // Create an error message.
2685 String message = "Exception: " + e.Message + Environment.NewLine + "Module: " + moduleName + Environment.NewLine + "Method: " + e.TargetSite.Name;
2687 const String caption = "Unexpected Exception";
2689 MessageBox.Show(message, caption, MessageBoxButtons.OK);
2690 Debug.Write(message);
2692 // Get the last error and display it.
2694 Int32 error = Marshal.GetLastWin32Error();
2696 Debug.WriteLine("The last Win32 Error was: " + error);
2700 internal static void Main() { Application.Run(new FrmMain()); }
2701 private static FrmMain _transDefaultFormFrmMain;
2702 internal static FrmMain TransDefaultFormFrmMain
2706 if (_transDefaultFormFrmMain == null)
2708 _transDefaultFormFrmMain = new FrmMain();
2710 return _transDefaultFormFrmMain;
2714 private void treeViewDevices_AfterSelect(object sender, TreeViewEventArgs e)
2716 MyMarshalDataToForm(FormActions.AddItemToListBox, "------------------------------------------------------------------------");
2717 //Node selected in our TreeView
2718 //Extract vendor and product IDs
2719 string deviceId = treeViewDevices.SelectedNode.Name;
2720 Regex regex = new Regex(@"VID_(....)&PID_(....).*");
2721 Match match = regex.Match(deviceId);
2724 //Take matches from each capturing group here. match.Groups[n].Value;
2725 //Put vendor and product ID in our text fields.
2726 txtVendorID.Text = match.Groups[1].Value;
2727 txtProductID.Text = match.Groups[2].Value;