FrmMain.cs
author sl
Wed, 14 May 2014 18:54:30 +0200
changeset 6 597a95926637
parent 5 8ea7a7e29c4d
child 7 5680e56e9b0b
permissions -rw-r--r--
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;
     2 using System;
     3 using System.Diagnostics;
     4 using System.Globalization;
     5 using System.IO;
     6 using System.Management;
     7 using System.Runtime.InteropServices;
     8 using System.Threading;
     9 using System.Timers;
    10 using System.Windows.Forms;
    11 using System.Collections.Generic;
    12 using System.Drawing;
    13 using System.Text.RegularExpressions;
    14 
    15 namespace GenericHid
    16 {
    17 	///<summary>
    18 	/// Project: GenericHid
    19 	///
    20 	/// ***********************************************************************
    21 	/// Software License Agreement
    22 	///
    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.
    28 	///
    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
    35 	/// THE SOFTWARE.
    36 	/// ***********************************************************************
    37 	///
    38 	/// Author
    39 	/// Jan Axelson
    40 	///
    41 	/// This software was written using Visual Studio Express 2012 for Windows
    42 	/// Desktop building for the .NET Framework v4.5.
    43 	///
    44 	/// Purpose:
    45 	/// Demonstrates USB communications with a generic HID-class device
    46 	///
    47 	/// Requirements:
    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.)
    50 	///
    51 	/// Description:
    52 	/// Finds an attached device that matches the vendor and product IDs in the form's
    53 	/// text boxes.
    54 	///
    55 	/// Retrieves the device's capabilities.
    56 	/// Sends and requests HID reports.
    57 	///
    58 	/// Uses the System.Management class and Windows Management Instrumentation (WMI) to detect
    59 	/// when a device is attached or removed.
    60 	///
    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.
    63 	///
    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.
    66 	///
    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.
    70 	///
    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.
    74 	///
    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.
    80 	///
    81 	/// This project includes the following modules:
    82 	///
    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.
    92 	///
    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.
    96 	///
    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
   100 	///
   101 	/// V6.2
   102 	/// 11/12/13
   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.
   106 	///
   107 	/// V6.1
   108 	/// 10/28/13
   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.
   112 	///
   113 	/// V6.0
   114 	/// 2/8/13
   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);
   120 	/// and here:
   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/
   128 	///
   129 	/// V5.0
   130 	/// 3/30/11
   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.
   134 	///
   135 	/// V4.6
   136 	/// 1/12/10
   137 	/// Supports Vendor IDs and Product IDs up to FFFFh.
   138 	///
   139 	/// V4.52
   140 	/// 11/10/09
   141 	/// Changed HIDD_ATTRIBUTES to use UInt16
   142 	///
   143 	/// V4.51
   144 	/// 2/11/09
   145 	/// Moved Free_ and similar to Finally blocks to ensure they execute.
   146 	///
   147 	/// V4.5
   148 	/// 2/9/09
   149 	/// Changes to support 64-bit systems, memory management, and other corrections.
   150 	/// Big thanks to Peter Nielsen.
   151 	///
   152 	/// </summary>
   153 
   154 	internal class FrmMain
   155 		: Form
   156 	{
   157 		#region '"Windows Form Designer generated code "'
   158 		public FrmMain()
   159 		//: base()
   160 		{
   161 			// This call is required by the Windows Form Designer.
   162 			InitializeComponent();
   163 		}
   164 		// Form overrides dispose to clean up the component list.
   165 		protected override void Dispose(bool Disposing1)
   166 		{
   167 			if (Disposing1)
   168 			{
   169 				if (components != null)
   170 				{
   171 					components.Dispose();
   172 				}
   173 			}
   174 			base.Dispose(Disposing1);
   175 		}
   176 
   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;
   213 
   214 		[System.Diagnostics.DebuggerStepThrough()]
   215 		private void InitializeComponent()
   216 		{
   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();
   257             //
   258             // FraBytesReceived
   259             //
   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";
   271             //
   272             // TxtBytesReceived
   273             //
   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;
   286             //
   287             // FraBytesToSend
   288             //
   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";
   302             //
   303             // ChkAutoincrement
   304             //
   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;
   316             //
   317             // CboByte1
   318             //
   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;
   329             //
   330             // CboByte0
   331             //
   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;
   342             //
   343             // LstResults
   344             //
   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;
   356             //
   357             // fraInputReportBufferSize
   358             //
   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";
   367             //
   368             // cmdInputReportBufferSize
   369             //
   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);
   376             //
   377             // txtInputReportBufferSize
   378             //
   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;
   383             //
   384             // fraDeviceIdentifiers
   385             //
   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";
   396             //
   397             // txtProductID
   398             //
   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);
   405             //
   406             // lblProductID
   407             //
   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):";
   413             //
   414             // txtVendorID
   415             //
   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);
   422             //
   423             // lblVendorID
   424             //
   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):";
   430             //
   431             // cmdFindDevice
   432             //
   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);
   439             //
   440             // cmdSendOutputReportInterrupt
   441             //
   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);
   449             //
   450             // cmdGetInputReportInterrupt
   451             //
   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);
   459             //
   460             // fraInterruptTransfers
   461             //
   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";
   474             //
   475             // cmdPeriodicTransfers
   476             //
   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);
   484             //
   485             // cmdSendOutputReportControl
   486             //
   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);
   494             //
   495             // cmdGetInputReportControl
   496             //
   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);
   504             //
   505             // fraControlTransfers
   506             //
   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";
   521             //
   522             // cmdGetFeatureReport
   523             //
   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);
   531             //
   532             // cmdSendFeatureReport
   533             //
   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);
   541             //
   542             // fraSendAndGetContinuous
   543             //
   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";
   558             //
   559             // radFeature
   560             //
   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);
   570             //
   571             // radInputOutputControl
   572             //
   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);
   582             //
   583             // radInputOutputInterrupt
   584             //
   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);
   594             //
   595             // treeViewDevices
   596             //
   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);
   602             //
   603             // FrmMain
   604             //
   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);
   635 
   636 		}
   637 		#endregion '"Windows Form Designer generated code "'
   638 
   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;
   656 
   657 		private static System.Timers.Timer _periodicTransfers;
   658 
   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();
   662 
   663 		private enum FormActions
   664 		{
   665 			AddItemToListBox,
   666 			DisableInputReportBufferSize,
   667 			EnableGetInputReportInterruptTransfer,
   668 			EnableInputReportBufferSize,
   669 			EnableSendOutputReportInterrupt,
   670 			ScrollToBottomOfListBox,
   671 			SetInputReportBufferSize,
   672             AddDeviceToTreeView,
   673             ResetDeviceTreeView,
   674             SelectDeviceInTreeView,
   675             CompleteDeviceTreeView
   676 		}
   677 
   678 		private enum ReportReadOrWritten
   679 		{
   680 			Read,
   681 			Written
   682 		}
   683 
   684 		private enum ReportTypes
   685 		{
   686 			Input,
   687 			Output,
   688 			Feature
   689 		}
   690 
   691 		private enum SendOrGet
   692 		{
   693 			Send,
   694 			Get
   695 		}
   696 
   697 		private enum TransferTypes
   698 		{
   699 			Control,
   700 			Interrupt
   701 		}
   702 
   703 		private enum WmiDeviceProperties
   704 		{
   705 			Name,
   706 			Caption,
   707 			Description,
   708 			Manufacturer,
   709 			PNPDeviceID,
   710 			DeviceID,
   711 			ClassGUID
   712 		}
   713 
   714 		internal FrmMain FrmMy;
   715 
   716 		//  This delegate has the same parameters as AccessForm.
   717 		//  Used in accessing the application's form from a different thread.
   718 
   719         private delegate void MarshalDataToForm(FormActions action, params string[] strings);
   720 
   721 		///  <summary>
   722 		///  Performs various application-specific functions that
   723 		///  involve accessing the application's form.
   724 		///  </summary>
   725 		///
   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>
   729 
   730 		private void AccessForm(FormActions action, params string[] strings)
   731 		{
   732 			try
   733 			{
   734 				//  Select an action to perform on the form:
   735 
   736 				switch (action)
   737 				{
   738 					case FormActions.AddItemToListBox:
   739 
   740                         LstResults.Items.Add(strings[0]);
   741 						break;
   742 
   743 					case FormActions.DisableInputReportBufferSize:
   744 
   745 						cmdInputReportBufferSize.Enabled = false;
   746 						break;
   747 
   748 					case FormActions.EnableGetInputReportInterruptTransfer:
   749 
   750 						cmdGetInputReportInterrupt.Enabled = true;
   751 						break;
   752 
   753 					case FormActions.EnableInputReportBufferSize:
   754 
   755 						cmdInputReportBufferSize.Enabled = true;
   756 						break;
   757 
   758 					case FormActions.EnableSendOutputReportInterrupt:
   759 
   760 						cmdSendOutputReportInterrupt.Enabled = true;
   761 						break;
   762 
   763 					case FormActions.ScrollToBottomOfListBox:
   764 
   765 						LstResults.SelectedIndex = LstResults.Items.Count - 1;
   766 						break;
   767 
   768 					case FormActions.SetInputReportBufferSize:
   769 
   770                         txtInputReportBufferSize.Text = strings[0];
   771 						break;
   772 
   773                     case FormActions.AddDeviceToTreeView:
   774                         {
   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)
   778                             {
   779                                 if (device.ForeColor == Color.Red)
   780                                 {
   781                                     //Device was removed and has now been added back
   782                                     device.ForeColor = Color.Green;
   783                                 }
   784                                 else
   785                                 {
   786                                     //Device was already there set back our device color to black
   787                                     device.ForeColor = Color.Black;
   788                                 }
   789                             }
   790 
   791                             if (res.Length > 0)
   792                             {
   793                                 //Our device is already there
   794                                 break;
   795                             }
   796 
   797                             //Build our node from our string array
   798                             TreeNode newNode = new TreeNode(strings[0]);
   799                             for (int i = 1; i < strings.Length; i++)
   800                             {
   801                                 newNode.Nodes.Add(strings[i]);
   802                                 if (strings[i].StartsWith("Name: "))
   803                                 {
   804                                     //Found our name property, update our node text
   805                                     newNode.Text = strings[i].Substring(6, strings[i].Length - 6);
   806                                 }
   807                             }
   808 
   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);
   813                         }
   814                         break;
   815 
   816                     case FormActions.ResetDeviceTreeView:
   817                         {
   818                             //Mark all non removed/red device as purple/unknown
   819                             foreach (TreeNode device in treeViewDevices.Nodes)
   820                             {
   821                                 if (device.ForeColor != Color.Red)
   822                                 {
   823                                     device.ForeColor = Color.Purple;
   824                                 }
   825                             }
   826                             //treeViewDevices.Nodes.Clear();
   827                         }
   828                         break;
   829 
   830                     case FormActions.CompleteDeviceTreeView:
   831                         {
   832                             //Our device list is now complete
   833                             foreach (TreeNode device in treeViewDevices.Nodes)
   834                             {
   835                                 //Purple devices need to be marked as red for removed
   836                                 if (device.ForeColor == Color.Purple)
   837                                 {
   838                                     device.ForeColor = Color.Red;
   839                                 }
   840                             }
   841                         }
   842                         break;
   843 
   844                     case FormActions.SelectDeviceInTreeView:
   845                         {
   846                             //treeViewDevices.SelectedNode = null;
   847                             TreeNode[] res = treeViewDevices.Nodes.Find(strings[0], false);
   848                             foreach (TreeNode device in res)
   849                             {
   850                                 device.ForeColor = Color.Blue;
   851                                 //treeViewDevices.SelectedNode = res[0];
   852                                 //treeViewDevices.SelectedNode.ForeColor = Color.Blue;
   853                             }
   854                         }
   855 
   856 
   857                         break;
   858 				}
   859 			}
   860 			catch (Exception ex)
   861 			{
   862 				DisplayException(Name, ex);
   863 				throw;
   864 			}
   865 		}
   866 
   867 		///  <summary>
   868 		///  Add a handler to detect arrival of devices using WMI.
   869 		///  </summary>
   870 
   871 		private void AddDeviceArrivedHandler()
   872 		{
   873 			const Int32 pollingIntervalSeconds = 3;
   874 			var scope = new ManagementScope("root\\CIMV2");
   875 			scope.Options.EnablePrivileges = true;
   876 
   877 			try
   878 			{
   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;
   885 
   886 				_deviceArrivedWatcher.Start();
   887 			}
   888 			catch (Exception e)
   889 			{
   890 				Debug.WriteLine(e.Message);
   891 				if (_deviceArrivedWatcher != null)
   892 					_deviceArrivedWatcher.Stop();
   893 			}
   894 		}
   895 
   896 		///  <summary>
   897 		///  Add a handler to detect removal of devices using WMI.
   898 		///  </summary>
   899 
   900 		private void AddDeviceRemovedHandler()
   901 		{
   902 			const Int32 pollingIntervalSeconds = 3;
   903 			var scope = new ManagementScope("root\\CIMV2");
   904 			scope.Options.EnablePrivileges = true;
   905 
   906 			try
   907 			{
   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();
   915 			}
   916 			catch (Exception e)
   917 			{
   918 				Debug.WriteLine(e.Message);
   919 				if (_deviceRemovedWatcher != null)
   920 					_deviceRemovedWatcher.Stop();
   921 			}
   922 		}
   923 
   924 		/// <summary>
   925 		/// Close the handle and FileStreams for a device.
   926 		/// </summary>
   927 		///
   928 		private void CloseCommunications()
   929 		{
   930 			if (_deviceData != null)
   931 			{
   932 				_deviceData.Close();
   933 			}
   934 
   935 			if ((_hidHandle != null) && (!(_hidHandle.IsInvalid)))
   936 			{
   937 				_hidHandle.Close();
   938 			}
   939 
   940 			// The next attempt to communicate will get a new handle and FileStreams.
   941 
   942 			_deviceHandleObtained = false;
   943 		}
   944 
   945 		///  <summary>
   946 		///  Search for a specific device.
   947 		///  </summary>
   948 
   949 		private void cmdFindDevice_Click(Object sender, EventArgs e)
   950 		{
   951 			try
   952 			{
   953 				if (_transferInProgress)
   954 				{
   955 					DisplayTransferInProgressMessage();
   956 				}
   957 				else
   958 				{
   959 					_deviceDetected = FindDeviceUsingWmi();
   960 					if (_deviceDetected)
   961 					{
   962 						FindTheHid();
   963 					}
   964 				}
   965 			}
   966 			catch (Exception ex)
   967 			{
   968 				DisplayException(Name, ex);
   969 				throw;
   970 			}
   971 		}
   972 
   973 		/// <summary>
   974 		/// Request to get a Feature report from the device.
   975 		/// </summary>
   976 		/// <param name="sender"></param>
   977 		/// <param name="e"></param>
   978 
   979 		private void cmdGetFeatureReport_Click(object sender, EventArgs e)
   980 		{
   981 			try
   982 			{
   983 				if (_transferInProgress)
   984 				{
   985 					DisplayTransferInProgressMessage();
   986 				}
   987 				else
   988 				{
   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.
   992 
   993 					fraControlTransfers.Focus();
   994 					cmdGetFeatureReport.Enabled = false;
   995 					_transferType = TransferTypes.Control;
   996 					RequestToGetFeatureReport();
   997 				}
   998 			}
   999 			catch (Exception ex)
  1000 			{
  1001 				DisplayException(Name, ex);
  1002 				throw;
  1003 			}
  1004 		}
  1005 
  1006 		/// <summary>
  1007 		/// Request to get an Input report from the device using a control transfer.
  1008 		/// </summary>
  1009 		/// <param name="sender"></param>
  1010 		/// <param name="e"></param>
  1011 
  1012 		private void cmdGetInputReportControl_Click(object sender, EventArgs e)
  1013 		{
  1014 			try
  1015 			{
  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.
  1019 
  1020 				if (_transferInProgress)
  1021 				{
  1022 					DisplayTransferInProgressMessage();
  1023 				}
  1024 				else
  1025 				{
  1026 					fraControlTransfers.Focus();
  1027 					cmdGetInputReportControl.Enabled = false;
  1028 					_transferType = TransferTypes.Control;
  1029 					RequestToGetInputReport();
  1030 				}
  1031 			}
  1032 			catch (Exception ex)
  1033 			{
  1034 				DisplayException(Name, ex);
  1035 				throw;
  1036 			}
  1037 		}
  1038 
  1039 		/// <summary>
  1040 		/// Request to get an Input report retrieved using interrupt transfers.
  1041 		/// </summary>
  1042 		/// <param name="sender"></param>
  1043 		/// <param name="e"></param>
  1044 		///
  1045 		private void cmdGetInputReportInterrupt_Click(object sender, EventArgs e)
  1046 		{
  1047 			try
  1048 			{
  1049 				if (_transferInProgress)
  1050 				{
  1051 					DisplayTransferInProgressMessage();
  1052 				}
  1053 				else
  1054 				{
  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.
  1058 
  1059 					fraInterruptTransfers.Focus();
  1060 					cmdGetInputReportInterrupt.Enabled = false;
  1061 					_transferType = TransferTypes.Interrupt;
  1062 					RequestToGetInputReport();
  1063 				}
  1064 			}
  1065 			catch (Exception ex)
  1066 			{
  1067 				DisplayException(Name, ex);
  1068 				throw;
  1069 			}
  1070 		}
  1071 
  1072 		///  <summary>
  1073 		///  Set the number of Input reports the HID driver will store.
  1074 		///  </summary>
  1075 
  1076 		private void cmdInputReportBufferSize_Click(Object sender, EventArgs e)
  1077 		{
  1078 			try
  1079 			{
  1080 				if (_transferInProgress)
  1081 				{
  1082 					DisplayTransferInProgressMessage();
  1083 				}
  1084 				else
  1085 				{
  1086 					SetInputReportBufferSize();
  1087 				}
  1088 			}
  1089 			catch
  1090 				(Exception ex)
  1091 			{
  1092 				DisplayException(Name, ex);
  1093 				throw;
  1094 			}
  1095 		}
  1096 
  1097 		/// <summary>
  1098 		/// Alternate sending and getting a report.
  1099 		/// </summary>
  1100 		/// <param name="sender"></param>
  1101 		/// <param name="e"></param>
  1102 
  1103 		private void cmdPeriodicTransfers_Click(object sender, EventArgs e)
  1104 		{
  1105 			try
  1106 			{
  1107 				if (cmdPeriodicTransfers.Text == "Start")
  1108 				{
  1109 					if (_transferInProgress)
  1110 					{
  1111 						DisplayTransferInProgressMessage();
  1112 					}
  1113 					else
  1114 					{
  1115 						_sendOrGet = SendOrGet.Send;
  1116 						PeriodicTransfersStart();
  1117 					}
  1118 				}
  1119 				else
  1120 				{
  1121 					PeriodicTransfersStop();
  1122 				}
  1123 			}
  1124 			catch (Exception ex)
  1125 			{
  1126 				DisplayException(Name, ex);
  1127 				throw;
  1128 			}
  1129 		}
  1130 
  1131 		/// <summary>
  1132 		/// Request to send a Feature report using a control transfer.
  1133 		/// </summary>
  1134 		/// <param name="sender"></param>
  1135 		/// <param name="e"></param>
  1136 
  1137 		private void cmdSendFeatureReport_Click(object sender, EventArgs e)
  1138 		{
  1139 			try
  1140 			{
  1141 				if (_transferInProgress)
  1142 				{
  1143 					DisplayTransferInProgressMessage();
  1144 				}
  1145 				else
  1146 				{
  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.
  1150 
  1151 					fraControlTransfers.Focus();
  1152 					cmdSendFeatureReport.Enabled = false;
  1153 					_transferType = TransferTypes.Control;
  1154 					RequestToSendFeatureReport();
  1155 				}
  1156 			}
  1157 			catch (Exception ex)
  1158 			{
  1159 				DisplayException(Name, ex);
  1160 				throw;
  1161 			}
  1162 		}
  1163 
  1164 		/// <summary>
  1165 		/// Request to send an Output report using a control transfer.
  1166 		/// </summary>
  1167 		/// <param name="sender"></param>
  1168 		/// <param name="e"></param>
  1169 		///
  1170 		private void cmdSendOutputReportControl_Click(object sender, EventArgs e)
  1171 		{
  1172 			try
  1173 			{
  1174 				if (_transferInProgress)
  1175 				{
  1176 					DisplayTransferInProgressMessage();
  1177 				}
  1178 				else
  1179 				{
  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.
  1183 
  1184 					fraControlTransfers.Focus();
  1185 					cmdSendOutputReportControl.Enabled = false;
  1186 					_transferType = TransferTypes.Control;
  1187 					RequestToSendOutputReport();
  1188 				}
  1189 			}
  1190 			catch (Exception ex)
  1191 			{
  1192 				DisplayException(Name, ex);
  1193 				throw;
  1194 			}
  1195 		}
  1196 
  1197 		/// <summary>
  1198 		/// Request to send an Output report using an interrupt transfer.
  1199 		/// </summary>
  1200 		/// <param name="sender"></param>
  1201 		/// <param name="e"></param>
  1202 
  1203 		private void cmdSendOutputReportInterrupt_Click(object sender, EventArgs e)
  1204 		{
  1205 			try
  1206 			{
  1207 				if (_transferInProgress)
  1208 				{
  1209 					DisplayTransferInProgressMessage();
  1210 				}
  1211 				else
  1212 				{
  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.
  1216 
  1217 					fraInterruptTransfers.Focus();
  1218 					cmdSendOutputReportInterrupt.Enabled = false;
  1219 					_transferType = TransferTypes.Interrupt;
  1220 					RequestToSendOutputReport();
  1221 				}
  1222 			}
  1223 			catch (Exception ex)
  1224 			{
  1225 				DisplayException(Name, ex);
  1226 				throw;
  1227 			}
  1228 		}
  1229 
  1230 		///  <summary>
  1231 		///  Called on arrival of any device.
  1232 		///  Calls a routine that searches to see if the desired device is present.
  1233 		///  </summary>
  1234 
  1235 		private void DeviceAdded(object sender, EventArrivedEventArgs e)
  1236 		{
  1237 			try
  1238 			{
  1239 				Debug.WriteLine("A USB device has been inserted");
  1240 
  1241 				_deviceDetected = FindDeviceUsingWmi();
  1242 			}
  1243 			catch (Exception ex)
  1244 			{
  1245 				DisplayException(Name, ex);
  1246 				throw;
  1247 			}
  1248 		}
  1249 
  1250 		///  <summary>
  1251 		///  Called if the user changes the Vendor ID or Product ID in the text box.
  1252 		///  </summary>
  1253 
  1254 		private void DeviceHasChanged()
  1255 		{
  1256 			try
  1257 			{
  1258 				//  If a device was previously detected, stop receiving notifications about it.
  1259 
  1260 				if (_deviceHandleObtained)
  1261 				{
  1262 					DeviceNotificationsStop();
  1263 
  1264 					CloseCommunications();
  1265 				}
  1266 				// Look for a device that matches the Vendor ID and Product ID in the text boxes.
  1267 
  1268 				FindTheHid();
  1269 			}
  1270 			catch (Exception ex)
  1271 			{
  1272 				DisplayException(Name, ex);
  1273 				throw;
  1274 			}
  1275 		}
  1276 
  1277 		///  <summary>
  1278 		///  Add handlers to detect device arrival and removal.
  1279 		///  </summary>
  1280 
  1281 		private void DeviceNotificationsStart()
  1282 		{
  1283 			AddDeviceArrivedHandler();
  1284 			AddDeviceRemovedHandler();
  1285 		}
  1286 
  1287 		///  <summary>
  1288 		///  Stop receiving notifications about device arrival and removal
  1289 		///  </summary>
  1290 
  1291 		private void DeviceNotificationsStop()
  1292 		{
  1293 			try
  1294 			{
  1295 				if (_deviceArrivedWatcher != null)
  1296 					_deviceArrivedWatcher.Stop();
  1297 				if (_deviceRemovedWatcher != null)
  1298 					_deviceRemovedWatcher.Stop();
  1299 			}
  1300 			catch (Exception ex)
  1301 			{
  1302 				DisplayException(Name, ex);
  1303 				throw;
  1304 			}
  1305 		}
  1306 
  1307 		///  <summary>
  1308 		///  Called on removal of any device.
  1309 		///  Calls a routine that searches to see if the desired device is still present.
  1310 		///  </summary>
  1311 		///
  1312 		private void DeviceRemoved(object sender, EventArgs e)
  1313 		{
  1314 			try
  1315 			{
  1316 				Debug.WriteLine("A USB device has been removed");
  1317 
  1318 				_deviceDetected = FindDeviceUsingWmi();
  1319 
  1320 				if (!_deviceDetected)
  1321 				{
  1322 					_deviceHandleObtained = false;
  1323 					CloseCommunications();
  1324 				}
  1325 			}
  1326 			catch (Exception ex)
  1327 			{
  1328 				DisplayException(Name, ex);
  1329 				throw;
  1330 			}
  1331 		}
  1332 
  1333 		///  <summary>
  1334 		///  Displays received or written report data.
  1335 		///  </summary>
  1336 		///
  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>
  1340 
  1341 		private void DisplayReportData(Byte[] buffer, ReportTypes currentReportType, ReportReadOrWritten currentReadOrWritten)
  1342 		{
  1343 			try
  1344 			{
  1345 				Int32 count;
  1346 
  1347 				LstResults.Items.Add(currentReportType.ToString() + " report has been " + currentReadOrWritten.ToString().ToLower() + ".");
  1348 
  1349 				//  Display the report data received in the form's list box.
  1350 
  1351 				LstResults.Items.Add(" Report ID: " + String.Format("{0:X2} ", buffer[0]));
  1352 				LstResults.Items.Add(" Report Data:");
  1353 
  1354 				TxtBytesReceived.Text = "";
  1355 
  1356 				for (count = 1; count <= buffer.Length - 1; count++)
  1357 				{
  1358 					//  Display bytes as 2-character Hex strings.
  1359 
  1360 					String byteValue = String.Format("{0:X2} ", buffer[count]);
  1361 
  1362 					LstResults.Items.Add(" " + byteValue);
  1363 
  1364 					//  Display the received bytes in the text box.
  1365 
  1366 					TxtBytesReceived.SelectionStart = TxtBytesReceived.Text.Length;
  1367 					TxtBytesReceived.SelectedText = byteValue + Environment.NewLine;
  1368 				}
  1369 				ScrollToBottomOfListBox();
  1370 			}
  1371 			catch (Exception ex)
  1372 			{
  1373 				DisplayException(Name, ex);
  1374 				throw;
  1375 			}
  1376 		}
  1377 
  1378 		///  <summary>
  1379 		///  Display a message if the user clicks a button when a transfer is in progress.
  1380 		///  </summary>
  1381 		///
  1382 		private void DisplayTransferInProgressMessage()
  1383 		{
  1384 			AccessForm(FormActions.AddItemToListBox, "Command not executed because a transfer is in progress.");
  1385 			ScrollToBottomOfListBox();
  1386 		}
  1387 
  1388 		///  <summary>
  1389 		///  Do periodic transfers.
  1390 		///  </summary>
  1391 		/// <param name="source"></param>
  1392 		/// <param name="e"></param>
  1393 		///  <remarks>
  1394 		///  The timer is enabled only if continuous (periodic) transfers have been requested.
  1395 		///  </remarks>
  1396 
  1397 		private void DoPeriodicTransfers(object source, ElapsedEventArgs e)
  1398 		{
  1399 			try
  1400 			{
  1401 				PeriodicTransfers();
  1402 			}
  1403 			catch (Exception ex)
  1404 			{
  1405 				DisplayException(Name, ex);
  1406 				throw;
  1407 			}
  1408 		}
  1409 
  1410 		/// <summary>
  1411 		/// Enable the command buttons on the form.
  1412 		/// Needed after attempting a transfer and device not found.
  1413 		/// </summary>
  1414 		///
  1415 		private void EnableFormControls()
  1416 		{
  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;
  1424 		}
  1425 
  1426 		///  <summary>
  1427 		///  Use the System.Management class to find a device by Vendor ID and Product ID using WMI. If found, display device properties.
  1428 		///  </summary>
  1429 		/// <remarks>
  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.
  1433 		/// </remarks>
  1434 
  1435 		private Boolean FindDeviceUsingWmi()
  1436 		{
  1437 			try
  1438 			{
  1439                 MyMarshalDataToForm(FormActions.ResetDeviceTreeView);
  1440 				// Prepend "@" to string below to treat backslash as a normal character (not escape character):
  1441 
  1442 				String deviceIdString = @"USB\VID_" + _myVendorId.ToString("X4") + "&PID_" + _myProductId.ToString("X4");
  1443 
  1444 				_deviceDetected = false;
  1445 				var searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_PnPEntity");
  1446                 int usbDeviceCounter = 0;
  1447 
  1448 				foreach (ManagementObject queryObj in searcher.Get())
  1449 				{
  1450                     string deviceId = queryObj["PNPDeviceID"].ToString();
  1451                     if (deviceId.Contains(deviceIdString))
  1452                     {
  1453                         _deviceDetected = true;
  1454                         MyMarshalDataToForm(FormActions.AddItemToListBox, "My device found (WMI):");
  1455 
  1456                         MyMarshalDataToForm(FormActions.AddItemToListBox, "--------");
  1457                         List<string> device = new List<string>();
  1458                         device.Add(deviceId);
  1459                         usbDeviceCounter++;
  1460                         // Display device properties.
  1461                         foreach (WmiDeviceProperties wmiDeviceProperty in Enum.GetValues(typeof(WmiDeviceProperties)))
  1462                         {
  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()]);
  1466                         }
  1467                         MyMarshalDataToForm(FormActions.AddItemToListBox, "--------");
  1468                         MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
  1469 
  1470                         MyMarshalDataToForm(FormActions.AddDeviceToTreeView, device.ToArray());
  1471                         MyMarshalDataToForm(FormActions.SelectDeviceInTreeView, deviceId);
  1472 
  1473                     }
  1474                     else if (deviceId.StartsWith("USB\\VID"))
  1475                     {
  1476                         List<string> device = new List<string>();
  1477                         device.Add(deviceId);
  1478                         usbDeviceCounter++;
  1479                         // Add device properties.
  1480                         foreach (WmiDeviceProperties wmiDeviceProperty in Enum.GetValues(typeof(WmiDeviceProperties)))
  1481                         {
  1482                             device.Add((wmiDeviceProperty.ToString() + ": " + queryObj[wmiDeviceProperty.ToString()]));
  1483                             Debug.WriteLine(wmiDeviceProperty.ToString() + ": {0}", queryObj[wmiDeviceProperty.ToString()]);
  1484                         }
  1485 
  1486                         MyMarshalDataToForm(FormActions.AddDeviceToTreeView, device.ToArray());
  1487                     }
  1488 				}
  1489 
  1490                 //Complete our device TreeView
  1491                 MyMarshalDataToForm(FormActions.CompleteDeviceTreeView);
  1492 
  1493 
  1494                 MyMarshalDataToForm(FormActions.AddItemToListBox, "Found " + usbDeviceCounter /*searcher.Get().Count*/ + " USB HID devices");
  1495 
  1496 				if (!_deviceDetected)
  1497 				{
  1498 					MyMarshalDataToForm(FormActions.AddItemToListBox, "My device not found (WMI)");
  1499 					MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
  1500 				}
  1501 				return _deviceDetected;
  1502 			}
  1503 			catch (Exception ex)
  1504 			{
  1505 				DisplayException(Name, ex);
  1506 				throw;
  1507 			}
  1508 		}
  1509 
  1510 		///  <summary>
  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.
  1513 		///  </summary>
  1514 		///
  1515 		///  <returns>
  1516 		///   True if the device is detected, False if not detected.
  1517 		///  </returns>
  1518 
  1519 		private Boolean FindTheHid()
  1520 		{
  1521 			var devicePathName = new String[128];
  1522 			String myDevicePathName = "";
  1523 
  1524 			try
  1525 			{
  1526 				_deviceHandleObtained = false;
  1527 				CloseCommunications();
  1528 
  1529 				//  Get the device's Vendor ID and Product ID from the form's text boxes.
  1530 
  1531 				GetVendorAndProductIDsFromTextBoxes(ref _myVendorId, ref _myProductId);
  1532 
  1533 				// Get the HID-class GUID.
  1534 
  1535 				Guid hidGuid = _myHid.GetHidGuid();
  1536 
  1537 				String functionName = "GetHidGuid";
  1538 				Debug.WriteLine(_myDebugging.ResultOfApiCall(functionName));
  1539 				Debug.WriteLine("  GUID for system HIDs: " + hidGuid.ToString());
  1540 
  1541 				//  Fill an array with the device path names of all attached HIDs.
  1542 
  1543 				Boolean availableHids = _myDeviceManagement.FindDeviceFromGuid(hidGuid, ref devicePathName);
  1544 
  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.
  1547 
  1548 				if (availableHids)
  1549 				{
  1550 					Int32 memberIndex = 0;
  1551 
  1552 					do
  1553 					{
  1554 						// Open the handle without read/write access to enable getting information about any HID, even system keyboards and mice.
  1555 
  1556 						_hidHandle = _myHid.OpenHandle(devicePathName[memberIndex], false);
  1557 
  1558 						functionName = "CreateFile";
  1559 						Debug.WriteLine(_myDebugging.ResultOfApiCall(functionName));
  1560 						Debug.WriteLine("  Returned handle: " + _hidHandle);
  1561 
  1562 						if (!_hidHandle.IsInvalid)
  1563 						{
  1564 							// The returned handle is valid,
  1565 							// so find out if this is the device we're looking for.
  1566 
  1567 							_myHid.DeviceAttributes.Size = Marshal.SizeOf(_myHid.DeviceAttributes);
  1568 
  1569 							Boolean success = _myHid.GetAttributes(_hidHandle, ref _myHid.DeviceAttributes);
  1570 
  1571 							if (success)
  1572 							{
  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));
  1578 
  1579 								if ((_myHid.DeviceAttributes.VendorID == _myVendorId) && (_myHid.DeviceAttributes.ProductID == _myProductId))
  1580 								{
  1581 									Debug.WriteLine("  Handle obtained to my device");
  1582 
  1583 									//  Display the information in form's list box.
  1584 
  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, "");
  1589 
  1590 									_deviceHandleObtained = true;
  1591 
  1592 									myDevicePathName = devicePathName[memberIndex];
  1593 								}
  1594 								else
  1595 								{
  1596 									//  It's not a match, so close the handle.
  1597 
  1598 									_deviceHandleObtained = false;
  1599 									_hidHandle.Close();
  1600 								}
  1601 							}
  1602 							else
  1603 							{
  1604 								//  There was a problem retrieving the information.
  1605 
  1606 								Debug.WriteLine("  Error in filling HIDD_ATTRIBUTES structure.");
  1607 								_deviceHandleObtained = false;
  1608 								_hidHandle.Close();
  1609 							}
  1610 						}
  1611 
  1612 						//  Keep looking until we find the device or there are no devices left to examine.
  1613 
  1614 						memberIndex = memberIndex + 1;
  1615 					}
  1616 					while (!((_deviceHandleObtained || (memberIndex == devicePathName.Length))));
  1617 				}
  1618 
  1619 				if (_deviceHandleObtained)
  1620 				{
  1621 					//  The device was detected.
  1622 					//  Learn the capabilities of the device.
  1623 
  1624 					_myHid.Capabilities = _myHid.GetDeviceCapabilities(_hidHandle);
  1625 
  1626 					//  Find out if the device is a system mouse or keyboard.
  1627 
  1628 					_hidUsage = _myHid.GetHidUsage(_myHid.Capabilities);
  1629 
  1630 					//  Get the Input report buffer size.
  1631 
  1632 					GetInputReportBufferSize();
  1633 					MyMarshalDataToForm(FormActions.EnableInputReportBufferSize, "");
  1634 
  1635 					//Close the handle and reopen it with read/write access.
  1636 
  1637 					_hidHandle.Close();
  1638 
  1639 					_hidHandle = _myHid.OpenHandle(myDevicePathName, true);
  1640 
  1641 					if (_hidHandle.IsInvalid)
  1642 					{
  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, "");
  1647 					}
  1648 					else
  1649 					{
  1650 						if (_myHid.Capabilities.InputReportByteLength > 0)
  1651 						{
  1652 							//  Set the size of the Input report buffer.
  1653 
  1654 							var inputReportBuffer = new Byte[_myHid.Capabilities.InputReportByteLength];
  1655 
  1656 							_deviceData = new FileStream(_hidHandle, FileAccess.Read | FileAccess.Write, inputReportBuffer.Length, false);
  1657 						}
  1658 
  1659 						if (_myHid.Capabilities.OutputReportByteLength > 0)
  1660 						{
  1661 							Byte[] outputReportBuffer = null;
  1662 						}
  1663 						//  Flush any waiting reports in the input buffer. (optional)
  1664 
  1665 						_myHid.FlushQueue(_hidHandle);
  1666 					}
  1667 				}
  1668 				else
  1669 				{
  1670 					MyMarshalDataToForm(FormActions.AddItemToListBox, "Device not found.");
  1671 					MyMarshalDataToForm(FormActions.DisableInputReportBufferSize, "");
  1672 					EnableFormControls();
  1673 					MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
  1674 				}
  1675 				return _deviceHandleObtained;
  1676 			}
  1677 			catch (Exception ex)
  1678 			{
  1679 				DisplayException(Name, ex);
  1680 				throw;
  1681 			}
  1682 		}
  1683 
  1684 		///  <summary>
  1685 		///  Perform shutdown operations.
  1686 		///  </summary>
  1687 
  1688 		private void frmMain_Closed(Object eventSender, EventArgs eventArgs)
  1689 		{
  1690 			try
  1691 			{
  1692 				Shutdown();
  1693 			}
  1694 			catch (Exception ex)
  1695 			{
  1696 				DisplayException(Name, ex);
  1697 				throw;
  1698 			}
  1699 		}
  1700 
  1701 		///  <summary>
  1702 		///  Perform startup operations.
  1703 		///  </summary>
  1704 
  1705 		private void frmMain_Load(Object eventSender, EventArgs eventArgs)
  1706 		{
  1707 			try
  1708 			{
  1709 				FrmMy = this;
  1710 				Startup();
  1711 			}
  1712 			catch (Exception ex)
  1713 			{
  1714 				DisplayException(Name, ex);
  1715 				throw;
  1716 			}
  1717 		}
  1718 
  1719 		private void GetBytesToSend()
  1720 		{
  1721 			try
  1722 			{
  1723 				//  Get the bytes to send in a report from the combo boxes.
  1724 				//  Increment the values if the autoincrement check box is selected.
  1725 
  1726 				if (ChkAutoincrement.Checked)
  1727 				{
  1728 					if (CboByte0.SelectedIndex < 255)
  1729 					{
  1730 						CboByte0.SelectedIndex = CboByte0.SelectedIndex + 1;
  1731 					}
  1732 					else
  1733 					{
  1734 						CboByte0.SelectedIndex = 0;
  1735 					}
  1736 					if (CboByte1.SelectedIndex < 255)
  1737 					{
  1738 						CboByte1.SelectedIndex = CboByte1.SelectedIndex + 1;
  1739 					}
  1740 					else
  1741 					{
  1742 						CboByte1.SelectedIndex = 0;
  1743 					}
  1744 				}
  1745 			}
  1746 			catch (Exception ex)
  1747 			{
  1748 				DisplayException(Name, ex);
  1749 				throw;
  1750 			}
  1751 		}
  1752 
  1753 		///  <summary>
  1754 		///  Find and display the number of Input buffers
  1755 		///  (the number of Input reports the HID driver will store).
  1756 		///  </summary>
  1757 
  1758 		private void GetInputReportBufferSize()
  1759 		{
  1760 			Int32 numberOfInputBuffers = 0;
  1761 			Boolean success;
  1762 
  1763 			try
  1764 			{
  1765 				//  Get the number of input buffers.
  1766 
  1767 				_myHid.GetNumberOfInputBuffers(_hidHandle, ref numberOfInputBuffers);
  1768 
  1769 				//  Display the result in the text box.
  1770 
  1771 				MyMarshalDataToForm(FormActions.SetInputReportBufferSize, Convert.ToString(numberOfInputBuffers));
  1772 			}
  1773 			catch (Exception ex)
  1774 			{
  1775 				DisplayException(Name, ex);
  1776 				throw;
  1777 			}
  1778 		}
  1779 
  1780 		///  <summary>
  1781 		///  Retrieve a Vendor ID and Product ID in hexadecimal
  1782 		///  from the form's text boxes and convert the text to Int32s.
  1783 		///  </summary>
  1784 		///
  1785 		///  <param name="myVendorId"> the Vendor ID</param>
  1786 		///  <param name="myProductId"> the Product ID</param>
  1787 
  1788 		private void GetVendorAndProductIDsFromTextBoxes(ref Int32 myVendorId, ref Int32 myProductId)
  1789 		{
  1790 			try
  1791 			{
  1792 				myVendorId = Int32.Parse(txtVendorID.Text, NumberStyles.AllowHexSpecifier);
  1793 				myProductId = Int32.Parse(txtProductID.Text, NumberStyles.AllowHexSpecifier);
  1794 			}
  1795 			catch (Exception ex)
  1796 			{
  1797 				DisplayException(Name, ex);
  1798 				throw;
  1799 			}
  1800 		}
  1801 
  1802 		///  <summary>
  1803 		///  Initialize the elements on the form.
  1804 		///  </summary>
  1805 
  1806 		private void InitializeDisplay()
  1807 		{
  1808 			try
  1809 			{
  1810 				//  Create a dropdown list box for each byte to send in a report.
  1811 				//  Display the values as 2-character hex strings.
  1812 
  1813 				Int16 count;
  1814 				for (count = 0; count <= 255; count++)
  1815 				{
  1816 					String byteValue = String.Format("{0:X2} ", count);
  1817 					FrmMy.CboByte0.Items.Insert(count, byteValue);
  1818 					FrmMy.CboByte1.Items.Insert(count, byteValue);
  1819 				}
  1820 
  1821 				//  Select a default value for each box
  1822 
  1823 				FrmMy.CboByte0.SelectedIndex = 0;
  1824 				FrmMy.CboByte1.SelectedIndex = 128;
  1825 				FrmMy.radInputOutputInterrupt.Checked = true;
  1826 
  1827 				//  Check the autoincrement box to increment the values each time a report is sent.
  1828 
  1829 				ChkAutoincrement.CheckState = CheckState.Checked;
  1830 
  1831 				//  Don't allow the user to select an input report buffer size until there is
  1832 				//  a handle to a HID.
  1833 
  1834 				cmdInputReportBufferSize.Focus();
  1835 				cmdInputReportBufferSize.Enabled = false;
  1836 
  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("");
  1841 			}
  1842 			catch (Exception ex)
  1843 			{
  1844 				DisplayException(Name, ex);
  1845 				throw;
  1846 			}
  1847 		}
  1848 
  1849 		///  <summary>
  1850 		///  Enables accessing a form's controls from another thread
  1851 		///  </summary>
  1852 		///
  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>
  1856 
  1857         private void MyMarshalDataToForm(FormActions action, params string[] strings)
  1858 		{
  1859 			try
  1860 			{
  1861                 object[] args = { action, strings };
  1862 
  1863 				//  The AccessForm routine contains the code that accesses the form.
  1864 
  1865 				MarshalDataToForm marshalDataToFormDelegate = AccessForm;
  1866 
  1867 				//  Execute AccessForm, passing the parameters in args.
  1868 
  1869 				Invoke(marshalDataToFormDelegate, args);
  1870 			}
  1871 			catch (Exception ex)
  1872 			{
  1873 				DisplayException(Name, ex);
  1874 				throw;
  1875 			}
  1876 		}
  1877 
  1878 		/// <summary>
  1879 		/// Timeout if read via interrupt transfer doesn't return.
  1880 		/// </summary>
  1881 
  1882 		private void OnReadTimeout()
  1883 		{
  1884 			try
  1885 			{
  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;
  1892 			}
  1893 			catch (Exception ex)
  1894 			{
  1895 				DisplayException(Name, ex);
  1896 				throw;
  1897 			}
  1898 		}
  1899 
  1900 		/// <summary>
  1901 		/// Timeout if write via interrupt transfer doesn't return.
  1902 		/// </summary>
  1903 
  1904 		private void OnWriteTimeout()
  1905 		{
  1906 			try
  1907 			{
  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;
  1914 			}
  1915 			catch (Exception ex)
  1916 			{
  1917 				DisplayException(Name, ex);
  1918 				throw;
  1919 			}
  1920 		}
  1921 
  1922 		/// <summary>
  1923 		/// Alternat sending and getting a report.
  1924 		/// </summary>
  1925 
  1926 		private void PeriodicTransfers()
  1927 		{
  1928 			try
  1929 			{
  1930 				if (!_transferInProgress)
  1931 				{
  1932 					if (_reportType == ReportTypes.Feature)
  1933 					{
  1934 						SendOrGetFeatureReport();
  1935 					}
  1936 					else
  1937 					{
  1938 						// Output and Input reports
  1939 
  1940 						SendOutputReportOrGetInputReport();
  1941 					}
  1942 				}
  1943 			}
  1944 			catch (Exception ex)
  1945 			{
  1946 				DisplayException(Name, ex);
  1947 				throw;
  1948 			}
  1949 		}
  1950 
  1951 		/// <summary>
  1952 		/// Start doing periodic transfers.
  1953 		/// </summary>
  1954 
  1955 		private void PeriodicTransfersStart()
  1956 		{
  1957 			// Don't allow changing the transfer type while transfers are in progress.
  1958 
  1959 			if (radFeature.Checked)
  1960 			{
  1961 				radInputOutputControl.Enabled = false;
  1962 				radInputOutputInterrupt.Enabled = false;
  1963 			}
  1964 			else if (radInputOutputControl.Checked)
  1965 			{
  1966 				radFeature.Enabled = false;
  1967 				radInputOutputInterrupt.Enabled = false;
  1968 			}
  1969 			else if (radInputOutputInterrupt.Checked)
  1970 			{
  1971 				radFeature.Enabled = false;
  1972 				radInputOutputControl.Enabled = false;
  1973 			}
  1974 
  1975 			//  Change the command button's text.
  1976 
  1977 			cmdPeriodicTransfers.Text = "Stop";
  1978 
  1979 			//  Enable the timer event to trigger a set of transfers.
  1980 
  1981 			_periodicTransfers.Start();
  1982 
  1983 			cmdPeriodicTransfers.Enabled = true;
  1984 
  1985 			if (radInputOutputInterrupt.Checked)
  1986 			{
  1987 				_transferType = TransferTypes.Interrupt;
  1988 				_reportType = ReportTypes.Output;
  1989 			}
  1990 			else if (radInputOutputControl.Checked)
  1991 			{
  1992 				_transferType = TransferTypes.Control;
  1993 				_reportType = ReportTypes.Output;
  1994 			}
  1995 			else if (radFeature.Checked)
  1996 			{
  1997 				_transferType = TransferTypes.Control;
  1998 				_reportType = ReportTypes.Feature;
  1999 			}
  2000 			_periodicTransfersRequested = true;
  2001 			PeriodicTransfers();
  2002 		}
  2003 
  2004 		/// <summary>
  2005 		/// Stop doing periodic transfers.
  2006 		/// </summary>
  2007 
  2008 		private void PeriodicTransfersStop()
  2009 		{
  2010 			//  Stop doing continuous transfers.
  2011 
  2012 			_periodicTransfersRequested = false;
  2013 
  2014 			// Disable the timer that triggers the transfers.
  2015 
  2016 			_periodicTransfers.Stop();
  2017 			cmdPeriodicTransfers.Enabled = true;
  2018 
  2019 			//  Change the command button's text.
  2020 
  2021 			cmdPeriodicTransfers.Text = "Start";
  2022 
  2023 			// Re-allow changing the transfer type.
  2024 
  2025 			radFeature.Enabled = true;
  2026 			radInputOutputControl.Enabled = true;
  2027 			radInputOutputInterrupt.Enabled = true;
  2028 		}
  2029 
  2030 		private void radInputOutputControl_CheckedChanged(object sender, EventArgs e)
  2031 		{
  2032 		}
  2033 
  2034 		private void radInputOutputInterrupt_CheckedChanged(object sender, EventArgs e)
  2035 		{
  2036 		}
  2037 
  2038 		private void radFeature_CheckedChanged(object sender, EventArgs e)
  2039 		{
  2040 		}
  2041 
  2042 		///  <summary>
  2043 		///  Request a Feature report.
  2044 		///  Assumes report ID = 0.
  2045 		///  </summary>
  2046 
  2047 		private void RequestToGetFeatureReport()
  2048 		{
  2049 			String byteValue = null;
  2050 
  2051 			try
  2052 			{
  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.
  2055 
  2056 				if (!_deviceHandleObtained)
  2057 				{
  2058 					_deviceHandleObtained = FindTheHid();
  2059 				}
  2060 
  2061 				if (_deviceHandleObtained)
  2062 				{
  2063 					Byte[] inFeatureReportBuffer = null;
  2064 
  2065 					if ((_myHid.Capabilities.FeatureReportByteLength > 0))
  2066 					{
  2067 						//  The HID has a Feature report.
  2068 						//  Read a report from the device.
  2069 
  2070 						//  Set the size of the Feature report buffer.
  2071 
  2072 						if ((_myHid.Capabilities.FeatureReportByteLength > 0))
  2073 						{
  2074 							inFeatureReportBuffer = new Byte[_myHid.Capabilities.FeatureReportByteLength];
  2075 						}
  2076 
  2077 						//  Read a report.
  2078 
  2079 						Boolean success = _myHid.GetFeatureReport(_hidHandle, ref inFeatureReportBuffer);
  2080 
  2081 						if (success)
  2082 						{
  2083 							DisplayReportData(inFeatureReportBuffer, ReportTypes.Feature, ReportReadOrWritten.Read);
  2084 						}
  2085 						else
  2086 						{
  2087 							CloseCommunications();
  2088 							MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to read a Feature report failed.");
  2089 							ScrollToBottomOfListBox();
  2090 						}
  2091 					}
  2092 					else
  2093 					{
  2094 						MyMarshalDataToForm(FormActions.AddItemToListBox, "The HID doesn't have a Feature report.");
  2095 						ScrollToBottomOfListBox();
  2096 					}
  2097 				}
  2098 				_transferInProgress = false;
  2099 				cmdGetFeatureReport.Enabled = true;
  2100 			}
  2101 			catch (Exception ex)
  2102 			{
  2103 				DisplayException(Name, ex);
  2104 				throw;
  2105 			}
  2106 		}
  2107 
  2108 		///  <summary>
  2109 		///  Request an Input report.
  2110 		///  Assumes report ID = 0.
  2111 		///  </summary>
  2112 
  2113 		private async void RequestToGetInputReport()
  2114 		{
  2115 			const Int32 readTimeout = 5000;
  2116 
  2117 			String byteValue = null;
  2118 			Byte[] inputReportBuffer = null;
  2119 
  2120 			try
  2121 			{
  2122 				Boolean success = false;
  2123 
  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.
  2126 
  2127 				if (!_deviceHandleObtained)
  2128 				{
  2129 					_deviceHandleObtained = FindTheHid();
  2130 				}
  2131 
  2132 				if (_deviceHandleObtained)
  2133 				{
  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.)
  2136 
  2137 					if (!_hidHandle.IsInvalid)
  2138 					{
  2139 						//  Read an Input report.
  2140 
  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.)
  2144 
  2145 						if (_myHid.Capabilities.InputReportByteLength > 0)
  2146 						{
  2147 							//  Set the size of the Input report buffer.
  2148 
  2149 							inputReportBuffer = new Byte[_myHid.Capabilities.InputReportByteLength];
  2150 
  2151 							if (_transferType.Equals(TransferTypes.Control))
  2152 							{
  2153 								{
  2154 									_transferInProgress = true;
  2155 
  2156 									//  Read a report using a control transfer.
  2157 
  2158 									success = _myHid.GetInputReportViaControlTransfer(_hidHandle, ref inputReportBuffer);
  2159 									cmdGetInputReportControl.Enabled = true;
  2160 									_transferInProgress = false;
  2161 								}
  2162 							}
  2163 							else
  2164 							{
  2165 								{
  2166 									_transferInProgress = true;
  2167 
  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.
  2171 
  2172 									// Create a delegate to execute on a timeout.
  2173 
  2174 									Action onReadTimeoutAction = OnReadTimeout;
  2175 
  2176 									// The CancellationTokenSource specifies the timeout value and the action to take on a timeout.
  2177 
  2178 									var cts = new CancellationTokenSource();
  2179 
  2180 									// Cancel the read if it hasn't completed after a timeout.
  2181 
  2182 									cts.CancelAfter(readTimeout);
  2183 
  2184 									// Specify the function to call on a timeout.
  2185 
  2186 									cts.Token.Register(onReadTimeoutAction);
  2187 
  2188 									// Stops waiting when data is available or on timeout:
  2189 
  2190 									Int32 bytesRead = await _myHid.GetInputReportViaInterruptTransfer(_deviceData, inputReportBuffer, cts);
  2191 
  2192 									// Arrive here only if the operation completed.
  2193 
  2194 									// Dispose to stop the timeout timer.
  2195 
  2196 									cts.Dispose();
  2197 
  2198 									_transferInProgress = false;
  2199 									cmdGetInputReportInterrupt.Enabled = true;
  2200 
  2201 									if (bytesRead > 0)
  2202 									{
  2203 										success = true;
  2204 										Debug.Print("bytes read (includes report ID) = " + Convert.ToString(bytesRead));
  2205 									}
  2206 								}
  2207 							}
  2208 						}
  2209 						else
  2210 						{
  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.");
  2213 						}
  2214 					}
  2215 					else
  2216 					{
  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.");
  2220 					}
  2221 
  2222 					if (success)
  2223 					{
  2224 						DisplayReportData(inputReportBuffer, ReportTypes.Input, ReportReadOrWritten.Read);
  2225 					}
  2226 					else
  2227 					{
  2228 						CloseCommunications();
  2229 						MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to read an Input report has failed.");
  2230 						ScrollToBottomOfListBox();
  2231 					}
  2232 				}
  2233 			}
  2234 			catch (Exception ex)
  2235 			{
  2236 				DisplayException(Name, ex);
  2237 				throw;
  2238 			}
  2239 		}
  2240 
  2241 		///  <summary>
  2242 		///  Sends a Feature report.
  2243 		///  Assumes report ID = 0.
  2244 		///  </summary>
  2245 
  2246 		private void RequestToSendFeatureReport()
  2247 		{
  2248 			String byteValue = null;
  2249 
  2250 			try
  2251 			{
  2252 				_transferInProgress = true;
  2253 
  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.
  2256 
  2257 				if (!_deviceHandleObtained)
  2258 				{
  2259 					_deviceHandleObtained = FindTheHid();
  2260 				}
  2261 
  2262 				if (_deviceHandleObtained)
  2263 				{
  2264 					GetBytesToSend();
  2265 
  2266 					if ((_myHid.Capabilities.FeatureReportByteLength > 0))
  2267 					{
  2268 						//  The HID has a Feature report.
  2269 						//  Set the size of the Feature report buffer.
  2270 
  2271 						var outFeatureReportBuffer = new Byte[_myHid.Capabilities.FeatureReportByteLength];
  2272 
  2273 						//  Store the report ID in the buffer.
  2274 
  2275 						outFeatureReportBuffer[0] = 0;
  2276 
  2277 						//  Store the report data following the report ID.
  2278 						//  Use the data in the combo boxes on the form.
  2279 
  2280 						outFeatureReportBuffer[1] = Convert.ToByte(CboByte0.SelectedIndex);
  2281 
  2282 						if (outFeatureReportBuffer.GetUpperBound(0) > 1)
  2283 						{
  2284 							outFeatureReportBuffer[2] = Convert.ToByte(CboByte1.SelectedIndex);
  2285 						}
  2286 
  2287 						//  Write a report to the device
  2288 
  2289 						Boolean success = _myHid.SendFeatureReport(_hidHandle, outFeatureReportBuffer);
  2290 
  2291 						if (success)
  2292 						{
  2293 							DisplayReportData(outFeatureReportBuffer, ReportTypes.Feature, ReportReadOrWritten.Written);
  2294 						}
  2295 						else
  2296 						{
  2297 							CloseCommunications();
  2298 							AccessForm(FormActions.AddItemToListBox, "The attempt to send a Feature report failed.");
  2299 							ScrollToBottomOfListBox();
  2300 						}
  2301 					}
  2302 					else
  2303 					{
  2304 						AccessForm(FormActions.AddItemToListBox, "The HID doesn't have a Feature report.");
  2305 						ScrollToBottomOfListBox();
  2306 					}
  2307 
  2308 				}
  2309 				_transferInProgress = false;
  2310 				cmdSendFeatureReport.Enabled = true;
  2311 				ScrollToBottomOfListBox();
  2312 
  2313 			}
  2314 			catch (Exception ex)
  2315 			{
  2316 				DisplayException(Name, ex);
  2317 				throw;
  2318 			}
  2319 		}
  2320 
  2321 		///  <summary>
  2322 		///  Sends an Output report.
  2323 		///  Assumes report ID = 0.
  2324 		///  </summary>
  2325 
  2326 		private async void RequestToSendOutputReport()
  2327 		{
  2328 			const Int32 writeTimeout = 5000;
  2329 			String byteValue = null;
  2330 
  2331 			try
  2332 			{
  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.
  2335 
  2336 				if (!_deviceHandleObtained)
  2337 				{
  2338 					_deviceHandleObtained = FindTheHid();
  2339 				}
  2340 
  2341 				if (_deviceHandleObtained)
  2342 				{
  2343 					GetBytesToSend();
  2344 				}
  2345 				//  Don't attempt to exchange reports if valid handles aren't available
  2346 				//  (as for a mouse or keyboard.)
  2347 
  2348 				if (!_hidHandle.IsInvalid)
  2349 				{
  2350 					//  Don't attempt to send an Output report if the HID has no Output report.
  2351 
  2352 					if (_myHid.Capabilities.OutputReportByteLength > 0)
  2353 					{
  2354 						//  Set the size of the Output report buffer.
  2355 
  2356 						var outputReportBuffer = new Byte[_myHid.Capabilities.OutputReportByteLength];
  2357 
  2358 						//  Store the report ID in the first byte of the buffer:
  2359 
  2360 						outputReportBuffer[0] = 0;
  2361 
  2362 						//  Store the report data following the report ID.
  2363 						//  Use the data in the combo boxes on the form.
  2364 
  2365 						outputReportBuffer[1] = Convert.ToByte(CboByte0.SelectedIndex);
  2366 
  2367 						if (outputReportBuffer.GetUpperBound(0) > 1)
  2368 						{
  2369 							outputReportBuffer[2] = Convert.ToByte(CboByte1.SelectedIndex);
  2370 						}
  2371 
  2372 						//  Write a report.
  2373 
  2374 						Boolean success;
  2375 
  2376 						if (_transferType.Equals(TransferTypes.Control))
  2377 						{
  2378 							{
  2379 								_transferInProgress = true;
  2380 
  2381 								//  Use a control transfer to send the report,
  2382 								//  even if the HID has an interrupt OUT endpoint.
  2383 
  2384 								success = _myHid.SendOutputReportViaControlTransfer(_hidHandle, outputReportBuffer);
  2385 
  2386 								_transferInProgress = false;
  2387 								cmdSendOutputReportControl.Enabled = true;
  2388 							}
  2389 						}
  2390 						else
  2391 						{
  2392 							Debug.Print("interrupt");
  2393 							_transferInProgress = true;
  2394 
  2395 							// The CancellationTokenSource specifies the timeout value and the action to take on a timeout.
  2396 
  2397 							var cts = new CancellationTokenSource();
  2398 
  2399 							// Create a delegate to execute on a timeout.
  2400 
  2401 							Action onWriteTimeoutAction = OnWriteTimeout;
  2402 
  2403 							// Cancel the read if it hasn't completed after a timeout.
  2404 
  2405 							cts.CancelAfter(writeTimeout);
  2406 
  2407 							// Specify the function to call on a timeout.
  2408 
  2409 							cts.Token.Register(onWriteTimeoutAction);
  2410 
  2411 							// Send an Output report and wait for completion or timeout.
  2412 
  2413 							success = await _myHid.SendOutputReportViaInterruptTransfer(_deviceData, _hidHandle, outputReportBuffer, cts);
  2414 
  2415 							// Get here only if the operation completes without a timeout.
  2416 
  2417 							_transferInProgress = false;
  2418 							cmdSendOutputReportInterrupt.Enabled = true;
  2419 
  2420 							// Dispose to stop the timeout timer.
  2421 
  2422 							cts.Dispose();
  2423 						}
  2424 						if (success)
  2425 						{
  2426 							DisplayReportData(outputReportBuffer, ReportTypes.Output, ReportReadOrWritten.Written);
  2427 						}
  2428 						else
  2429 						{
  2430 							CloseCommunications();
  2431 							AccessForm(FormActions.AddItemToListBox, "The attempt to write an Output report failed.");
  2432 							ScrollToBottomOfListBox();
  2433 						}
  2434 					}
  2435 				}
  2436 				else
  2437 				{
  2438 					AccessForm(FormActions.AddItemToListBox, "The HID doesn't have an Output report.");
  2439 				}
  2440 			}
  2441 			catch (Exception ex)
  2442 			{
  2443 				DisplayException(Name, ex);
  2444 				throw;
  2445 			}
  2446 		}
  2447 
  2448 		///  <summary>
  2449 		///  Scroll to the bottom of the list box and trim as needed.
  2450 		///  </summary>
  2451 
  2452 		private void ScrollToBottomOfListBox()
  2453 		{
  2454 			try
  2455 			{
  2456 				LstResults.SelectedIndex = LstResults.Items.Count - 1;
  2457 
  2458 				//  If the list box is getting too large, trim its contents by removing the earliest data.
  2459 
  2460 				if (LstResults.Items.Count > 1000)
  2461 				{
  2462 					Int32 count;
  2463 					for (count = 1; count <= 500; count++)
  2464 					{
  2465 						LstResults.Items.RemoveAt(4);
  2466 					}
  2467 				}
  2468 			}
  2469 			catch (Exception ex)
  2470 			{
  2471 				DisplayException(Name, ex);
  2472 				throw;
  2473 			}
  2474 		}
  2475 
  2476 		/// <summary>
  2477 		/// Request to send or get a Feature report.
  2478 		/// </summary>
  2479 
  2480 		private void SendOrGetFeatureReport()
  2481 		{
  2482 			try
  2483 			{
  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.
  2486 
  2487 				if (!_deviceHandleObtained)
  2488 				{
  2489 					_deviceHandleObtained = FindTheHid();
  2490 				}
  2491 
  2492 				if (_deviceHandleObtained)
  2493 				{
  2494 					switch (_sendOrGet)
  2495 					{
  2496 						case SendOrGet.Send:
  2497 							RequestToSendFeatureReport();
  2498 							_sendOrGet = SendOrGet.Get;
  2499 							break;
  2500 						case SendOrGet.Get:
  2501 							RequestToGetFeatureReport();
  2502 							_sendOrGet = SendOrGet.Send;
  2503 							break;
  2504 					}
  2505 				}
  2506 			}
  2507 			catch (Exception ex)
  2508 			{
  2509 				DisplayException(Name, ex);
  2510 				throw;
  2511 			}
  2512 		}
  2513 
  2514 		/// <summary>
  2515 		/// Request to send an Output report or get an Input report.
  2516 		/// </summary>
  2517 
  2518 		private void SendOutputReportOrGetInputReport()
  2519 		{
  2520 			try
  2521 			{
  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.
  2524 
  2525 				if (!_deviceHandleObtained)
  2526 				{
  2527 					_deviceHandleObtained = FindTheHid();
  2528 				}
  2529 
  2530 				if (_deviceHandleObtained)
  2531 				{
  2532 					if (_sendOrGet == SendOrGet.Send)
  2533 					{
  2534 						RequestToSendOutputReport();
  2535 						_sendOrGet = SendOrGet.Get;
  2536 					}
  2537 					else
  2538 					{
  2539 						RequestToGetInputReport();
  2540 						_sendOrGet = SendOrGet.Send;
  2541 					}
  2542 				}
  2543 			}
  2544 			catch (Exception ex)
  2545 			{
  2546 				DisplayException(Name, ex);
  2547 				throw;
  2548 			}
  2549 		}
  2550 
  2551 		///  <summary>
  2552 		///  Set the number of Input buffers (the number of Input reports
  2553 		///  the host will store) from the value in the text box.
  2554 		///  </summary>
  2555 
  2556 		private void SetInputReportBufferSize()
  2557 		{
  2558 			try
  2559 			{
  2560 				if (!_transferInProgress)
  2561 				{
  2562 					//  Get the number of buffers from the text box.
  2563 
  2564 					Int32 numberOfInputBuffers = Convert.ToInt32(txtInputReportBufferSize.Text);
  2565 
  2566 					//  Set the number of buffers.
  2567 
  2568 					_myHid.SetNumberOfInputBuffers(_hidHandle, numberOfInputBuffers);
  2569 
  2570 					//  Verify and display the result.
  2571 
  2572 					GetInputReportBufferSize();
  2573 				}
  2574 				else
  2575 				{
  2576 					DisplayTransferInProgressMessage();
  2577 				}
  2578 			}
  2579 			catch (Exception ex)
  2580 			{
  2581 				DisplayException(Name, ex);
  2582 				throw;
  2583 			}
  2584 		}
  2585 
  2586 		///  <summary>
  2587 		///  Perform actions that must execute when the program ends.
  2588 		///  </summary>
  2589 
  2590 		private void Shutdown()
  2591 		{
  2592 			try
  2593 			{
  2594 				CloseCommunications();
  2595 				DeviceNotificationsStop();
  2596 			}
  2597 			catch (Exception ex)
  2598 			{
  2599 				DisplayException(Name, ex);
  2600 				throw;
  2601 			}
  2602 		}
  2603 
  2604 		///  <summary>
  2605 		///  Perform actions that must execute when the program starts.
  2606 		///  </summary>
  2607 
  2608 		private void Startup()
  2609 		{
  2610 			const Int32 periodicTransferInterval = 1000;
  2611 			try
  2612 			{
  2613 				_myHid = new Hid();
  2614 				InitializeDisplay();
  2615 
  2616 				_periodicTransfers = new System.Timers.Timer(periodicTransferInterval);
  2617 				_periodicTransfers.Elapsed += DoPeriodicTransfers;
  2618 				_periodicTransfers.Stop();
  2619 				_periodicTransfers.SynchronizingObject = this;
  2620 
  2621 				//  Default USB Vendor ID and Product ID:
  2622 
  2623 				txtVendorID.Text = "0925";
  2624 				txtProductID.Text = "7001";
  2625 
  2626 				GetVendorAndProductIDsFromTextBoxes(ref _myVendorId, ref _myProductId);
  2627 
  2628 				DeviceNotificationsStart();
  2629 				FindDeviceUsingWmi();
  2630 				FindTheHid();
  2631 			}
  2632 			catch (Exception ex)
  2633 			{
  2634 				DisplayException(Name, ex);
  2635 				throw;
  2636 			}
  2637 		}
  2638 
  2639 		///  <summary>
  2640 		///  The Product ID has changed in the text box. Call a routine to handle it.
  2641 		///  </summary>
  2642 
  2643 		private void txtProductID_TextChanged(Object sender, EventArgs e)
  2644 		{
  2645 			try
  2646 			{
  2647 				//DeviceHasChanged();
  2648 			}
  2649 			catch (Exception ex)
  2650 			{
  2651 				DisplayException(Name, ex);
  2652 				throw;
  2653 			}
  2654 		}
  2655 
  2656 		///  <summary>
  2657 		///  The Vendor ID has changed in the text box. Call a routine to handle it.
  2658 		///  </summary>
  2659 
  2660 		private void txtVendorID_TextChanged(Object sender, EventArgs e)
  2661 		{
  2662 			try
  2663 			{
  2664 				//DeviceHasChanged();
  2665 			}
  2666 			catch (Exception ex)
  2667 			{
  2668 				DisplayException(Name, ex);
  2669 				throw;
  2670 			}
  2671 		}
  2672 
  2673 		///  <summary>
  2674 		///  Provides a central mechanism for exception handling.
  2675 		///  Displays a message box that describes the exception.
  2676 		///  </summary>
  2677 		///
  2678 		///  <param name="moduleName"> the module where the exception occurred. </param>
  2679 		///  <param name="e"> the exception </param>
  2680 
  2681 		internal static void DisplayException(String moduleName, Exception e)
  2682 		{
  2683 			//  Create an error message.
  2684 
  2685 			String message = "Exception: " + e.Message + Environment.NewLine + "Module: " + moduleName + Environment.NewLine + "Method: " + e.TargetSite.Name;
  2686 
  2687 			const String caption = "Unexpected Exception";
  2688 
  2689 			MessageBox.Show(message, caption, MessageBoxButtons.OK);
  2690 			Debug.Write(message);
  2691 
  2692 			// Get the last error and display it.
  2693 
  2694 			Int32 error = Marshal.GetLastWin32Error();
  2695 
  2696 			Debug.WriteLine("The last Win32 Error was: " + error);
  2697 		}
  2698 
  2699 		[STAThread]
  2700 		internal static void Main() { Application.Run(new FrmMain()); }
  2701 		private static FrmMain _transDefaultFormFrmMain;
  2702 		internal static FrmMain TransDefaultFormFrmMain
  2703 		{
  2704 			get
  2705 			{
  2706 				if (_transDefaultFormFrmMain == null)
  2707 				{
  2708 					_transDefaultFormFrmMain = new FrmMain();
  2709 				}
  2710 				return _transDefaultFormFrmMain;
  2711 			}
  2712 		}
  2713 
  2714         private void treeViewDevices_AfterSelect(object sender, TreeViewEventArgs e)
  2715         {
  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);
  2722             if (match.Success)
  2723             {
  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;
  2728                 DeviceHasChanged();
  2729             }
  2730         }
  2731 	}
  2732 }