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