FrmMain.cs
author sl
Wed, 14 May 2014 16:37:44 +0200
changeset 4 0220435cf48a
parent 3 f5e6a5b8a56a
child 5 8ea7a7e29c4d
permissions -rw-r--r--
Newly added device are now showing green. Removed devices are showing red.
Other devices are showing black. Devices in unknown state are showing purple.
     1 using Microsoft.Win32.SafeHandles;
     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 
    14 namespace GenericHid
    15 {
    16 	///<summary>
    17 	/// Project: GenericHid
    18 	///
    19 	/// ***********************************************************************
    20 	/// Software License Agreement
    21 	///
    22 	/// Licensor grants any person obtaining a copy of this software ("You")
    23 	/// a worldwide, royalty-free, non-exclusive license, for the duration of
    24 	/// the copyright, free of charge, to store and execute the Software in a
    25 	/// computer system and to incorporate the Software or any portion of it
    26 	/// in computer programs You write.
    27 	///
    28 	/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    29 	/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    30 	/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    31 	/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    32 	/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    33 	/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    34 	/// THE SOFTWARE.
    35 	/// ***********************************************************************
    36 	///
    37 	/// Author
    38 	/// Jan Axelson
    39 	///
    40 	/// This software was written using Visual Studio Express 2012 for Windows
    41 	/// Desktop building for the .NET Framework v4.5.
    42 	///
    43 	/// Purpose:
    44 	/// Demonstrates USB communications with a generic HID-class device
    45 	///
    46 	/// Requirements:
    47 	/// Windows Vista or later and an attached USB generic Human Interface Device (HID).
    48 	/// (Does not run on Windows XP or earlier because .NET Framework 4.5 will not install on these OSes.)
    49 	///
    50 	/// Description:
    51 	/// Finds an attached device that matches the vendor and product IDs in the form's
    52 	/// text boxes.
    53 	///
    54 	/// Retrieves the device's capabilities.
    55 	/// Sends and requests HID reports.
    56 	///
    57 	/// Uses the System.Management class and Windows Management Instrumentation (WMI) to detect
    58 	/// when a device is attached or removed.
    59 	///
    60 	/// A list box displays the data sent and received along with error and status messages.
    61 	/// You can select data to send and 1-time or periodic transfers.
    62 	///
    63 	/// You can change the size of the host's Input report buffer and request to use control
    64 	/// transfers only to exchange Input and Output reports.
    65 	///
    66 	/// To view additional debugging messages, in the Visual Studio development environment,
    67 	/// from the main menu, select Build > Configuration Manager > Active Solution Configuration
    68 	/// and select Configuration > Debug and from the main menu, select View > Output.
    69 	///
    70 	/// The application uses asynchronous FileStreams to read Input reports and write Output
    71 	/// reports so the application's main thread doesn't have to wait for the device to retrieve a
    72 	/// report when the HID driver's buffer is empty or send a report when the device's endpoint is busy.
    73 	///
    74 	/// For code that finds a device and opens handles to it, see the FindTheHid routine in frmMain.cs.
    75 	/// For code that reads from the device, see GetInputReportViaInterruptTransfer,
    76 	/// GetInputReportViaControlTransfer, and GetFeatureReport in Hid.cs.
    77 	/// For code that writes to the device, see SendInputReportViaInterruptTransfer,
    78 	/// SendInputReportViaControlTransfer, and SendFeatureReport in Hid.cs.
    79 	///
    80 	/// This project includes the following modules:
    81 	///
    82 	/// GenericHid.cs - runs the application.
    83 	/// FrmMain.cs - routines specific to the form.
    84 	/// Hid.cs - routines specific to HID communications.
    85 	/// DeviceManagement.cs - routine for obtaining a handle to a device from its GUID.
    86 	/// Debugging.cs - contains a routine for displaying API error messages.
    87 	/// HidDeclarations.cs - Declarations for API functions used by Hid.cs.
    88 	/// FileIODeclarations.cs - Declarations for file-related API functions.
    89 	/// DeviceManagementDeclarations.cs - Declarations for API functions used by DeviceManagement.cs.
    90 	/// DebuggingDeclarations.cs - Declarations for API functions used by Debugging.cs.
    91 	///
    92 	/// Companion device firmware for several device CPUs is available from www.Lvr.com/hidpage.htm
    93 	/// You can use any generic HID (not a system mouse or keyboard) that sends and receives reports.
    94 	/// This application will not detect or communicate with non-HID-class devices.
    95 	///
    96 	/// For more information about HIDs and USB, and additional example device firmware to use
    97 	/// with this application, visit Lakeview Research at http://Lvr.com
    98 	/// Send comments, bug reports, etc. to jan@Lvr.com or post on my PORTS forum: http://www.lvr.com/forum
    99 	///
   100 	/// V6.2
   101 	/// 11/12/13
   102 	/// Disabled form buttons when a transfer is in progress.
   103 	/// Other minor edits for clarity and readability.
   104 	/// Will NOT run on Windows XP or earlier, see below.
   105 	///
   106 	/// V6.1
   107 	/// 10/28/13
   108 	/// Uses the .NET System.Management class to detect device arrival and removal with WMI instead of Win32 RegisterDeviceNotification.
   109 	/// Other minor edits.
   110 	/// Will NOT run on Windows XP or earlier, see below.
   111 	///
   112 	/// V6.0
   113 	/// 2/8/13
   114 	/// This version will NOT run on Windows XP or earlier because the code uses .NET Framework 4.5 to support asynchronous FileStreams.
   115 	/// The .NET Framework 4.5 redistributable is compatible with Windows 8, Windows 7 SP1, Windows Server 2008 R2 SP1,
   116 	/// Windows Server 2008 SP2, Windows Vista SP2, and Windows Vista SP3.
   117 	/// For compatibility, replaced ToInt32 with ToInt64 here:
   118 	/// IntPtr pDevicePathName = new IntPtr(detailDataBuffer.ToInt64() + 4);
   119 	/// and here:
   120 	/// if ((deviceNotificationHandle.ToInt64() == IntPtr.Zero.ToInt64()))
   121 	/// For compatibility if the charset isn't English, added System.Globalization.CultureInfo.InvariantCulture here:
   122 	/// if ((String.Compare(DeviceNameString, mydevicePathName, true, System.Globalization.CultureInfo.InvariantCulture) == 0))
   123 	/// Replaced all Microsoft.VisualBasic namespace code with other .NET equivalents.
   124 	/// Revised user interface for more flexibility.
   125 	/// Moved interrupt-transfer and other HID-specific code to Hid.cs.
   126 	/// Used JetBrains ReSharper to clean up the code: http://www.jetbrains.com/resharper/
   127 	///
   128 	/// V5.0
   129 	/// 3/30/11
   130 	/// Replaced ReadFile and WriteFile with FileStreams. Thanks to Joe Dunne and John on my Ports forum for tips on this.
   131 	/// Simplified Hid.cs.
   132 	/// Replaced the form timer with a system timer.
   133 	///
   134 	/// V4.6
   135 	/// 1/12/10
   136 	/// Supports Vendor IDs and Product IDs up to FFFFh.
   137 	///
   138 	/// V4.52
   139 	/// 11/10/09
   140 	/// Changed HIDD_ATTRIBUTES to use UInt16
   141 	///
   142 	/// V4.51
   143 	/// 2/11/09
   144 	/// Moved Free_ and similar to Finally blocks to ensure they execute.
   145 	///
   146 	/// V4.5
   147 	/// 2/9/09
   148 	/// Changes to support 64-bit systems, memory management, and other corrections.
   149 	/// Big thanks to Peter Nielsen.
   150 	///
   151 	/// </summary>
   152 
   153 	internal class FrmMain
   154 		: Form
   155 	{
   156 		#region '"Windows Form Designer generated code "'
   157 		public FrmMain()
   158 		//: base()
   159 		{
   160 			// This call is required by the Windows Form Designer.
   161 			InitializeComponent();
   162 		}
   163 		// Form overrides dispose to clean up the component list.
   164 		protected override void Dispose(bool Disposing1)
   165 		{
   166 			if (Disposing1)
   167 			{
   168 				if (components != null)
   169 				{
   170 					components.Dispose();
   171 				}
   172 			}
   173 			base.Dispose(Disposing1);
   174 		}
   175 
   176 		// Required by the Windows Form Designer
   177 		private System.ComponentModel.IContainer components;
   178 		public System.Windows.Forms.ToolTip ToolTip1;
   179 		public System.Windows.Forms.TextBox TxtBytesReceived;
   180 		public System.Windows.Forms.GroupBox FraBytesReceived;
   181 		public System.Windows.Forms.CheckBox ChkAutoincrement;
   182 		public System.Windows.Forms.ComboBox CboByte1;
   183 		public System.Windows.Forms.ComboBox CboByte0;
   184 		public System.Windows.Forms.GroupBox FraBytesToSend;
   185 		public System.Windows.Forms.ListBox LstResults;
   186 		// NOTE: The following procedure is required by the Windows Form Designer
   187 		// It can be modified using the Windows Form Designer.
   188 		// Do not modify it using the code editor.
   189 		internal System.Windows.Forms.GroupBox fraInputReportBufferSize;
   190 		internal System.Windows.Forms.TextBox txtInputReportBufferSize;
   191 		internal System.Windows.Forms.Button cmdInputReportBufferSize;
   192 		internal System.Windows.Forms.GroupBox fraDeviceIdentifiers;
   193 		internal System.Windows.Forms.Label lblVendorID;
   194 		internal System.Windows.Forms.TextBox txtVendorID;
   195 		internal System.Windows.Forms.Label lblProductID;
   196 		internal System.Windows.Forms.TextBox txtProductID;
   197 		internal System.Windows.Forms.Button cmdFindDevice;
   198 		private Button cmdGetInputReportInterrupt;
   199 		public GroupBox fraInterruptTransfers;
   200 		private Button cmdSendOutputReportControl;
   201 		private Button cmdGetInputReportControl;
   202 		public GroupBox fraControlTransfers;
   203 		private Button cmdGetFeatureReport;
   204 		private Button cmdSendFeatureReport;
   205 		private Button cmdPeriodicTransfers;
   206 		public GroupBox fraSendAndGetContinuous;
   207 		private RadioButton radFeature;
   208 		private RadioButton radInputOutputControl;
   209 		private RadioButton radInputOutputInterrupt;
   210         private TreeView treeViewDevices;
   211 		private Button cmdSendOutputReportInterrupt;
   212 
   213 		[System.Diagnostics.DebuggerStepThrough()]
   214 		private void InitializeComponent()
   215 		{
   216             this.components = new System.ComponentModel.Container();
   217             this.ToolTip1 = new System.Windows.Forms.ToolTip(this.components);
   218             this.FraBytesReceived = new System.Windows.Forms.GroupBox();
   219             this.TxtBytesReceived = new System.Windows.Forms.TextBox();
   220             this.FraBytesToSend = new System.Windows.Forms.GroupBox();
   221             this.ChkAutoincrement = new System.Windows.Forms.CheckBox();
   222             this.CboByte1 = new System.Windows.Forms.ComboBox();
   223             this.CboByte0 = new System.Windows.Forms.ComboBox();
   224             this.LstResults = new System.Windows.Forms.ListBox();
   225             this.fraInputReportBufferSize = new System.Windows.Forms.GroupBox();
   226             this.cmdInputReportBufferSize = new System.Windows.Forms.Button();
   227             this.txtInputReportBufferSize = new System.Windows.Forms.TextBox();
   228             this.fraDeviceIdentifiers = new System.Windows.Forms.GroupBox();
   229             this.txtProductID = new System.Windows.Forms.TextBox();
   230             this.lblProductID = new System.Windows.Forms.Label();
   231             this.txtVendorID = new System.Windows.Forms.TextBox();
   232             this.lblVendorID = new System.Windows.Forms.Label();
   233             this.cmdFindDevice = new System.Windows.Forms.Button();
   234             this.cmdSendOutputReportInterrupt = new System.Windows.Forms.Button();
   235             this.cmdGetInputReportInterrupt = new System.Windows.Forms.Button();
   236             this.fraInterruptTransfers = new System.Windows.Forms.GroupBox();
   237             this.cmdPeriodicTransfers = new System.Windows.Forms.Button();
   238             this.cmdSendOutputReportControl = new System.Windows.Forms.Button();
   239             this.cmdGetInputReportControl = new System.Windows.Forms.Button();
   240             this.fraControlTransfers = new System.Windows.Forms.GroupBox();
   241             this.cmdGetFeatureReport = new System.Windows.Forms.Button();
   242             this.cmdSendFeatureReport = new System.Windows.Forms.Button();
   243             this.fraSendAndGetContinuous = new System.Windows.Forms.GroupBox();
   244             this.radFeature = new System.Windows.Forms.RadioButton();
   245             this.radInputOutputControl = new System.Windows.Forms.RadioButton();
   246             this.radInputOutputInterrupt = new System.Windows.Forms.RadioButton();
   247             this.treeViewDevices = new System.Windows.Forms.TreeView();
   248             this.FraBytesReceived.SuspendLayout();
   249             this.FraBytesToSend.SuspendLayout();
   250             this.fraInputReportBufferSize.SuspendLayout();
   251             this.fraDeviceIdentifiers.SuspendLayout();
   252             this.fraInterruptTransfers.SuspendLayout();
   253             this.fraControlTransfers.SuspendLayout();
   254             this.fraSendAndGetContinuous.SuspendLayout();
   255             this.SuspendLayout();
   256             //
   257             // FraBytesReceived
   258             //
   259             this.FraBytesReceived.BackColor = System.Drawing.SystemColors.Control;
   260             this.FraBytesReceived.Controls.Add(this.TxtBytesReceived);
   261             this.FraBytesReceived.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
   262             this.FraBytesReceived.ForeColor = System.Drawing.SystemColors.ControlText;
   263             this.FraBytesReceived.Location = new System.Drawing.Point(495, 353);
   264             this.FraBytesReceived.Name = "FraBytesReceived";
   265             this.FraBytesReceived.RightToLeft = System.Windows.Forms.RightToLeft.No;
   266             this.FraBytesReceived.Size = new System.Drawing.Size(112, 136);
   267             this.FraBytesReceived.TabIndex = 4;
   268             this.FraBytesReceived.TabStop = false;
   269             this.FraBytesReceived.Text = "Bytes Received";
   270             //
   271             // TxtBytesReceived
   272             //
   273             this.TxtBytesReceived.AcceptsReturn = true;
   274             this.TxtBytesReceived.BackColor = System.Drawing.SystemColors.Window;
   275             this.TxtBytesReceived.Cursor = System.Windows.Forms.Cursors.IBeam;
   276             this.TxtBytesReceived.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
   277             this.TxtBytesReceived.ForeColor = System.Drawing.SystemColors.WindowText;
   278             this.TxtBytesReceived.Location = new System.Drawing.Point(18, 24);
   279             this.TxtBytesReceived.MaxLength = 0;
   280             this.TxtBytesReceived.Multiline = true;
   281             this.TxtBytesReceived.Name = "TxtBytesReceived";
   282             this.TxtBytesReceived.RightToLeft = System.Windows.Forms.RightToLeft.No;
   283             this.TxtBytesReceived.Size = new System.Drawing.Size(72, 96);
   284             this.TxtBytesReceived.TabIndex = 5;
   285             //
   286             // FraBytesToSend
   287             //
   288             this.FraBytesToSend.BackColor = System.Drawing.SystemColors.Control;
   289             this.FraBytesToSend.Controls.Add(this.ChkAutoincrement);
   290             this.FraBytesToSend.Controls.Add(this.CboByte1);
   291             this.FraBytesToSend.Controls.Add(this.CboByte0);
   292             this.FraBytesToSend.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
   293             this.FraBytesToSend.ForeColor = System.Drawing.SystemColors.ControlText;
   294             this.FraBytesToSend.Location = new System.Drawing.Point(612, 235);
   295             this.FraBytesToSend.Name = "FraBytesToSend";
   296             this.FraBytesToSend.RightToLeft = System.Windows.Forms.RightToLeft.No;
   297             this.FraBytesToSend.Size = new System.Drawing.Size(160, 136);
   298             this.FraBytesToSend.TabIndex = 1;
   299             this.FraBytesToSend.TabStop = false;
   300             this.FraBytesToSend.Text = "Bytes to Send";
   301             //
   302             // ChkAutoincrement
   303             //
   304             this.ChkAutoincrement.BackColor = System.Drawing.SystemColors.Control;
   305             this.ChkAutoincrement.Cursor = System.Windows.Forms.Cursors.Default;
   306             this.ChkAutoincrement.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
   307             this.ChkAutoincrement.ForeColor = System.Drawing.SystemColors.ControlText;
   308             this.ChkAutoincrement.Location = new System.Drawing.Point(8, 96);
   309             this.ChkAutoincrement.Name = "ChkAutoincrement";
   310             this.ChkAutoincrement.RightToLeft = System.Windows.Forms.RightToLeft.No;
   311             this.ChkAutoincrement.Size = new System.Drawing.Size(201, 35);
   312             this.ChkAutoincrement.TabIndex = 6;
   313             this.ChkAutoincrement.Text = "Autoincrement values";
   314             this.ChkAutoincrement.UseVisualStyleBackColor = false;
   315             //
   316             // CboByte1
   317             //
   318             this.CboByte1.BackColor = System.Drawing.SystemColors.Window;
   319             this.CboByte1.Cursor = System.Windows.Forms.Cursors.Default;
   320             this.CboByte1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
   321             this.CboByte1.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
   322             this.CboByte1.ForeColor = System.Drawing.SystemColors.WindowText;
   323             this.CboByte1.Location = new System.Drawing.Point(8, 64);
   324             this.CboByte1.Name = "CboByte1";
   325             this.CboByte1.RightToLeft = System.Windows.Forms.RightToLeft.No;
   326             this.CboByte1.Size = new System.Drawing.Size(101, 22);
   327             this.CboByte1.TabIndex = 3;
   328             //
   329             // CboByte0
   330             //
   331             this.CboByte0.BackColor = System.Drawing.SystemColors.Window;
   332             this.CboByte0.Cursor = System.Windows.Forms.Cursors.Default;
   333             this.CboByte0.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
   334             this.CboByte0.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
   335             this.CboByte0.ForeColor = System.Drawing.SystemColors.WindowText;
   336             this.CboByte0.Location = new System.Drawing.Point(8, 24);
   337             this.CboByte0.Name = "CboByte0";
   338             this.CboByte0.RightToLeft = System.Windows.Forms.RightToLeft.No;
   339             this.CboByte0.Size = new System.Drawing.Size(101, 22);
   340             this.CboByte0.TabIndex = 2;
   341             //
   342             // LstResults
   343             //
   344             this.LstResults.BackColor = System.Drawing.SystemColors.Window;
   345             this.LstResults.Cursor = System.Windows.Forms.Cursors.Default;
   346             this.LstResults.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
   347             this.LstResults.ForeColor = System.Drawing.SystemColors.WindowText;
   348             this.LstResults.HorizontalScrollbar = true;
   349             this.LstResults.ItemHeight = 14;
   350             this.LstResults.Location = new System.Drawing.Point(12, 494);
   351             this.LstResults.Name = "LstResults";
   352             this.LstResults.RightToLeft = System.Windows.Forms.RightToLeft.No;
   353             this.LstResults.Size = new System.Drawing.Size(760, 256);
   354             this.LstResults.TabIndex = 0;
   355             //
   356             // fraInputReportBufferSize
   357             //
   358             this.fraInputReportBufferSize.Controls.Add(this.cmdInputReportBufferSize);
   359             this.fraInputReportBufferSize.Controls.Add(this.txtInputReportBufferSize);
   360             this.fraInputReportBufferSize.Location = new System.Drawing.Point(623, 44);
   361             this.fraInputReportBufferSize.Name = "fraInputReportBufferSize";
   362             this.fraInputReportBufferSize.Size = new System.Drawing.Size(149, 79);
   363             this.fraInputReportBufferSize.TabIndex = 9;
   364             this.fraInputReportBufferSize.TabStop = false;
   365             this.fraInputReportBufferSize.Text = "Input Report Buffer Size";
   366             //
   367             // cmdInputReportBufferSize
   368             //
   369             this.cmdInputReportBufferSize.Location = new System.Drawing.Point(6, 47);
   370             this.cmdInputReportBufferSize.Name = "cmdInputReportBufferSize";
   371             this.cmdInputReportBufferSize.Size = new System.Drawing.Size(136, 26);
   372             this.cmdInputReportBufferSize.TabIndex = 1;
   373             this.cmdInputReportBufferSize.Text = "Change Buffer Size";
   374             this.cmdInputReportBufferSize.Click += new System.EventHandler(this.cmdInputReportBufferSize_Click);
   375             //
   376             // txtInputReportBufferSize
   377             //
   378             this.txtInputReportBufferSize.Location = new System.Drawing.Point(6, 21);
   379             this.txtInputReportBufferSize.Name = "txtInputReportBufferSize";
   380             this.txtInputReportBufferSize.Size = new System.Drawing.Size(56, 20);
   381             this.txtInputReportBufferSize.TabIndex = 0;
   382             //
   383             // fraDeviceIdentifiers
   384             //
   385             this.fraDeviceIdentifiers.Controls.Add(this.txtProductID);
   386             this.fraDeviceIdentifiers.Controls.Add(this.lblProductID);
   387             this.fraDeviceIdentifiers.Controls.Add(this.txtVendorID);
   388             this.fraDeviceIdentifiers.Controls.Add(this.lblVendorID);
   389             this.fraDeviceIdentifiers.Location = new System.Drawing.Point(409, 12);
   390             this.fraDeviceIdentifiers.Name = "fraDeviceIdentifiers";
   391             this.fraDeviceIdentifiers.Size = new System.Drawing.Size(208, 96);
   392             this.fraDeviceIdentifiers.TabIndex = 10;
   393             this.fraDeviceIdentifiers.TabStop = false;
   394             this.fraDeviceIdentifiers.Text = "Device Identifiers";
   395             //
   396             // txtProductID
   397             //
   398             this.txtProductID.Location = new System.Drawing.Point(120, 56);
   399             this.txtProductID.Name = "txtProductID";
   400             this.txtProductID.Size = new System.Drawing.Size(72, 20);
   401             this.txtProductID.TabIndex = 3;
   402             this.txtProductID.Text = "1299";
   403             this.txtProductID.TextChanged += new System.EventHandler(this.txtProductID_TextChanged);
   404             //
   405             // lblProductID
   406             //
   407             this.lblProductID.Location = new System.Drawing.Point(16, 56);
   408             this.lblProductID.Name = "lblProductID";
   409             this.lblProductID.Size = new System.Drawing.Size(112, 23);
   410             this.lblProductID.TabIndex = 2;
   411             this.lblProductID.Text = "Product ID (hex):";
   412             //
   413             // txtVendorID
   414             //
   415             this.txtVendorID.Location = new System.Drawing.Point(120, 24);
   416             this.txtVendorID.Name = "txtVendorID";
   417             this.txtVendorID.Size = new System.Drawing.Size(72, 20);
   418             this.txtVendorID.TabIndex = 1;
   419             this.txtVendorID.Text = "0925";
   420             this.txtVendorID.TextChanged += new System.EventHandler(this.txtVendorID_TextChanged);
   421             //
   422             // lblVendorID
   423             //
   424             this.lblVendorID.Location = new System.Drawing.Point(16, 24);
   425             this.lblVendorID.Name = "lblVendorID";
   426             this.lblVendorID.Size = new System.Drawing.Size(112, 23);
   427             this.lblVendorID.TabIndex = 0;
   428             this.lblVendorID.Text = "Vendor ID (hex):";
   429             //
   430             // cmdFindDevice
   431             //
   432             this.cmdFindDevice.Location = new System.Drawing.Point(636, 12);
   433             this.cmdFindDevice.Name = "cmdFindDevice";
   434             this.cmdFindDevice.Size = new System.Drawing.Size(136, 26);
   435             this.cmdFindDevice.TabIndex = 11;
   436             this.cmdFindDevice.Text = "Find My Device";
   437             this.cmdFindDevice.Click += new System.EventHandler(this.cmdFindDevice_Click);
   438             //
   439             // cmdSendOutputReportInterrupt
   440             //
   441             this.cmdSendOutputReportInterrupt.Location = new System.Drawing.Point(21, 27);
   442             this.cmdSendOutputReportInterrupt.Name = "cmdSendOutputReportInterrupt";
   443             this.cmdSendOutputReportInterrupt.Size = new System.Drawing.Size(118, 26);
   444             this.cmdSendOutputReportInterrupt.TabIndex = 12;
   445             this.cmdSendOutputReportInterrupt.Text = "Send Output Report";
   446             this.cmdSendOutputReportInterrupt.UseVisualStyleBackColor = true;
   447             this.cmdSendOutputReportInterrupt.Click += new System.EventHandler(this.cmdSendOutputReportInterrupt_Click);
   448             //
   449             // cmdGetInputReportInterrupt
   450             //
   451             this.cmdGetInputReportInterrupt.Location = new System.Drawing.Point(21, 60);
   452             this.cmdGetInputReportInterrupt.Name = "cmdGetInputReportInterrupt";
   453             this.cmdGetInputReportInterrupt.Size = new System.Drawing.Size(118, 26);
   454             this.cmdGetInputReportInterrupt.TabIndex = 13;
   455             this.cmdGetInputReportInterrupt.Text = "Get Input Report";
   456             this.cmdGetInputReportInterrupt.UseVisualStyleBackColor = true;
   457             this.cmdGetInputReportInterrupt.Click += new System.EventHandler(this.cmdGetInputReportInterrupt_Click);
   458             //
   459             // fraInterruptTransfers
   460             //
   461             this.fraInterruptTransfers.BackColor = System.Drawing.SystemColors.Control;
   462             this.fraInterruptTransfers.Controls.Add(this.cmdSendOutputReportInterrupt);
   463             this.fraInterruptTransfers.Controls.Add(this.cmdGetInputReportInterrupt);
   464             this.fraInterruptTransfers.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
   465             this.fraInterruptTransfers.ForeColor = System.Drawing.SystemColors.ControlText;
   466             this.fraInterruptTransfers.Location = new System.Drawing.Point(338, 129);
   467             this.fraInterruptTransfers.Name = "fraInterruptTransfers";
   468             this.fraInterruptTransfers.RightToLeft = System.Windows.Forms.RightToLeft.No;
   469             this.fraInterruptTransfers.Size = new System.Drawing.Size(151, 100);
   470             this.fraInterruptTransfers.TabIndex = 14;
   471             this.fraInterruptTransfers.TabStop = false;
   472             this.fraInterruptTransfers.Text = "Interrupt Transfers";
   473             //
   474             // cmdPeriodicTransfers
   475             //
   476             this.cmdPeriodicTransfers.Location = new System.Drawing.Point(153, 36);
   477             this.cmdPeriodicTransfers.Name = "cmdPeriodicTransfers";
   478             this.cmdPeriodicTransfers.Size = new System.Drawing.Size(118, 26);
   479             this.cmdPeriodicTransfers.TabIndex = 16;
   480             this.cmdPeriodicTransfers.Text = "Start";
   481             this.cmdPeriodicTransfers.UseVisualStyleBackColor = true;
   482             this.cmdPeriodicTransfers.Click += new System.EventHandler(this.cmdPeriodicTransfers_Click);
   483             //
   484             // cmdSendOutputReportControl
   485             //
   486             this.cmdSendOutputReportControl.Location = new System.Drawing.Point(10, 27);
   487             this.cmdSendOutputReportControl.Name = "cmdSendOutputReportControl";
   488             this.cmdSendOutputReportControl.Size = new System.Drawing.Size(118, 26);
   489             this.cmdSendOutputReportControl.TabIndex = 12;
   490             this.cmdSendOutputReportControl.Text = "Send Output Report";
   491             this.cmdSendOutputReportControl.UseVisualStyleBackColor = true;
   492             this.cmdSendOutputReportControl.Click += new System.EventHandler(this.cmdSendOutputReportControl_Click);
   493             //
   494             // cmdGetInputReportControl
   495             //
   496             this.cmdGetInputReportControl.Location = new System.Drawing.Point(10, 60);
   497             this.cmdGetInputReportControl.Name = "cmdGetInputReportControl";
   498             this.cmdGetInputReportControl.Size = new System.Drawing.Size(118, 26);
   499             this.cmdGetInputReportControl.TabIndex = 13;
   500             this.cmdGetInputReportControl.Text = "Get Input Report";
   501             this.cmdGetInputReportControl.UseVisualStyleBackColor = true;
   502             this.cmdGetInputReportControl.Click += new System.EventHandler(this.cmdGetInputReportControl_Click);
   503             //
   504             // fraControlTransfers
   505             //
   506             this.fraControlTransfers.BackColor = System.Drawing.SystemColors.Control;
   507             this.fraControlTransfers.Controls.Add(this.cmdGetFeatureReport);
   508             this.fraControlTransfers.Controls.Add(this.cmdSendFeatureReport);
   509             this.fraControlTransfers.Controls.Add(this.cmdSendOutputReportControl);
   510             this.fraControlTransfers.Controls.Add(this.cmdGetInputReportControl);
   511             this.fraControlTransfers.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
   512             this.fraControlTransfers.ForeColor = System.Drawing.SystemColors.ControlText;
   513             this.fraControlTransfers.Location = new System.Drawing.Point(495, 129);
   514             this.fraControlTransfers.Name = "fraControlTransfers";
   515             this.fraControlTransfers.RightToLeft = System.Windows.Forms.RightToLeft.No;
   516             this.fraControlTransfers.Size = new System.Drawing.Size(277, 100);
   517             this.fraControlTransfers.TabIndex = 15;
   518             this.fraControlTransfers.TabStop = false;
   519             this.fraControlTransfers.Text = "Control Transfers";
   520             //
   521             // cmdGetFeatureReport
   522             //
   523             this.cmdGetFeatureReport.Location = new System.Drawing.Point(141, 60);
   524             this.cmdGetFeatureReport.Name = "cmdGetFeatureReport";
   525             this.cmdGetFeatureReport.Size = new System.Drawing.Size(118, 26);
   526             this.cmdGetFeatureReport.TabIndex = 15;
   527             this.cmdGetFeatureReport.Text = "Get Feature Report";
   528             this.cmdGetFeatureReport.UseVisualStyleBackColor = true;
   529             this.cmdGetFeatureReport.Click += new System.EventHandler(this.cmdGetFeatureReport_Click);
   530             //
   531             // cmdSendFeatureReport
   532             //
   533             this.cmdSendFeatureReport.Location = new System.Drawing.Point(141, 27);
   534             this.cmdSendFeatureReport.Name = "cmdSendFeatureReport";
   535             this.cmdSendFeatureReport.Size = new System.Drawing.Size(118, 26);
   536             this.cmdSendFeatureReport.TabIndex = 14;
   537             this.cmdSendFeatureReport.Text = "Send Feature Report";
   538             this.cmdSendFeatureReport.UseVisualStyleBackColor = true;
   539             this.cmdSendFeatureReport.Click += new System.EventHandler(this.cmdSendFeatureReport_Click);
   540             //
   541             // fraSendAndGetContinuous
   542             //
   543             this.fraSendAndGetContinuous.BackColor = System.Drawing.SystemColors.Control;
   544             this.fraSendAndGetContinuous.Controls.Add(this.radFeature);
   545             this.fraSendAndGetContinuous.Controls.Add(this.radInputOutputControl);
   546             this.fraSendAndGetContinuous.Controls.Add(this.radInputOutputInterrupt);
   547             this.fraSendAndGetContinuous.Controls.Add(this.cmdPeriodicTransfers);
   548             this.fraSendAndGetContinuous.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
   549             this.fraSendAndGetContinuous.ForeColor = System.Drawing.SystemColors.ControlText;
   550             this.fraSendAndGetContinuous.Location = new System.Drawing.Point(311, 235);
   551             this.fraSendAndGetContinuous.Name = "fraSendAndGetContinuous";
   552             this.fraSendAndGetContinuous.RightToLeft = System.Windows.Forms.RightToLeft.No;
   553             this.fraSendAndGetContinuous.Size = new System.Drawing.Size(295, 112);
   554             this.fraSendAndGetContinuous.TabIndex = 17;
   555             this.fraSendAndGetContinuous.TabStop = false;
   556             this.fraSendAndGetContinuous.Text = "Send and Get Continuous";
   557             //
   558             // radFeature
   559             //
   560             this.radFeature.AutoSize = true;
   561             this.radFeature.Location = new System.Drawing.Point(17, 76);
   562             this.radFeature.Name = "radFeature";
   563             this.radFeature.Size = new System.Drawing.Size(62, 18);
   564             this.radFeature.TabIndex = 19;
   565             this.radFeature.TabStop = true;
   566             this.radFeature.Text = "Feature";
   567             this.radFeature.UseVisualStyleBackColor = true;
   568             this.radFeature.CheckedChanged += new System.EventHandler(this.radFeature_CheckedChanged);
   569             //
   570             // radInputOutputControl
   571             //
   572             this.radInputOutputControl.AutoSize = true;
   573             this.radInputOutputControl.Location = new System.Drawing.Point(17, 52);
   574             this.radInputOutputControl.Name = "radInputOutputControl";
   575             this.radInputOutputControl.Size = new System.Drawing.Size(120, 18);
   576             this.radInputOutputControl.TabIndex = 18;
   577             this.radInputOutputControl.TabStop = true;
   578             this.radInputOutputControl.Text = "Input Output Control";
   579             this.radInputOutputControl.UseVisualStyleBackColor = true;
   580             this.radInputOutputControl.CheckedChanged += new System.EventHandler(this.radInputOutputControl_CheckedChanged);
   581             //
   582             // radInputOutputInterrupt
   583             //
   584             this.radInputOutputInterrupt.AutoSize = true;
   585             this.radInputOutputInterrupt.Location = new System.Drawing.Point(17, 28);
   586             this.radInputOutputInterrupt.Name = "radInputOutputInterrupt";
   587             this.radInputOutputInterrupt.Size = new System.Drawing.Size(126, 18);
   588             this.radInputOutputInterrupt.TabIndex = 17;
   589             this.radInputOutputInterrupt.TabStop = true;
   590             this.radInputOutputInterrupt.Text = "Input Output Interrupt";
   591             this.radInputOutputInterrupt.UseVisualStyleBackColor = true;
   592             this.radInputOutputInterrupt.CheckedChanged += new System.EventHandler(this.radInputOutputInterrupt_CheckedChanged);
   593             //
   594             // treeViewDevices
   595             //
   596             this.treeViewDevices.Location = new System.Drawing.Point(12, 12);
   597             this.treeViewDevices.Name = "treeViewDevices";
   598             this.treeViewDevices.Size = new System.Drawing.Size(284, 461);
   599             this.treeViewDevices.TabIndex = 18;
   600             //
   601             // FrmMain
   602             //
   603             this.ClientSize = new System.Drawing.Size(784, 756);
   604             this.Controls.Add(this.treeViewDevices);
   605             this.Controls.Add(this.fraSendAndGetContinuous);
   606             this.Controls.Add(this.fraControlTransfers);
   607             this.Controls.Add(this.fraInterruptTransfers);
   608             this.Controls.Add(this.cmdFindDevice);
   609             this.Controls.Add(this.fraDeviceIdentifiers);
   610             this.Controls.Add(this.fraInputReportBufferSize);
   611             this.Controls.Add(this.FraBytesReceived);
   612             this.Controls.Add(this.FraBytesToSend);
   613             this.Controls.Add(this.LstResults);
   614             this.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
   615             this.Location = new System.Drawing.Point(21, 28);
   616             this.Name = "FrmMain";
   617             this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
   618             this.Text = "Generic HID Tester";
   619             this.Closed += new System.EventHandler(this.frmMain_Closed);
   620             this.Load += new System.EventHandler(this.frmMain_Load);
   621             this.FraBytesReceived.ResumeLayout(false);
   622             this.FraBytesReceived.PerformLayout();
   623             this.FraBytesToSend.ResumeLayout(false);
   624             this.fraInputReportBufferSize.ResumeLayout(false);
   625             this.fraInputReportBufferSize.PerformLayout();
   626             this.fraDeviceIdentifiers.ResumeLayout(false);
   627             this.fraDeviceIdentifiers.PerformLayout();
   628             this.fraInterruptTransfers.ResumeLayout(false);
   629             this.fraControlTransfers.ResumeLayout(false);
   630             this.fraSendAndGetContinuous.ResumeLayout(false);
   631             this.fraSendAndGetContinuous.PerformLayout();
   632             this.ResumeLayout(false);
   633 
   634 		}
   635 		#endregion '"Windows Form Designer generated code "'
   636 
   637 		private Boolean _deviceDetected;
   638 		private IntPtr _deviceNotificationHandle;
   639 		private FileStream _deviceData;
   640 		private FormActions _formActions;
   641 		private SafeFileHandle _hidHandle;
   642 		private String _hidUsage;
   643 		private ManagementEventWatcher _deviceArrivedWatcher;
   644 		private Boolean _deviceHandleObtained;
   645 		private ManagementEventWatcher _deviceRemovedWatcher;
   646 		private Int32 _myProductId;
   647 		private Int32 _myVendorId;
   648 		private Boolean _periodicTransfersRequested;
   649 		private ReportReadOrWritten _readOrWritten;
   650 		private ReportTypes _reportType;
   651 		private SendOrGet _sendOrGet;
   652 		private Boolean _transferInProgress;
   653 		private TransferTypes _transferType;
   654 
   655 		private static System.Timers.Timer _periodicTransfers;
   656 
   657 		private readonly Debugging _myDebugging = new Debugging(); //  For viewing results of API calls via Debug.Write.
   658 		private readonly DeviceManagement _myDeviceManagement = new DeviceManagement();
   659 		private Hid _myHid = new Hid();
   660 
   661 		private enum FormActions
   662 		{
   663 			AddItemToListBox,
   664 			DisableInputReportBufferSize,
   665 			EnableGetInputReportInterruptTransfer,
   666 			EnableInputReportBufferSize,
   667 			EnableSendOutputReportInterrupt,
   668 			ScrollToBottomOfListBox,
   669 			SetInputReportBufferSize,
   670             AddDeviceToTreeView,
   671             ResetDeviceTreeView,
   672             SelectDeviceInTreeView,
   673             CompleteDeviceTreeView
   674 		}
   675 
   676 		private enum ReportReadOrWritten
   677 		{
   678 			Read,
   679 			Written
   680 		}
   681 
   682 		private enum ReportTypes
   683 		{
   684 			Input,
   685 			Output,
   686 			Feature
   687 		}
   688 
   689 		private enum SendOrGet
   690 		{
   691 			Send,
   692 			Get
   693 		}
   694 
   695 		private enum TransferTypes
   696 		{
   697 			Control,
   698 			Interrupt
   699 		}
   700 
   701 		private enum WmiDeviceProperties
   702 		{
   703 			Name,
   704 			Caption,
   705 			Description,
   706 			Manufacturer,
   707 			PNPDeviceID,
   708 			DeviceID,
   709 			ClassGUID
   710 		}
   711 
   712 		internal FrmMain FrmMy;
   713 
   714 		//  This delegate has the same parameters as AccessForm.
   715 		//  Used in accessing the application's form from a different thread.
   716 
   717         private delegate void MarshalDataToForm(FormActions action, params string[] strings);
   718 
   719 		///  <summary>
   720 		///  Performs various application-specific functions that
   721 		///  involve accessing the application's form.
   722 		///  </summary>
   723 		///
   724 		///  <param name="action"> a FormActions member that names the action to perform on the form</param>
   725 		///  <param name="formText"> text that the form displays or the code uses for
   726 		///  another purpose. Actions that don't use text ignore this parameter. </param>
   727 
   728 		private void AccessForm(FormActions action, params string[] strings)
   729 		{
   730 			try
   731 			{
   732 				//  Select an action to perform on the form:
   733 
   734 				switch (action)
   735 				{
   736 					case FormActions.AddItemToListBox:
   737 
   738                         LstResults.Items.Add(strings[0]);
   739 						break;
   740 
   741 					case FormActions.DisableInputReportBufferSize:
   742 
   743 						cmdInputReportBufferSize.Enabled = false;
   744 						break;
   745 
   746 					case FormActions.EnableGetInputReportInterruptTransfer:
   747 
   748 						cmdGetInputReportInterrupt.Enabled = true;
   749 						break;
   750 
   751 					case FormActions.EnableInputReportBufferSize:
   752 
   753 						cmdInputReportBufferSize.Enabled = true;
   754 						break;
   755 
   756 					case FormActions.EnableSendOutputReportInterrupt:
   757 
   758 						cmdSendOutputReportInterrupt.Enabled = true;
   759 						break;
   760 
   761 					case FormActions.ScrollToBottomOfListBox:
   762 
   763 						LstResults.SelectedIndex = LstResults.Items.Count - 1;
   764 						break;
   765 
   766 					case FormActions.SetInputReportBufferSize:
   767 
   768                         txtInputReportBufferSize.Text = strings[0];
   769 						break;
   770 
   771                     case FormActions.AddDeviceToTreeView:
   772                         {
   773                             //Try and see if our device is already present
   774                             TreeNode[] res = treeViewDevices.Nodes.Find(strings[0], false);
   775                             foreach (TreeNode device in res)
   776                             {
   777                                 if (device.ForeColor == Color.Red)
   778                                 {
   779                                     //Device was removed and has now been added back
   780                                     device.ForeColor = Color.Green;
   781                                 }
   782                                 else
   783                                 {
   784                                     //Device was already there set back our device color to black
   785                                     device.ForeColor = Color.Black;
   786                                 }
   787                             }
   788 
   789                             if (res.Length > 0)
   790                             {
   791                                 //Our device is already there
   792                                 break;
   793                             }
   794 
   795                             //Build our node from our string array
   796                             TreeNode newNode = new TreeNode(strings[0]);
   797                             for (int i = 1; i < strings.Length; i++)
   798                             {
   799                                 newNode.Nodes.Add(strings[i]);
   800                                 if (strings[i].StartsWith("Name: "))
   801                                 {
   802                                     //Found our name property, update our node text
   803                                     newNode.Text = strings[i].Substring(6, strings[i].Length - 6);
   804                                 }
   805                             }
   806 
   807                             //New device color is green
   808                             newNode.ForeColor = Color.Green;
   809                             newNode.Name = strings[0]; //Set ID as name to make sure we can find it
   810                             treeViewDevices.Nodes.Add(newNode);
   811                         }
   812                         break;
   813 
   814                     case FormActions.ResetDeviceTreeView:
   815                         {
   816                             //Mark all non removed/red device as purple/unknown
   817                             foreach (TreeNode device in treeViewDevices.Nodes)
   818                             {
   819                                 if (device.ForeColor != Color.Red)
   820                                 {
   821                                     device.ForeColor = Color.Purple;
   822                                 }
   823                             }
   824                             //treeViewDevices.Nodes.Clear();
   825                         }
   826                         break;
   827 
   828                     case FormActions.CompleteDeviceTreeView:
   829                         {
   830                             //Our device list is now complete
   831                             foreach (TreeNode device in treeViewDevices.Nodes)
   832                             {
   833                                 //Purple devices need to be marked as red for removed
   834                                 if (device.ForeColor == Color.Purple)
   835                                 {
   836                                     device.ForeColor = Color.Red;
   837                                 }
   838                             }
   839                         }
   840                         break;
   841 
   842                     case FormActions.SelectDeviceInTreeView:
   843                         {
   844                             //treeViewDevices.SelectedNode = null;
   845                             TreeNode[] res = treeViewDevices.Nodes.Find(strings[0], false);
   846                             foreach (TreeNode device in res)
   847                             {
   848                                 device.ForeColor = Color.Blue;
   849                                 //treeViewDevices.SelectedNode = res[0];
   850                                 //treeViewDevices.SelectedNode.ForeColor = Color.Blue;
   851                             }
   852                         }
   853 
   854 
   855                         break;
   856 				}
   857 			}
   858 			catch (Exception ex)
   859 			{
   860 				DisplayException(Name, ex);
   861 				throw;
   862 			}
   863 		}
   864 
   865 		///  <summary>
   866 		///  Add a handler to detect arrival of devices using WMI.
   867 		///  </summary>
   868 
   869 		private void AddDeviceArrivedHandler()
   870 		{
   871 			const Int32 pollingIntervalSeconds = 3;
   872 			var scope = new ManagementScope("root\\CIMV2");
   873 			scope.Options.EnablePrivileges = true;
   874 
   875 			try
   876 			{
   877 				var q = new WqlEventQuery();
   878 				q.EventClassName = "__InstanceCreationEvent";
   879 				q.WithinInterval = new TimeSpan(0, 0, pollingIntervalSeconds);
   880 				q.Condition = @"TargetInstance ISA 'Win32_USBControllerdevice'";
   881 				_deviceArrivedWatcher = new ManagementEventWatcher(scope, q);
   882 				_deviceArrivedWatcher.EventArrived += DeviceAdded;
   883 
   884 				_deviceArrivedWatcher.Start();
   885 			}
   886 			catch (Exception e)
   887 			{
   888 				Debug.WriteLine(e.Message);
   889 				if (_deviceArrivedWatcher != null)
   890 					_deviceArrivedWatcher.Stop();
   891 			}
   892 		}
   893 
   894 		///  <summary>
   895 		///  Add a handler to detect removal of devices using WMI.
   896 		///  </summary>
   897 
   898 		private void AddDeviceRemovedHandler()
   899 		{
   900 			const Int32 pollingIntervalSeconds = 3;
   901 			var scope = new ManagementScope("root\\CIMV2");
   902 			scope.Options.EnablePrivileges = true;
   903 
   904 			try
   905 			{
   906 				var q = new WqlEventQuery();
   907 				q.EventClassName = "__InstanceDeletionEvent";
   908 				q.WithinInterval = new TimeSpan(0, 0, pollingIntervalSeconds);
   909 				q.Condition = @"TargetInstance ISA 'Win32_USBControllerdevice'";
   910 				_deviceRemovedWatcher = new ManagementEventWatcher(scope, q);
   911 				_deviceRemovedWatcher.EventArrived += DeviceRemoved;
   912 				_deviceRemovedWatcher.Start();
   913 			}
   914 			catch (Exception e)
   915 			{
   916 				Debug.WriteLine(e.Message);
   917 				if (_deviceRemovedWatcher != null)
   918 					_deviceRemovedWatcher.Stop();
   919 			}
   920 		}
   921 
   922 		/// <summary>
   923 		/// Close the handle and FileStreams for a device.
   924 		/// </summary>
   925 		///
   926 		private void CloseCommunications()
   927 		{
   928 			if (_deviceData != null)
   929 			{
   930 				_deviceData.Close();
   931 			}
   932 
   933 			if ((_hidHandle != null) && (!(_hidHandle.IsInvalid)))
   934 			{
   935 				_hidHandle.Close();
   936 			}
   937 
   938 			// The next attempt to communicate will get a new handle and FileStreams.
   939 
   940 			_deviceHandleObtained = false;
   941 		}
   942 
   943 		///  <summary>
   944 		///  Search for a specific device.
   945 		///  </summary>
   946 
   947 		private void cmdFindDevice_Click(Object sender, EventArgs e)
   948 		{
   949 			try
   950 			{
   951 				if (_transferInProgress)
   952 				{
   953 					DisplayTransferInProgressMessage();
   954 				}
   955 				else
   956 				{
   957 					_deviceDetected = FindDeviceUsingWmi();
   958 					if (_deviceDetected)
   959 					{
   960 						FindTheHid();
   961 					}
   962 				}
   963 			}
   964 			catch (Exception ex)
   965 			{
   966 				DisplayException(Name, ex);
   967 				throw;
   968 			}
   969 		}
   970 
   971 		/// <summary>
   972 		/// Request to get a Feature report from the device.
   973 		/// </summary>
   974 		/// <param name="sender"></param>
   975 		/// <param name="e"></param>
   976 
   977 		private void cmdGetFeatureReport_Click(object sender, EventArgs e)
   978 		{
   979 			try
   980 			{
   981 				if (_transferInProgress)
   982 				{
   983 					DisplayTransferInProgressMessage();
   984 				}
   985 				else
   986 				{
   987 					//  Don't allow another transfer request until this one completes.
   988 					//  Move the focus away from the button to prevent the focus from
   989 					//  switching to the next control in the tab order on disabling the button.
   990 
   991 					fraControlTransfers.Focus();
   992 					cmdGetFeatureReport.Enabled = false;
   993 					_transferType = TransferTypes.Control;
   994 					RequestToGetFeatureReport();
   995 				}
   996 			}
   997 			catch (Exception ex)
   998 			{
   999 				DisplayException(Name, ex);
  1000 				throw;
  1001 			}
  1002 		}
  1003 
  1004 		/// <summary>
  1005 		/// Request to get an Input report from the device using a control transfer.
  1006 		/// </summary>
  1007 		/// <param name="sender"></param>
  1008 		/// <param name="e"></param>
  1009 
  1010 		private void cmdGetInputReportControl_Click(object sender, EventArgs e)
  1011 		{
  1012 			try
  1013 			{
  1014 				//  Don't allow another transfer request until this one completes.
  1015 				//  Move the focus away from the button to prevent the focus from
  1016 				//  switching to the next control in the tab order on disabling the button.
  1017 
  1018 				if (_transferInProgress)
  1019 				{
  1020 					DisplayTransferInProgressMessage();
  1021 				}
  1022 				else
  1023 				{
  1024 					fraControlTransfers.Focus();
  1025 					cmdGetInputReportControl.Enabled = false;
  1026 					_transferType = TransferTypes.Control;
  1027 					RequestToGetInputReport();
  1028 				}
  1029 			}
  1030 			catch (Exception ex)
  1031 			{
  1032 				DisplayException(Name, ex);
  1033 				throw;
  1034 			}
  1035 		}
  1036 
  1037 		/// <summary>
  1038 		/// Request to get an Input report retrieved using interrupt transfers.
  1039 		/// </summary>
  1040 		/// <param name="sender"></param>
  1041 		/// <param name="e"></param>
  1042 		///
  1043 		private void cmdGetInputReportInterrupt_Click(object sender, EventArgs e)
  1044 		{
  1045 			try
  1046 			{
  1047 				if (_transferInProgress)
  1048 				{
  1049 					DisplayTransferInProgressMessage();
  1050 				}
  1051 				else
  1052 				{
  1053 					//  Don't allow another transfer request until this one completes.
  1054 					//  Move the focus away from the button to prevent the focus from
  1055 					//  switching to the next control in the tab order on disabling the button.
  1056 
  1057 					fraInterruptTransfers.Focus();
  1058 					cmdGetInputReportInterrupt.Enabled = false;
  1059 					_transferType = TransferTypes.Interrupt;
  1060 					RequestToGetInputReport();
  1061 				}
  1062 			}
  1063 			catch (Exception ex)
  1064 			{
  1065 				DisplayException(Name, ex);
  1066 				throw;
  1067 			}
  1068 		}
  1069 
  1070 		///  <summary>
  1071 		///  Set the number of Input reports the HID driver will store.
  1072 		///  </summary>
  1073 
  1074 		private void cmdInputReportBufferSize_Click(Object sender, EventArgs e)
  1075 		{
  1076 			try
  1077 			{
  1078 				if (_transferInProgress)
  1079 				{
  1080 					DisplayTransferInProgressMessage();
  1081 				}
  1082 				else
  1083 				{
  1084 					SetInputReportBufferSize();
  1085 				}
  1086 			}
  1087 			catch
  1088 				(Exception ex)
  1089 			{
  1090 				DisplayException(Name, ex);
  1091 				throw;
  1092 			}
  1093 		}
  1094 
  1095 		/// <summary>
  1096 		/// Alternate sending and getting a report.
  1097 		/// </summary>
  1098 		/// <param name="sender"></param>
  1099 		/// <param name="e"></param>
  1100 
  1101 		private void cmdPeriodicTransfers_Click(object sender, EventArgs e)
  1102 		{
  1103 			try
  1104 			{
  1105 				if (cmdPeriodicTransfers.Text == "Start")
  1106 				{
  1107 					if (_transferInProgress)
  1108 					{
  1109 						DisplayTransferInProgressMessage();
  1110 					}
  1111 					else
  1112 					{
  1113 						_sendOrGet = SendOrGet.Send;
  1114 						PeriodicTransfersStart();
  1115 					}
  1116 				}
  1117 				else
  1118 				{
  1119 					PeriodicTransfersStop();
  1120 				}
  1121 			}
  1122 			catch (Exception ex)
  1123 			{
  1124 				DisplayException(Name, ex);
  1125 				throw;
  1126 			}
  1127 		}
  1128 
  1129 		/// <summary>
  1130 		/// Request to send a Feature report using a control transfer.
  1131 		/// </summary>
  1132 		/// <param name="sender"></param>
  1133 		/// <param name="e"></param>
  1134 
  1135 		private void cmdSendFeatureReport_Click(object sender, EventArgs e)
  1136 		{
  1137 			try
  1138 			{
  1139 				if (_transferInProgress)
  1140 				{
  1141 					DisplayTransferInProgressMessage();
  1142 				}
  1143 				else
  1144 				{
  1145 					//  Don't allow another transfer request until this one completes.
  1146 					//  Move the focus away from the button to prevent the focus from
  1147 					//  switching to the next control in the tab order on disabling the button.
  1148 
  1149 					fraControlTransfers.Focus();
  1150 					cmdSendFeatureReport.Enabled = false;
  1151 					_transferType = TransferTypes.Control;
  1152 					RequestToSendFeatureReport();
  1153 				}
  1154 			}
  1155 			catch (Exception ex)
  1156 			{
  1157 				DisplayException(Name, ex);
  1158 				throw;
  1159 			}
  1160 		}
  1161 
  1162 		/// <summary>
  1163 		/// Request to send an Output report using a control transfer.
  1164 		/// </summary>
  1165 		/// <param name="sender"></param>
  1166 		/// <param name="e"></param>
  1167 		///
  1168 		private void cmdSendOutputReportControl_Click(object sender, EventArgs e)
  1169 		{
  1170 			try
  1171 			{
  1172 				if (_transferInProgress)
  1173 				{
  1174 					DisplayTransferInProgressMessage();
  1175 				}
  1176 				else
  1177 				{
  1178 					//  Don't allow another transfer request until this one completes.
  1179 					//  Move the focus away from the button to prevent the focus from
  1180 					//  switching to the next control in the tab order on disabling the button.
  1181 
  1182 					fraControlTransfers.Focus();
  1183 					cmdSendOutputReportControl.Enabled = false;
  1184 					_transferType = TransferTypes.Control;
  1185 					RequestToSendOutputReport();
  1186 				}
  1187 			}
  1188 			catch (Exception ex)
  1189 			{
  1190 				DisplayException(Name, ex);
  1191 				throw;
  1192 			}
  1193 		}
  1194 
  1195 		/// <summary>
  1196 		/// Request to send an Output report using an interrupt transfer.
  1197 		/// </summary>
  1198 		/// <param name="sender"></param>
  1199 		/// <param name="e"></param>
  1200 
  1201 		private void cmdSendOutputReportInterrupt_Click(object sender, EventArgs e)
  1202 		{
  1203 			try
  1204 			{
  1205 				if (_transferInProgress)
  1206 				{
  1207 					DisplayTransferInProgressMessage();
  1208 				}
  1209 				else
  1210 				{
  1211 					//  Don't allow another transfer request until this one completes.
  1212 					//  Move the focus away from the button to prevent the focus from
  1213 					//  switching to the next control in the tab order on disabling the button.
  1214 
  1215 					fraInterruptTransfers.Focus();
  1216 					cmdSendOutputReportInterrupt.Enabled = false;
  1217 					_transferType = TransferTypes.Interrupt;
  1218 					RequestToSendOutputReport();
  1219 				}
  1220 			}
  1221 			catch (Exception ex)
  1222 			{
  1223 				DisplayException(Name, ex);
  1224 				throw;
  1225 			}
  1226 		}
  1227 
  1228 		///  <summary>
  1229 		///  Called on arrival of any device.
  1230 		///  Calls a routine that searches to see if the desired device is present.
  1231 		///  </summary>
  1232 
  1233 		private void DeviceAdded(object sender, EventArrivedEventArgs e)
  1234 		{
  1235 			try
  1236 			{
  1237 				Debug.WriteLine("A USB device has been inserted");
  1238 
  1239 				_deviceDetected = FindDeviceUsingWmi();
  1240 			}
  1241 			catch (Exception ex)
  1242 			{
  1243 				DisplayException(Name, ex);
  1244 				throw;
  1245 			}
  1246 		}
  1247 
  1248 		///  <summary>
  1249 		///  Called if the user changes the Vendor ID or Product ID in the text box.
  1250 		///  </summary>
  1251 
  1252 		private void DeviceHasChanged()
  1253 		{
  1254 			try
  1255 			{
  1256 				//  If a device was previously detected, stop receiving notifications about it.
  1257 
  1258 				if (_deviceHandleObtained)
  1259 				{
  1260 					DeviceNotificationsStop();
  1261 
  1262 					CloseCommunications();
  1263 				}
  1264 				// Look for a device that matches the Vendor ID and Product ID in the text boxes.
  1265 
  1266 				FindTheHid();
  1267 			}
  1268 			catch (Exception ex)
  1269 			{
  1270 				DisplayException(Name, ex);
  1271 				throw;
  1272 			}
  1273 		}
  1274 
  1275 		///  <summary>
  1276 		///  Add handlers to detect device arrival and removal.
  1277 		///  </summary>
  1278 
  1279 		private void DeviceNotificationsStart()
  1280 		{
  1281 			AddDeviceArrivedHandler();
  1282 			AddDeviceRemovedHandler();
  1283 		}
  1284 
  1285 		///  <summary>
  1286 		///  Stop receiving notifications about device arrival and removal
  1287 		///  </summary>
  1288 
  1289 		private void DeviceNotificationsStop()
  1290 		{
  1291 			try
  1292 			{
  1293 				if (_deviceArrivedWatcher != null)
  1294 					_deviceArrivedWatcher.Stop();
  1295 				if (_deviceRemovedWatcher != null)
  1296 					_deviceRemovedWatcher.Stop();
  1297 			}
  1298 			catch (Exception ex)
  1299 			{
  1300 				DisplayException(Name, ex);
  1301 				throw;
  1302 			}
  1303 		}
  1304 
  1305 		///  <summary>
  1306 		///  Called on removal of any device.
  1307 		///  Calls a routine that searches to see if the desired device is still present.
  1308 		///  </summary>
  1309 		///
  1310 		private void DeviceRemoved(object sender, EventArgs e)
  1311 		{
  1312 			try
  1313 			{
  1314 				Debug.WriteLine("A USB device has been removed");
  1315 
  1316 				_deviceDetected = FindDeviceUsingWmi();
  1317 
  1318 				if (!_deviceDetected)
  1319 				{
  1320 					_deviceHandleObtained = false;
  1321 					CloseCommunications();
  1322 				}
  1323 			}
  1324 			catch (Exception ex)
  1325 			{
  1326 				DisplayException(Name, ex);
  1327 				throw;
  1328 			}
  1329 		}
  1330 
  1331 		///  <summary>
  1332 		///  Displays received or written report data.
  1333 		///  </summary>
  1334 		///
  1335 		///  <param name="buffer"> contains the report data. </param>
  1336 		///  <param name="currentReportType" > "Input", "Output", or "Feature"</param>
  1337 		///  <param name="currentReadOrWritten" > "read" for Input and IN Feature reports, "written" for Output and OUT Feature reports.</param>
  1338 
  1339 		private void DisplayReportData(Byte[] buffer, ReportTypes currentReportType, ReportReadOrWritten currentReadOrWritten)
  1340 		{
  1341 			try
  1342 			{
  1343 				Int32 count;
  1344 
  1345 				LstResults.Items.Add(currentReportType.ToString() + " report has been " + currentReadOrWritten.ToString().ToLower() + ".");
  1346 
  1347 				//  Display the report data received in the form's list box.
  1348 
  1349 				LstResults.Items.Add(" Report ID: " + String.Format("{0:X2} ", buffer[0]));
  1350 				LstResults.Items.Add(" Report Data:");
  1351 
  1352 				TxtBytesReceived.Text = "";
  1353 
  1354 				for (count = 1; count <= buffer.Length - 1; count++)
  1355 				{
  1356 					//  Display bytes as 2-character Hex strings.
  1357 
  1358 					String byteValue = String.Format("{0:X2} ", buffer[count]);
  1359 
  1360 					LstResults.Items.Add(" " + byteValue);
  1361 
  1362 					//  Display the received bytes in the text box.
  1363 
  1364 					TxtBytesReceived.SelectionStart = TxtBytesReceived.Text.Length;
  1365 					TxtBytesReceived.SelectedText = byteValue + Environment.NewLine;
  1366 				}
  1367 				ScrollToBottomOfListBox();
  1368 			}
  1369 			catch (Exception ex)
  1370 			{
  1371 				DisplayException(Name, ex);
  1372 				throw;
  1373 			}
  1374 		}
  1375 
  1376 		///  <summary>
  1377 		///  Display a message if the user clicks a button when a transfer is in progress.
  1378 		///  </summary>
  1379 		///
  1380 		private void DisplayTransferInProgressMessage()
  1381 		{
  1382 			AccessForm(FormActions.AddItemToListBox, "Command not executed because a transfer is in progress.");
  1383 			ScrollToBottomOfListBox();
  1384 		}
  1385 
  1386 		///  <summary>
  1387 		///  Do periodic transfers.
  1388 		///  </summary>
  1389 		/// <param name="source"></param>
  1390 		/// <param name="e"></param>
  1391 		///  <remarks>
  1392 		///  The timer is enabled only if continuous (periodic) transfers have been requested.
  1393 		///  </remarks>
  1394 
  1395 		private void DoPeriodicTransfers(object source, ElapsedEventArgs e)
  1396 		{
  1397 			try
  1398 			{
  1399 				PeriodicTransfers();
  1400 			}
  1401 			catch (Exception ex)
  1402 			{
  1403 				DisplayException(Name, ex);
  1404 				throw;
  1405 			}
  1406 		}
  1407 
  1408 		/// <summary>
  1409 		/// Enable the command buttons on the form.
  1410 		/// Needed after attempting a transfer and device not found.
  1411 		/// </summary>
  1412 		///
  1413 		private void EnableFormControls()
  1414 		{
  1415 			cmdGetInputReportInterrupt.Enabled = true;
  1416 			cmdSendOutputReportControl.Enabled = true;
  1417 			cmdGetInputReportControl.Enabled = true;
  1418 			cmdGetFeatureReport.Enabled = true;
  1419 			cmdSendFeatureReport.Enabled = true;
  1420 			cmdPeriodicTransfers.Enabled = true;
  1421 			cmdSendOutputReportInterrupt.Enabled = true;
  1422 		}
  1423 
  1424 		///  <summary>
  1425 		///  Use the System.Management class to find a device by Vendor ID and Product ID using WMI. If found, display device properties.
  1426 		///  </summary>
  1427 		/// <remarks>
  1428 		/// During debugging, if you stop the firmware but leave the device attached, the device may still be detected as present
  1429 		/// but will be unable to communicate. The device will show up in Windows Device Manager as well.
  1430 		/// This situation is unlikely to occur with a final product.
  1431 		/// </remarks>
  1432 
  1433 		private Boolean FindDeviceUsingWmi()
  1434 		{
  1435 			try
  1436 			{
  1437                 MyMarshalDataToForm(FormActions.ResetDeviceTreeView);
  1438 				// Prepend "@" to string below to treat backslash as a normal character (not escape character):
  1439 
  1440 				String deviceIdString = @"USB\VID_" + _myVendorId.ToString("X4") + "&PID_" + _myProductId.ToString("X4");
  1441 
  1442 				_deviceDetected = false;
  1443 				var searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_PnPEntity");
  1444                 int usbDeviceCounter = 0;
  1445 
  1446 				foreach (ManagementObject queryObj in searcher.Get())
  1447 				{
  1448                     string deviceId = queryObj["PNPDeviceID"].ToString();
  1449                     if (deviceId.Contains(deviceIdString))
  1450                     {
  1451                         _deviceDetected = true;
  1452                         MyMarshalDataToForm(FormActions.AddItemToListBox, "My device found (WMI):");
  1453 
  1454                         MyMarshalDataToForm(FormActions.AddItemToListBox, "--------");
  1455                         List<string> device = new List<string>();
  1456                         device.Add(deviceId);
  1457                         usbDeviceCounter++;
  1458                         // Display device properties.
  1459                         foreach (WmiDeviceProperties wmiDeviceProperty in Enum.GetValues(typeof(WmiDeviceProperties)))
  1460                         {
  1461                             MyMarshalDataToForm(FormActions.AddItemToListBox, (wmiDeviceProperty.ToString() + ": " + queryObj[wmiDeviceProperty.ToString()]));
  1462                             device.Add((wmiDeviceProperty.ToString() + ": " + queryObj[wmiDeviceProperty.ToString()]));
  1463                             Debug.WriteLine(wmiDeviceProperty.ToString() + ": {0}", queryObj[wmiDeviceProperty.ToString()]);
  1464                         }
  1465                         MyMarshalDataToForm(FormActions.AddItemToListBox, "--------");
  1466                         MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
  1467 
  1468                         MyMarshalDataToForm(FormActions.AddDeviceToTreeView, device.ToArray());
  1469                         MyMarshalDataToForm(FormActions.SelectDeviceInTreeView, deviceId);
  1470 
  1471                     }
  1472                     else if (deviceId.StartsWith("USB\\VID"))
  1473                     {
  1474                         List<string> device = new List<string>();
  1475                         device.Add(deviceId);
  1476                         usbDeviceCounter++;
  1477                         // Add device properties.
  1478                         foreach (WmiDeviceProperties wmiDeviceProperty in Enum.GetValues(typeof(WmiDeviceProperties)))
  1479                         {
  1480                             device.Add((wmiDeviceProperty.ToString() + ": " + queryObj[wmiDeviceProperty.ToString()]));
  1481                             Debug.WriteLine(wmiDeviceProperty.ToString() + ": {0}", queryObj[wmiDeviceProperty.ToString()]);
  1482                         }
  1483 
  1484                         MyMarshalDataToForm(FormActions.AddDeviceToTreeView, device.ToArray());
  1485                     }
  1486 				}
  1487 
  1488                 //Complete our device TreeView
  1489                 MyMarshalDataToForm(FormActions.CompleteDeviceTreeView);
  1490 
  1491 
  1492                 MyMarshalDataToForm(FormActions.AddItemToListBox, "Found " + usbDeviceCounter /*searcher.Get().Count*/ + " USB HID devices");
  1493 
  1494 				if (!_deviceDetected)
  1495 				{
  1496 					MyMarshalDataToForm(FormActions.AddItemToListBox, "My device not found (WMI)");
  1497 					MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
  1498 				}
  1499 				return _deviceDetected;
  1500 			}
  1501 			catch (Exception ex)
  1502 			{
  1503 				DisplayException(Name, ex);
  1504 				throw;
  1505 			}
  1506 		}
  1507 
  1508 		///  <summary>
  1509 		///  Call HID functions that use Win32 API functions to locate a HID-class device
  1510 		///  by its Vendor ID and Product ID. Open a handle to the device.
  1511 		///  </summary>
  1512 		///
  1513 		///  <returns>
  1514 		///   True if the device is detected, False if not detected.
  1515 		///  </returns>
  1516 
  1517 		private Boolean FindTheHid()
  1518 		{
  1519 			var devicePathName = new String[128];
  1520 			String myDevicePathName = "";
  1521 
  1522 			try
  1523 			{
  1524 				_deviceHandleObtained = false;
  1525 				CloseCommunications();
  1526 
  1527 				//  Get the device's Vendor ID and Product ID from the form's text boxes.
  1528 
  1529 				GetVendorAndProductIDsFromTextBoxes(ref _myVendorId, ref _myProductId);
  1530 
  1531 				// Get the HID-class GUID.
  1532 
  1533 				Guid hidGuid = _myHid.GetHidGuid();
  1534 
  1535 				String functionName = "GetHidGuid";
  1536 				Debug.WriteLine(_myDebugging.ResultOfApiCall(functionName));
  1537 				Debug.WriteLine("  GUID for system HIDs: " + hidGuid.ToString());
  1538 
  1539 				//  Fill an array with the device path names of all attached HIDs.
  1540 
  1541 				Boolean availableHids = _myDeviceManagement.FindDeviceFromGuid(hidGuid, ref devicePathName);
  1542 
  1543 				//  If there is at least one HID, attempt to read the Vendor ID and Product ID
  1544 				//  of each device until there is a match or all devices have been examined.
  1545 
  1546 				if (availableHids)
  1547 				{
  1548 					Int32 memberIndex = 0;
  1549 
  1550 					do
  1551 					{
  1552 						// Open the handle without read/write access to enable getting information about any HID, even system keyboards and mice.
  1553 
  1554 						_hidHandle = _myHid.OpenHandle(devicePathName[memberIndex], false);
  1555 
  1556 						functionName = "CreateFile";
  1557 						Debug.WriteLine(_myDebugging.ResultOfApiCall(functionName));
  1558 						Debug.WriteLine("  Returned handle: " + _hidHandle);
  1559 
  1560 						if (!_hidHandle.IsInvalid)
  1561 						{
  1562 							// The returned handle is valid,
  1563 							// so find out if this is the device we're looking for.
  1564 
  1565 							_myHid.DeviceAttributes.Size = Marshal.SizeOf(_myHid.DeviceAttributes);
  1566 
  1567 							Boolean success = _myHid.GetAttributes(_hidHandle, ref _myHid.DeviceAttributes);
  1568 
  1569 							if (success)
  1570 							{
  1571 								Debug.WriteLine("  HIDD_ATTRIBUTES structure filled without error.");
  1572 								Debug.WriteLine("  Structure size: " + _myHid.DeviceAttributes.Size);
  1573 								Debug.WriteLine("  Vendor ID: " + Convert.ToString(_myHid.DeviceAttributes.VendorID, 16));
  1574 								Debug.WriteLine("  Product ID: " + Convert.ToString(_myHid.DeviceAttributes.ProductID, 16));
  1575 								Debug.WriteLine("  Version Number: " + Convert.ToString(_myHid.DeviceAttributes.VersionNumber, 16));
  1576 
  1577 								if ((_myHid.DeviceAttributes.VendorID == _myVendorId) && (_myHid.DeviceAttributes.ProductID == _myProductId))
  1578 								{
  1579 									Debug.WriteLine("  Handle obtained to my device");
  1580 
  1581 									//  Display the information in form's list box.
  1582 
  1583 									MyMarshalDataToForm(FormActions.AddItemToListBox, "Handle obtained to my device:");
  1584 									MyMarshalDataToForm(FormActions.AddItemToListBox, "  Vendor ID= " + Convert.ToString(_myHid.DeviceAttributes.VendorID, 16));
  1585 									MyMarshalDataToForm(FormActions.AddItemToListBox, "  Product ID = " + Convert.ToString(_myHid.DeviceAttributes.ProductID, 16));
  1586 									MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
  1587 
  1588 									_deviceHandleObtained = true;
  1589 
  1590 									myDevicePathName = devicePathName[memberIndex];
  1591 								}
  1592 								else
  1593 								{
  1594 									//  It's not a match, so close the handle.
  1595 
  1596 									_deviceHandleObtained = false;
  1597 									_hidHandle.Close();
  1598 								}
  1599 							}
  1600 							else
  1601 							{
  1602 								//  There was a problem retrieving the information.
  1603 
  1604 								Debug.WriteLine("  Error in filling HIDD_ATTRIBUTES structure.");
  1605 								_deviceHandleObtained = false;
  1606 								_hidHandle.Close();
  1607 							}
  1608 						}
  1609 
  1610 						//  Keep looking until we find the device or there are no devices left to examine.
  1611 
  1612 						memberIndex = memberIndex + 1;
  1613 					}
  1614 					while (!((_deviceHandleObtained || (memberIndex == devicePathName.Length))));
  1615 				}
  1616 
  1617 				if (_deviceHandleObtained)
  1618 				{
  1619 					//  The device was detected.
  1620 					//  Learn the capabilities of the device.
  1621 
  1622 					_myHid.Capabilities = _myHid.GetDeviceCapabilities(_hidHandle);
  1623 
  1624 					//  Find out if the device is a system mouse or keyboard.
  1625 
  1626 					_hidUsage = _myHid.GetHidUsage(_myHid.Capabilities);
  1627 
  1628 					//  Get the Input report buffer size.
  1629 
  1630 					GetInputReportBufferSize();
  1631 					MyMarshalDataToForm(FormActions.EnableInputReportBufferSize, "");
  1632 
  1633 					//Close the handle and reopen it with read/write access.
  1634 
  1635 					_hidHandle.Close();
  1636 
  1637 					_hidHandle = _myHid.OpenHandle(myDevicePathName, true);
  1638 
  1639 					if (_hidHandle.IsInvalid)
  1640 					{
  1641 						MyMarshalDataToForm(FormActions.AddItemToListBox, "The device is a system " + _hidUsage + ".");
  1642 						MyMarshalDataToForm(FormActions.AddItemToListBox, "Windows 2000 and later obtain exclusive access to Input and Output reports for this devices.");
  1643 						MyMarshalDataToForm(FormActions.AddItemToListBox, "Windows 8 also obtains exclusive access to Feature reports.");
  1644 						MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
  1645 					}
  1646 					else
  1647 					{
  1648 						if (_myHid.Capabilities.InputReportByteLength > 0)
  1649 						{
  1650 							//  Set the size of the Input report buffer.
  1651 
  1652 							var inputReportBuffer = new Byte[_myHid.Capabilities.InputReportByteLength];
  1653 
  1654 							_deviceData = new FileStream(_hidHandle, FileAccess.Read | FileAccess.Write, inputReportBuffer.Length, false);
  1655 						}
  1656 
  1657 						if (_myHid.Capabilities.OutputReportByteLength > 0)
  1658 						{
  1659 							Byte[] outputReportBuffer = null;
  1660 						}
  1661 						//  Flush any waiting reports in the input buffer. (optional)
  1662 
  1663 						_myHid.FlushQueue(_hidHandle);
  1664 					}
  1665 				}
  1666 				else
  1667 				{
  1668 					MyMarshalDataToForm(FormActions.AddItemToListBox, "Device not found.");
  1669 					MyMarshalDataToForm(FormActions.DisableInputReportBufferSize, "");
  1670 					EnableFormControls();
  1671 					MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
  1672 				}
  1673 				return _deviceHandleObtained;
  1674 			}
  1675 			catch (Exception ex)
  1676 			{
  1677 				DisplayException(Name, ex);
  1678 				throw;
  1679 			}
  1680 		}
  1681 
  1682 		///  <summary>
  1683 		///  Perform shutdown operations.
  1684 		///  </summary>
  1685 
  1686 		private void frmMain_Closed(Object eventSender, EventArgs eventArgs)
  1687 		{
  1688 			try
  1689 			{
  1690 				Shutdown();
  1691 			}
  1692 			catch (Exception ex)
  1693 			{
  1694 				DisplayException(Name, ex);
  1695 				throw;
  1696 			}
  1697 		}
  1698 
  1699 		///  <summary>
  1700 		///  Perform startup operations.
  1701 		///  </summary>
  1702 
  1703 		private void frmMain_Load(Object eventSender, EventArgs eventArgs)
  1704 		{
  1705 			try
  1706 			{
  1707 				FrmMy = this;
  1708 				Startup();
  1709 			}
  1710 			catch (Exception ex)
  1711 			{
  1712 				DisplayException(Name, ex);
  1713 				throw;
  1714 			}
  1715 		}
  1716 
  1717 		private void GetBytesToSend()
  1718 		{
  1719 			try
  1720 			{
  1721 				//  Get the bytes to send in a report from the combo boxes.
  1722 				//  Increment the values if the autoincrement check box is selected.
  1723 
  1724 				if (ChkAutoincrement.Checked)
  1725 				{
  1726 					if (CboByte0.SelectedIndex < 255)
  1727 					{
  1728 						CboByte0.SelectedIndex = CboByte0.SelectedIndex + 1;
  1729 					}
  1730 					else
  1731 					{
  1732 						CboByte0.SelectedIndex = 0;
  1733 					}
  1734 					if (CboByte1.SelectedIndex < 255)
  1735 					{
  1736 						CboByte1.SelectedIndex = CboByte1.SelectedIndex + 1;
  1737 					}
  1738 					else
  1739 					{
  1740 						CboByte1.SelectedIndex = 0;
  1741 					}
  1742 				}
  1743 			}
  1744 			catch (Exception ex)
  1745 			{
  1746 				DisplayException(Name, ex);
  1747 				throw;
  1748 			}
  1749 		}
  1750 
  1751 		///  <summary>
  1752 		///  Find and display the number of Input buffers
  1753 		///  (the number of Input reports the HID driver will store).
  1754 		///  </summary>
  1755 
  1756 		private void GetInputReportBufferSize()
  1757 		{
  1758 			Int32 numberOfInputBuffers = 0;
  1759 			Boolean success;
  1760 
  1761 			try
  1762 			{
  1763 				//  Get the number of input buffers.
  1764 
  1765 				_myHid.GetNumberOfInputBuffers(_hidHandle, ref numberOfInputBuffers);
  1766 
  1767 				//  Display the result in the text box.
  1768 
  1769 				MyMarshalDataToForm(FormActions.SetInputReportBufferSize, Convert.ToString(numberOfInputBuffers));
  1770 			}
  1771 			catch (Exception ex)
  1772 			{
  1773 				DisplayException(Name, ex);
  1774 				throw;
  1775 			}
  1776 		}
  1777 
  1778 		///  <summary>
  1779 		///  Retrieve a Vendor ID and Product ID in hexadecimal
  1780 		///  from the form's text boxes and convert the text to Int32s.
  1781 		///  </summary>
  1782 		///
  1783 		///  <param name="myVendorId"> the Vendor ID</param>
  1784 		///  <param name="myProductId"> the Product ID</param>
  1785 
  1786 		private void GetVendorAndProductIDsFromTextBoxes(ref Int32 myVendorId, ref Int32 myProductId)
  1787 		{
  1788 			try
  1789 			{
  1790 				myVendorId = Int32.Parse(txtVendorID.Text, NumberStyles.AllowHexSpecifier);
  1791 				myProductId = Int32.Parse(txtProductID.Text, NumberStyles.AllowHexSpecifier);
  1792 			}
  1793 			catch (Exception ex)
  1794 			{
  1795 				DisplayException(Name, ex);
  1796 				throw;
  1797 			}
  1798 		}
  1799 
  1800 		///  <summary>
  1801 		///  Initialize the elements on the form.
  1802 		///  </summary>
  1803 
  1804 		private void InitializeDisplay()
  1805 		{
  1806 			try
  1807 			{
  1808 				//  Create a dropdown list box for each byte to send in a report.
  1809 				//  Display the values as 2-character hex strings.
  1810 
  1811 				Int16 count;
  1812 				for (count = 0; count <= 255; count++)
  1813 				{
  1814 					String byteValue = String.Format("{0:X2} ", count);
  1815 					FrmMy.CboByte0.Items.Insert(count, byteValue);
  1816 					FrmMy.CboByte1.Items.Insert(count, byteValue);
  1817 				}
  1818 
  1819 				//  Select a default value for each box
  1820 
  1821 				FrmMy.CboByte0.SelectedIndex = 0;
  1822 				FrmMy.CboByte1.SelectedIndex = 128;
  1823 				FrmMy.radInputOutputInterrupt.Checked = true;
  1824 
  1825 				//  Check the autoincrement box to increment the values each time a report is sent.
  1826 
  1827 				ChkAutoincrement.CheckState = CheckState.Checked;
  1828 
  1829 				//  Don't allow the user to select an input report buffer size until there is
  1830 				//  a handle to a HID.
  1831 
  1832 				cmdInputReportBufferSize.Focus();
  1833 				cmdInputReportBufferSize.Enabled = false;
  1834 
  1835 				LstResults.Items.Add("For a more detailed event log, view debug statements in Visual Studio's Output window:");
  1836 				LstResults.Items.Add("Click Build > Configuration Manager > Active Solution Configuration > Debug > Close.");
  1837 				LstResults.Items.Add("Then click View > Output.");
  1838 				LstResults.Items.Add("");
  1839 			}
  1840 			catch (Exception ex)
  1841 			{
  1842 				DisplayException(Name, ex);
  1843 				throw;
  1844 			}
  1845 		}
  1846 
  1847 		///  <summary>
  1848 		///  Enables accessing a form's controls from another thread
  1849 		///  </summary>
  1850 		///
  1851 		///  <param name="action"> a FormActions member that names the action to perform on the form </param>
  1852 		///  <param name="textToDisplay"> text that the form displays or the code uses for
  1853 		///  another purpose. Actions that don't use text ignore this parameter.  </param>
  1854 
  1855         private void MyMarshalDataToForm(FormActions action, params string[] strings)
  1856 		{
  1857 			try
  1858 			{
  1859                 object[] args = { action, strings };
  1860 
  1861 				//  The AccessForm routine contains the code that accesses the form.
  1862 
  1863 				MarshalDataToForm marshalDataToFormDelegate = AccessForm;
  1864 
  1865 				//  Execute AccessForm, passing the parameters in args.
  1866 
  1867 				Invoke(marshalDataToFormDelegate, args);
  1868 			}
  1869 			catch (Exception ex)
  1870 			{
  1871 				DisplayException(Name, ex);
  1872 				throw;
  1873 			}
  1874 		}
  1875 
  1876 		/// <summary>
  1877 		/// Timeout if read via interrupt transfer doesn't return.
  1878 		/// </summary>
  1879 
  1880 		private void OnReadTimeout()
  1881 		{
  1882 			try
  1883 			{
  1884 				MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to read a report timed out.");
  1885 				MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
  1886 				CloseCommunications();
  1887 				MyMarshalDataToForm(FormActions.EnableGetInputReportInterruptTransfer, "");
  1888 				_transferInProgress = false;
  1889 				_sendOrGet = SendOrGet.Send;
  1890 			}
  1891 			catch (Exception ex)
  1892 			{
  1893 				DisplayException(Name, ex);
  1894 				throw;
  1895 			}
  1896 		}
  1897 
  1898 		/// <summary>
  1899 		/// Timeout if write via interrupt transfer doesn't return.
  1900 		/// </summary>
  1901 
  1902 		private void OnWriteTimeout()
  1903 		{
  1904 			try
  1905 			{
  1906 				MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to write a report timed out.");
  1907 				MyMarshalDataToForm(FormActions.ScrollToBottomOfListBox, "");
  1908 				CloseCommunications();
  1909 				MyMarshalDataToForm(FormActions.EnableSendOutputReportInterrupt, "");
  1910 				_transferInProgress = false;
  1911 				_sendOrGet = SendOrGet.Get;
  1912 			}
  1913 			catch (Exception ex)
  1914 			{
  1915 				DisplayException(Name, ex);
  1916 				throw;
  1917 			}
  1918 		}
  1919 
  1920 		/// <summary>
  1921 		/// Alternat sending and getting a report.
  1922 		/// </summary>
  1923 
  1924 		private void PeriodicTransfers()
  1925 		{
  1926 			try
  1927 			{
  1928 				if (!_transferInProgress)
  1929 				{
  1930 					if (_reportType == ReportTypes.Feature)
  1931 					{
  1932 						SendOrGetFeatureReport();
  1933 					}
  1934 					else
  1935 					{
  1936 						// Output and Input reports
  1937 
  1938 						SendOutputReportOrGetInputReport();
  1939 					}
  1940 				}
  1941 			}
  1942 			catch (Exception ex)
  1943 			{
  1944 				DisplayException(Name, ex);
  1945 				throw;
  1946 			}
  1947 		}
  1948 
  1949 		/// <summary>
  1950 		/// Start doing periodic transfers.
  1951 		/// </summary>
  1952 
  1953 		private void PeriodicTransfersStart()
  1954 		{
  1955 			// Don't allow changing the transfer type while transfers are in progress.
  1956 
  1957 			if (radFeature.Checked)
  1958 			{
  1959 				radInputOutputControl.Enabled = false;
  1960 				radInputOutputInterrupt.Enabled = false;
  1961 			}
  1962 			else if (radInputOutputControl.Checked)
  1963 			{
  1964 				radFeature.Enabled = false;
  1965 				radInputOutputInterrupt.Enabled = false;
  1966 			}
  1967 			else if (radInputOutputInterrupt.Checked)
  1968 			{
  1969 				radFeature.Enabled = false;
  1970 				radInputOutputControl.Enabled = false;
  1971 			}
  1972 
  1973 			//  Change the command button's text.
  1974 
  1975 			cmdPeriodicTransfers.Text = "Stop";
  1976 
  1977 			//  Enable the timer event to trigger a set of transfers.
  1978 
  1979 			_periodicTransfers.Start();
  1980 
  1981 			cmdPeriodicTransfers.Enabled = true;
  1982 
  1983 			if (radInputOutputInterrupt.Checked)
  1984 			{
  1985 				_transferType = TransferTypes.Interrupt;
  1986 				_reportType = ReportTypes.Output;
  1987 			}
  1988 			else if (radInputOutputControl.Checked)
  1989 			{
  1990 				_transferType = TransferTypes.Control;
  1991 				_reportType = ReportTypes.Output;
  1992 			}
  1993 			else if (radFeature.Checked)
  1994 			{
  1995 				_transferType = TransferTypes.Control;
  1996 				_reportType = ReportTypes.Feature;
  1997 			}
  1998 			_periodicTransfersRequested = true;
  1999 			PeriodicTransfers();
  2000 		}
  2001 
  2002 		/// <summary>
  2003 		/// Stop doing periodic transfers.
  2004 		/// </summary>
  2005 
  2006 		private void PeriodicTransfersStop()
  2007 		{
  2008 			//  Stop doing continuous transfers.
  2009 
  2010 			_periodicTransfersRequested = false;
  2011 
  2012 			// Disable the timer that triggers the transfers.
  2013 
  2014 			_periodicTransfers.Stop();
  2015 			cmdPeriodicTransfers.Enabled = true;
  2016 
  2017 			//  Change the command button's text.
  2018 
  2019 			cmdPeriodicTransfers.Text = "Start";
  2020 
  2021 			// Re-allow changing the transfer type.
  2022 
  2023 			radFeature.Enabled = true;
  2024 			radInputOutputControl.Enabled = true;
  2025 			radInputOutputInterrupt.Enabled = true;
  2026 		}
  2027 
  2028 		private void radInputOutputControl_CheckedChanged(object sender, EventArgs e)
  2029 		{
  2030 		}
  2031 
  2032 		private void radInputOutputInterrupt_CheckedChanged(object sender, EventArgs e)
  2033 		{
  2034 		}
  2035 
  2036 		private void radFeature_CheckedChanged(object sender, EventArgs e)
  2037 		{
  2038 		}
  2039 
  2040 		///  <summary>
  2041 		///  Request a Feature report.
  2042 		///  Assumes report ID = 0.
  2043 		///  </summary>
  2044 
  2045 		private void RequestToGetFeatureReport()
  2046 		{
  2047 			String byteValue = null;
  2048 
  2049 			try
  2050 			{
  2051 				//  If the device hasn't been detected, was removed, or timed out on a previous attempt
  2052 				//  to access it, look for the device.
  2053 
  2054 				if (!_deviceHandleObtained)
  2055 				{
  2056 					_deviceHandleObtained = FindTheHid();
  2057 				}
  2058 
  2059 				if (_deviceHandleObtained)
  2060 				{
  2061 					Byte[] inFeatureReportBuffer = null;
  2062 
  2063 					if ((_myHid.Capabilities.FeatureReportByteLength > 0))
  2064 					{
  2065 						//  The HID has a Feature report.
  2066 						//  Read a report from the device.
  2067 
  2068 						//  Set the size of the Feature report buffer.
  2069 
  2070 						if ((_myHid.Capabilities.FeatureReportByteLength > 0))
  2071 						{
  2072 							inFeatureReportBuffer = new Byte[_myHid.Capabilities.FeatureReportByteLength];
  2073 						}
  2074 
  2075 						//  Read a report.
  2076 
  2077 						Boolean success = _myHid.GetFeatureReport(_hidHandle, ref inFeatureReportBuffer);
  2078 
  2079 						if (success)
  2080 						{
  2081 							DisplayReportData(inFeatureReportBuffer, ReportTypes.Feature, ReportReadOrWritten.Read);
  2082 						}
  2083 						else
  2084 						{
  2085 							CloseCommunications();
  2086 							MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to read a Feature report failed.");
  2087 							ScrollToBottomOfListBox();
  2088 						}
  2089 					}
  2090 					else
  2091 					{
  2092 						MyMarshalDataToForm(FormActions.AddItemToListBox, "The HID doesn't have a Feature report.");
  2093 						ScrollToBottomOfListBox();
  2094 					}
  2095 				}
  2096 				_transferInProgress = false;
  2097 				cmdGetFeatureReport.Enabled = true;
  2098 			}
  2099 			catch (Exception ex)
  2100 			{
  2101 				DisplayException(Name, ex);
  2102 				throw;
  2103 			}
  2104 		}
  2105 
  2106 		///  <summary>
  2107 		///  Request an Input report.
  2108 		///  Assumes report ID = 0.
  2109 		///  </summary>
  2110 
  2111 		private async void RequestToGetInputReport()
  2112 		{
  2113 			const Int32 readTimeout = 5000;
  2114 
  2115 			String byteValue = null;
  2116 			Byte[] inputReportBuffer = null;
  2117 
  2118 			try
  2119 			{
  2120 				Boolean success = false;
  2121 
  2122 				//  If the device hasn't been detected, was removed, or timed out on a previous attempt
  2123 				//  to access it, look for the device.
  2124 
  2125 				if (!_deviceHandleObtained)
  2126 				{
  2127 					_deviceHandleObtained = FindTheHid();
  2128 				}
  2129 
  2130 				if (_deviceHandleObtained)
  2131 				{
  2132 					//  Don't attempt to exchange reports if valid handles aren't available
  2133 					//  (as for a mouse or keyboard under Windows 2000 and later.)
  2134 
  2135 					if (!_hidHandle.IsInvalid)
  2136 					{
  2137 						//  Read an Input report.
  2138 
  2139 						//  Don't attempt to send an Input report if the HID has no Input report.
  2140 						//  (The HID spec requires all HIDs to have an interrupt IN endpoint,
  2141 						//  which suggests that all HIDs must support Input reports.)
  2142 
  2143 						if (_myHid.Capabilities.InputReportByteLength > 0)
  2144 						{
  2145 							//  Set the size of the Input report buffer.
  2146 
  2147 							inputReportBuffer = new Byte[_myHid.Capabilities.InputReportByteLength];
  2148 
  2149 							if (_transferType.Equals(TransferTypes.Control))
  2150 							{
  2151 								{
  2152 									_transferInProgress = true;
  2153 
  2154 									//  Read a report using a control transfer.
  2155 
  2156 									success = _myHid.GetInputReportViaControlTransfer(_hidHandle, ref inputReportBuffer);
  2157 									cmdGetInputReportControl.Enabled = true;
  2158 									_transferInProgress = false;
  2159 								}
  2160 							}
  2161 							else
  2162 							{
  2163 								{
  2164 									_transferInProgress = true;
  2165 
  2166 									//  Read a report using interrupt transfers.
  2167 									//  Timeout if no report available.
  2168 									//  To enable reading a report without blocking the calling thread, uses Filestream's ReadAsync method.
  2169 
  2170 									// Create a delegate to execute on a timeout.
  2171 
  2172 									Action onReadTimeoutAction = OnReadTimeout;
  2173 
  2174 									// The CancellationTokenSource specifies the timeout value and the action to take on a timeout.
  2175 
  2176 									var cts = new CancellationTokenSource();
  2177 
  2178 									// Cancel the read if it hasn't completed after a timeout.
  2179 
  2180 									cts.CancelAfter(readTimeout);
  2181 
  2182 									// Specify the function to call on a timeout.
  2183 
  2184 									cts.Token.Register(onReadTimeoutAction);
  2185 
  2186 									// Stops waiting when data is available or on timeout:
  2187 
  2188 									Int32 bytesRead = await _myHid.GetInputReportViaInterruptTransfer(_deviceData, inputReportBuffer, cts);
  2189 
  2190 									// Arrive here only if the operation completed.
  2191 
  2192 									// Dispose to stop the timeout timer.
  2193 
  2194 									cts.Dispose();
  2195 
  2196 									_transferInProgress = false;
  2197 									cmdGetInputReportInterrupt.Enabled = true;
  2198 
  2199 									if (bytesRead > 0)
  2200 									{
  2201 										success = true;
  2202 										Debug.Print("bytes read (includes report ID) = " + Convert.ToString(bytesRead));
  2203 									}
  2204 								}
  2205 							}
  2206 						}
  2207 						else
  2208 						{
  2209 							MyMarshalDataToForm(FormActions.AddItemToListBox, "No attempt to read an Input report was made.");
  2210 							MyMarshalDataToForm(FormActions.AddItemToListBox, "The HID doesn't have an Input report.");
  2211 						}
  2212 					}
  2213 					else
  2214 					{
  2215 						MyMarshalDataToForm(FormActions.AddItemToListBox, "Invalid handle.");
  2216 						MyMarshalDataToForm(FormActions.AddItemToListBox,
  2217 											"No attempt to write an Output report or read an Input report was made.");
  2218 					}
  2219 
  2220 					if (success)
  2221 					{
  2222 						DisplayReportData(inputReportBuffer, ReportTypes.Input, ReportReadOrWritten.Read);
  2223 					}
  2224 					else
  2225 					{
  2226 						CloseCommunications();
  2227 						MyMarshalDataToForm(FormActions.AddItemToListBox, "The attempt to read an Input report has failed.");
  2228 						ScrollToBottomOfListBox();
  2229 					}
  2230 				}
  2231 			}
  2232 			catch (Exception ex)
  2233 			{
  2234 				DisplayException(Name, ex);
  2235 				throw;
  2236 			}
  2237 		}
  2238 
  2239 		///  <summary>
  2240 		///  Sends a Feature report.
  2241 		///  Assumes report ID = 0.
  2242 		///  </summary>
  2243 
  2244 		private void RequestToSendFeatureReport()
  2245 		{
  2246 			String byteValue = null;
  2247 
  2248 			try
  2249 			{
  2250 				_transferInProgress = true;
  2251 
  2252 				//  If the device hasn't been detected, was removed, or timed out on a previous attempt
  2253 				//  to access it, look for the device.
  2254 
  2255 				if (!_deviceHandleObtained)
  2256 				{
  2257 					_deviceHandleObtained = FindTheHid();
  2258 				}
  2259 
  2260 				if (_deviceHandleObtained)
  2261 				{
  2262 					GetBytesToSend();
  2263 
  2264 					if ((_myHid.Capabilities.FeatureReportByteLength > 0))
  2265 					{
  2266 						//  The HID has a Feature report.
  2267 						//  Set the size of the Feature report buffer.
  2268 
  2269 						var outFeatureReportBuffer = new Byte[_myHid.Capabilities.FeatureReportByteLength];
  2270 
  2271 						//  Store the report ID in the buffer.
  2272 
  2273 						outFeatureReportBuffer[0] = 0;
  2274 
  2275 						//  Store the report data following the report ID.
  2276 						//  Use the data in the combo boxes on the form.
  2277 
  2278 						outFeatureReportBuffer[1] = Convert.ToByte(CboByte0.SelectedIndex);
  2279 
  2280 						if (outFeatureReportBuffer.GetUpperBound(0) > 1)
  2281 						{
  2282 							outFeatureReportBuffer[2] = Convert.ToByte(CboByte1.SelectedIndex);
  2283 						}
  2284 
  2285 						//  Write a report to the device
  2286 
  2287 						Boolean success = _myHid.SendFeatureReport(_hidHandle, outFeatureReportBuffer);
  2288 
  2289 						if (success)
  2290 						{
  2291 							DisplayReportData(outFeatureReportBuffer, ReportTypes.Feature, ReportReadOrWritten.Written);
  2292 						}
  2293 						else
  2294 						{
  2295 							CloseCommunications();
  2296 							AccessForm(FormActions.AddItemToListBox, "The attempt to send a Feature report failed.");
  2297 							ScrollToBottomOfListBox();
  2298 						}
  2299 					}
  2300 					else
  2301 					{
  2302 						AccessForm(FormActions.AddItemToListBox, "The HID doesn't have a Feature report.");
  2303 						ScrollToBottomOfListBox();
  2304 					}
  2305 
  2306 				}
  2307 				_transferInProgress = false;
  2308 				cmdSendFeatureReport.Enabled = true;
  2309 				ScrollToBottomOfListBox();
  2310 
  2311 			}
  2312 			catch (Exception ex)
  2313 			{
  2314 				DisplayException(Name, ex);
  2315 				throw;
  2316 			}
  2317 		}
  2318 
  2319 		///  <summary>
  2320 		///  Sends an Output report.
  2321 		///  Assumes report ID = 0.
  2322 		///  </summary>
  2323 
  2324 		private async void RequestToSendOutputReport()
  2325 		{
  2326 			const Int32 writeTimeout = 5000;
  2327 			String byteValue = null;
  2328 
  2329 			try
  2330 			{
  2331 				//  If the device hasn't been detected, was removed, or timed out on a previous attempt
  2332 				//  to access it, look for the device.
  2333 
  2334 				if (!_deviceHandleObtained)
  2335 				{
  2336 					_deviceHandleObtained = FindTheHid();
  2337 				}
  2338 
  2339 				if (_deviceHandleObtained)
  2340 				{
  2341 					GetBytesToSend();
  2342 				}
  2343 				//  Don't attempt to exchange reports if valid handles aren't available
  2344 				//  (as for a mouse or keyboard.)
  2345 
  2346 				if (!_hidHandle.IsInvalid)
  2347 				{
  2348 					//  Don't attempt to send an Output report if the HID has no Output report.
  2349 
  2350 					if (_myHid.Capabilities.OutputReportByteLength > 0)
  2351 					{
  2352 						//  Set the size of the Output report buffer.
  2353 
  2354 						var outputReportBuffer = new Byte[_myHid.Capabilities.OutputReportByteLength];
  2355 
  2356 						//  Store the report ID in the first byte of the buffer:
  2357 
  2358 						outputReportBuffer[0] = 0;
  2359 
  2360 						//  Store the report data following the report ID.
  2361 						//  Use the data in the combo boxes on the form.
  2362 
  2363 						outputReportBuffer[1] = Convert.ToByte(CboByte0.SelectedIndex);
  2364 
  2365 						if (outputReportBuffer.GetUpperBound(0) > 1)
  2366 						{
  2367 							outputReportBuffer[2] = Convert.ToByte(CboByte1.SelectedIndex);
  2368 						}
  2369 
  2370 						//  Write a report.
  2371 
  2372 						Boolean success;
  2373 
  2374 						if (_transferType.Equals(TransferTypes.Control))
  2375 						{
  2376 							{
  2377 								_transferInProgress = true;
  2378 
  2379 								//  Use a control transfer to send the report,
  2380 								//  even if the HID has an interrupt OUT endpoint.
  2381 
  2382 								success = _myHid.SendOutputReportViaControlTransfer(_hidHandle, outputReportBuffer);
  2383 
  2384 								_transferInProgress = false;
  2385 								cmdSendOutputReportControl.Enabled = true;
  2386 							}
  2387 						}
  2388 						else
  2389 						{
  2390 							Debug.Print("interrupt");
  2391 							_transferInProgress = true;
  2392 
  2393 							// The CancellationTokenSource specifies the timeout value and the action to take on a timeout.
  2394 
  2395 							var cts = new CancellationTokenSource();
  2396 
  2397 							// Create a delegate to execute on a timeout.
  2398 
  2399 							Action onWriteTimeoutAction = OnWriteTimeout;
  2400 
  2401 							// Cancel the read if it hasn't completed after a timeout.
  2402 
  2403 							cts.CancelAfter(writeTimeout);
  2404 
  2405 							// Specify the function to call on a timeout.
  2406 
  2407 							cts.Token.Register(onWriteTimeoutAction);
  2408 
  2409 							// Send an Output report and wait for completion or timeout.
  2410 
  2411 							success = await _myHid.SendOutputReportViaInterruptTransfer(_deviceData, _hidHandle, outputReportBuffer, cts);
  2412 
  2413 							// Get here only if the operation completes without a timeout.
  2414 
  2415 							_transferInProgress = false;
  2416 							cmdSendOutputReportInterrupt.Enabled = true;
  2417 
  2418 							// Dispose to stop the timeout timer.
  2419 
  2420 							cts.Dispose();
  2421 						}
  2422 						if (success)
  2423 						{
  2424 							DisplayReportData(outputReportBuffer, ReportTypes.Output, ReportReadOrWritten.Written);
  2425 						}
  2426 						else
  2427 						{
  2428 							CloseCommunications();
  2429 							AccessForm(FormActions.AddItemToListBox, "The attempt to write an Output report failed.");
  2430 							ScrollToBottomOfListBox();
  2431 						}
  2432 					}
  2433 				}
  2434 				else
  2435 				{
  2436 					AccessForm(FormActions.AddItemToListBox, "The HID doesn't have an Output report.");
  2437 				}
  2438 			}
  2439 			catch (Exception ex)
  2440 			{
  2441 				DisplayException(Name, ex);
  2442 				throw;
  2443 			}
  2444 		}
  2445 
  2446 		///  <summary>
  2447 		///  Scroll to the bottom of the list box and trim as needed.
  2448 		///  </summary>
  2449 
  2450 		private void ScrollToBottomOfListBox()
  2451 		{
  2452 			try
  2453 			{
  2454 				LstResults.SelectedIndex = LstResults.Items.Count - 1;
  2455 
  2456 				//  If the list box is getting too large, trim its contents by removing the earliest data.
  2457 
  2458 				if (LstResults.Items.Count > 1000)
  2459 				{
  2460 					Int32 count;
  2461 					for (count = 1; count <= 500; count++)
  2462 					{
  2463 						LstResults.Items.RemoveAt(4);
  2464 					}
  2465 				}
  2466 			}
  2467 			catch (Exception ex)
  2468 			{
  2469 				DisplayException(Name, ex);
  2470 				throw;
  2471 			}
  2472 		}
  2473 
  2474 		/// <summary>
  2475 		/// Request to send or get a Feature report.
  2476 		/// </summary>
  2477 
  2478 		private void SendOrGetFeatureReport()
  2479 		{
  2480 			try
  2481 			{
  2482 				//  If the device hasn't been detected, was removed, or timed out on a previous attempt
  2483 				//  to access it, look for the device.
  2484 
  2485 				if (!_deviceHandleObtained)
  2486 				{
  2487 					_deviceHandleObtained = FindTheHid();
  2488 				}
  2489 
  2490 				if (_deviceHandleObtained)
  2491 				{
  2492 					switch (_sendOrGet)
  2493 					{
  2494 						case SendOrGet.Send:
  2495 							RequestToSendFeatureReport();
  2496 							_sendOrGet = SendOrGet.Get;
  2497 							break;
  2498 						case SendOrGet.Get:
  2499 							RequestToGetFeatureReport();
  2500 							_sendOrGet = SendOrGet.Send;
  2501 							break;
  2502 					}
  2503 				}
  2504 			}
  2505 			catch (Exception ex)
  2506 			{
  2507 				DisplayException(Name, ex);
  2508 				throw;
  2509 			}
  2510 		}
  2511 
  2512 		/// <summary>
  2513 		/// Request to send an Output report or get an Input report.
  2514 		/// </summary>
  2515 
  2516 		private void SendOutputReportOrGetInputReport()
  2517 		{
  2518 			try
  2519 			{
  2520 				//  If the device hasn't been detected, was removed, or timed out on a previous attempt
  2521 				//  to access it, look for the device.
  2522 
  2523 				if (!_deviceHandleObtained)
  2524 				{
  2525 					_deviceHandleObtained = FindTheHid();
  2526 				}
  2527 
  2528 				if (_deviceHandleObtained)
  2529 				{
  2530 					if (_sendOrGet == SendOrGet.Send)
  2531 					{
  2532 						RequestToSendOutputReport();
  2533 						_sendOrGet = SendOrGet.Get;
  2534 					}
  2535 					else
  2536 					{
  2537 						RequestToGetInputReport();
  2538 						_sendOrGet = SendOrGet.Send;
  2539 					}
  2540 				}
  2541 			}
  2542 			catch (Exception ex)
  2543 			{
  2544 				DisplayException(Name, ex);
  2545 				throw;
  2546 			}
  2547 		}
  2548 
  2549 		///  <summary>
  2550 		///  Set the number of Input buffers (the number of Input reports
  2551 		///  the host will store) from the value in the text box.
  2552 		///  </summary>
  2553 
  2554 		private void SetInputReportBufferSize()
  2555 		{
  2556 			try
  2557 			{
  2558 				if (!_transferInProgress)
  2559 				{
  2560 					//  Get the number of buffers from the text box.
  2561 
  2562 					Int32 numberOfInputBuffers = Convert.ToInt32(txtInputReportBufferSize.Text);
  2563 
  2564 					//  Set the number of buffers.
  2565 
  2566 					_myHid.SetNumberOfInputBuffers(_hidHandle, numberOfInputBuffers);
  2567 
  2568 					//  Verify and display the result.
  2569 
  2570 					GetInputReportBufferSize();
  2571 				}
  2572 				else
  2573 				{
  2574 					DisplayTransferInProgressMessage();
  2575 				}
  2576 			}
  2577 			catch (Exception ex)
  2578 			{
  2579 				DisplayException(Name, ex);
  2580 				throw;
  2581 			}
  2582 		}
  2583 
  2584 		///  <summary>
  2585 		///  Perform actions that must execute when the program ends.
  2586 		///  </summary>
  2587 
  2588 		private void Shutdown()
  2589 		{
  2590 			try
  2591 			{
  2592 				CloseCommunications();
  2593 				DeviceNotificationsStop();
  2594 			}
  2595 			catch (Exception ex)
  2596 			{
  2597 				DisplayException(Name, ex);
  2598 				throw;
  2599 			}
  2600 		}
  2601 
  2602 		///  <summary>
  2603 		///  Perform actions that must execute when the program starts.
  2604 		///  </summary>
  2605 
  2606 		private void Startup()
  2607 		{
  2608 			const Int32 periodicTransferInterval = 1000;
  2609 			try
  2610 			{
  2611 				_myHid = new Hid();
  2612 				InitializeDisplay();
  2613 
  2614 				_periodicTransfers = new System.Timers.Timer(periodicTransferInterval);
  2615 				_periodicTransfers.Elapsed += DoPeriodicTransfers;
  2616 				_periodicTransfers.Stop();
  2617 				_periodicTransfers.SynchronizingObject = this;
  2618 
  2619 				//  Default USB Vendor ID and Product ID:
  2620 
  2621 				txtVendorID.Text = "0925";
  2622 				txtProductID.Text = "7001";
  2623 
  2624 				GetVendorAndProductIDsFromTextBoxes(ref _myVendorId, ref _myProductId);
  2625 
  2626 				DeviceNotificationsStart();
  2627 				FindDeviceUsingWmi();
  2628 				FindTheHid();
  2629 			}
  2630 			catch (Exception ex)
  2631 			{
  2632 				DisplayException(Name, ex);
  2633 				throw;
  2634 			}
  2635 		}
  2636 
  2637 		///  <summary>
  2638 		///  The Product ID has changed in the text box. Call a routine to handle it.
  2639 		///  </summary>
  2640 
  2641 		private void txtProductID_TextChanged(Object sender, EventArgs e)
  2642 		{
  2643 			try
  2644 			{
  2645 				DeviceHasChanged();
  2646 			}
  2647 			catch (Exception ex)
  2648 			{
  2649 				DisplayException(Name, ex);
  2650 				throw;
  2651 			}
  2652 		}
  2653 
  2654 		///  <summary>
  2655 		///  The Vendor ID has changed in the text box. Call a routine to handle it.
  2656 		///  </summary>
  2657 
  2658 		private void txtVendorID_TextChanged(Object sender, EventArgs e)
  2659 		{
  2660 			try
  2661 			{
  2662 				DeviceHasChanged();
  2663 			}
  2664 			catch (Exception ex)
  2665 			{
  2666 				DisplayException(Name, ex);
  2667 				throw;
  2668 			}
  2669 		}
  2670 
  2671 		///  <summary>
  2672 		///  Provides a central mechanism for exception handling.
  2673 		///  Displays a message box that describes the exception.
  2674 		///  </summary>
  2675 		///
  2676 		///  <param name="moduleName"> the module where the exception occurred. </param>
  2677 		///  <param name="e"> the exception </param>
  2678 
  2679 		internal static void DisplayException(String moduleName, Exception e)
  2680 		{
  2681 			//  Create an error message.
  2682 
  2683 			String message = "Exception: " + e.Message + Environment.NewLine + "Module: " + moduleName + Environment.NewLine + "Method: " + e.TargetSite.Name;
  2684 
  2685 			const String caption = "Unexpected Exception";
  2686 
  2687 			MessageBox.Show(message, caption, MessageBoxButtons.OK);
  2688 			Debug.Write(message);
  2689 
  2690 			// Get the last error and display it.
  2691 
  2692 			Int32 error = Marshal.GetLastWin32Error();
  2693 
  2694 			Debug.WriteLine("The last Win32 Error was: " + error);
  2695 		}
  2696 
  2697 		[STAThread]
  2698 		internal static void Main() { Application.Run(new FrmMain()); }
  2699 		private static FrmMain _transDefaultFormFrmMain;
  2700 		internal static FrmMain TransDefaultFormFrmMain
  2701 		{
  2702 			get
  2703 			{
  2704 				if (_transDefaultFormFrmMain == null)
  2705 				{
  2706 					_transDefaultFormFrmMain = new FrmMain();
  2707 				}
  2708 				return _transDefaultFormFrmMain;
  2709 			}
  2710 		}
  2711 	}
  2712 }