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