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