private bool TryReopenHandle() { // if the last error is 6 ("invalid handle") or 1167 ("Device not connected"), // try re-opening the handle int err = Marshal.GetLastWin32Error(); if (err == 6 || err == 1167) { // try opening a new handle on the device path Console.WriteLine("invalid handle on read/write - trying to reopen"); IntPtr fp2 = OpenFile(); // if that succeeded, replace the old handle with the new one and retry the read if (fp2 != IntPtr.Zero && fp2.ToInt32() != -1) { // close the old handle if (fp != IntPtr.Zero && fp.ToInt32() != -1) { HIDImports.CloseHandle(fp); } // replace the handle fp = fp2; // tell the caller to try again return(true); } } // we didn't successfully reopen the handle return(false); }
/// <summary> /// Reading /// </summary> private void ReadReport() { byte[] buff = new byte[REPORT_LENGTH]; uint numberOfBytesRead = 0; NativeOverlapped overlapped = new NativeOverlapped(); overlapped.EventHandle = mre.SafeWaitHandle.DangerousGetHandle(); overlapped.OffsetHigh = 0; overlapped.OffsetLow = 0; bool result = HIDImports.ReadFile(this.SafeFileHandle.DangerousGetHandle(), buff, (uint)buff.Length, out numberOfBytesRead, ref overlapped); //Log("ReadReport: " + result.ToString()); // parse it if (buff != null && result) { if (ParseInputReport(buff)) { if (WiiControllerStateChanged != null) { WiiControllerStateChanged(this, new WiiControllerStateChangedEventArgs(this.WiiControllerState)); } } } }
private static bool CheckWiiControllerType(SafeFileHandle handle, ControllerType controllerType, HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA diDetail, WiiControllerFoundDelegate wiiControllerFound) { // Get ProductName string product = string.Empty; byte[] productBuff = new byte[128]; // attempt to read the serial number string if (HIDImports.HidD_GetProductString(handle.DangerousGetHandle(), productBuff, productBuff.Length)) { // convert from unicode to the default encoding product = Encoding.Default.GetString(Encoding.Convert(Encoding.Unicode, Encoding.Default, productBuff)); } // Trim the string down by removing any '\0' characters product = product.Remove(product.IndexOf('\0')); switch (controllerType) { case ControllerType.WiiBalanceBoard: if (product == WBBName_new || product == WBBName_old) { if (wiiControllerFound != null) { wiiControllerFound(diDetail.DevicePath, controllerType); } return(true); } break; default: break; } return(false); }
// clear out the USB input buffer public void FlushReadUSB() { // note the starting time so we don't wait forever DateTime t0 = DateTime.Now; // wait until a read would block int rptLen = inputReportLength; while ((DateTime.Now - t0).TotalMilliseconds < 100) { // set up a non-blocking read IntPtr buf = Marshal.AllocHGlobal(rptLen); try { unsafe { // set up the overlapped I/O descriptor Overlapped o = new Overlapped(0, 0, evov.SafeWaitHandle.DangerousGetHandle(), null); NativeOverlapped *no = o.Pack(null, null); // start the non-blocking read Marshal.WriteByte(buf, 0); HIDImports.ReadFile(fp, buf, rptLen, IntPtr.Zero, no); // check to see if it's ready immediately bool ready = evov.WaitOne(0); if (ready) { // it's ready - complete the read UInt32 readLen; int result = HIDImports.GetOverlappedResult(fp, no, out readLen, 0); } else { // Not ready - we'd have to wait to do a read, so the // buffer is empty. Cancel the read. HIDImports.CancelIo(fp); } // done with the overlapped I/O descriptor Overlapped.Unpack(no); Overlapped.Free(no); // if there was nothing ready to read, we've cleared out buffered // inputs, so we're done if (!ready) { return; } } } finally { Marshal.FreeHGlobal(buf); } } }
protected virtual void Dispose(bool disposing) { if (disposing) { if (evov != null) { evov.Dispose(); evov = null; } } if (fp != IntPtr.Zero && fp.ToInt32() != -1) { HIDImports.CloseHandle(fp); fp = IntPtr.Zero; } }
/// <summary> /// Write a report to the WiiController /// </summary> internal bool WriteReport() { Log("WriteReport: " + buff[0].ToString("x")); bool result = HIDImports.HidD_SetOutputReport(this.SafeFileHandle.DangerousGetHandle(), buff, (uint)buff.Length); Log("WriteReport: " + result.ToString()); if (this.buff[0] == (byte)OutputReport.WriteMemory) { Log("Wait"); if (!writeDone.WaitOne(1000, false)) { Log("Wait failed"); } } return(result); }
private const UInt32 PIDAppleMagicKeyboardJIS = 0x267; // by @Drunkar (https://github.com/mayuki/AppleWirelessKeyboardHelper/pull/3) /// <summary> /// /// </summary> internal Boolean Start() { if (_stream != null) { throw new InvalidOperationException("ヘルパーはすでに実行中です。"); } Guid guid; HIDImports.HidD_GetHidGuid(out guid); IntPtr hDevInfo = HIDImports.SetupDiGetClassDevs(ref guid, null, IntPtr.Zero, HIDImports.DIGCF_DEVICEINTERFACE); HIDImports.SP_DEVICE_INTERFACE_DATA diData = new HIDImports.SP_DEVICE_INTERFACE_DATA(); diData.cbSize = Marshal.SizeOf(diData); UInt32 index = 0; while (HIDImports.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref guid, index++, ref diData)) { HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA diDetail = new HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA(); diDetail.cbSize = (IntPtr.Size == 8) ? (UInt32)8 : 5; // x64:8, x86:5 UInt32 size; HIDImports.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref diData, IntPtr.Zero, 0, out size, IntPtr.Zero); if (HIDImports.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref diData, ref diDetail, size, out size, IntPtr.Zero)) { Debug.WriteLine("Device: " + diDetail.DevicePath); Debug.Indent(); SafeFileHandle mHandle = HIDImports.CreateFile(diDetail.DevicePath, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, HIDImports.EFileAttributes.Overlapped, IntPtr.Zero); HIDImports.HIDD_ATTRIBUTES attrib = new HIDImports.HIDD_ATTRIBUTES(); attrib.Size = Marshal.SizeOf(attrib); if (HIDImports.HidD_GetAttributes(mHandle.DangerousGetHandle(), ref attrib)) { Debug.WriteLine(String.Format("VendorID:{0:x}, ProductID:{1:x}, VersionNumber:{2:x}", attrib.VendorID, attrib.ProductID, attrib.VersionNumber)); if (attrib.VendorID == VIDApple && (attrib.ProductID == PIDAppleWirelessKeyboardUS || attrib.ProductID == PIDAppleWirelessKeyboardJIS || attrib.ProductID == PIDAppleKeyboardJIS || attrib.ProductID == PIDAppleKeyboardUS || attrib.ProductID == PIDAppleWirelessKeyboardFR || attrib.ProductID == PIDAppleWirelessKeyboardRU_MC184RS || //attrib.ProductID == PIDAppleKeyboardWithoutTenKeyUS || attrib.ProductID == PIDAppleWirelessKeyboardJIS_MC184JA || attrib.ProductID == PIDAppleWirelessKeyboardJIS_MC184JB || attrib.ProductID == PIDAppleWirelessKeyboardUS_MC184LL || attrib.ProductID == PIDAppleWirelessKeyboardUS_MC184LLB || attrib.ProductID == PIDAppleWirelessKeyboardJIS_MB110JB || attrib.ProductID == PIDAppleMagicKeyboardJIS )) { _stream = new FileStream(mHandle, FileAccess.ReadWrite, 22, true); //break; } else { mHandle.Close(); } } Debug.Unindent(); } } if (_stream != null) { Byte[] buffer = new Byte[22]; _stream.BeginRead(buffer, 0, buffer.Length, SpecialKeyStateChanged, buffer); return(true); } else { // Not Connected return(false); } }
public bool WriteUSB(byte[] buf) { for (int tries = 0; tries < 3; ++tries) { unsafe { // write the data - the file handle is in overlapped mode, so we have to do // this as an overlapped write with an OVERLAPPED structure and an event to // monitor for completion Overlapped o = new Overlapped(0, 0, evov.SafeWaitHandle.DangerousGetHandle(), null); NativeOverlapped *no = o.Pack(null, null); IntPtr hbuf = Marshal.AllocHGlobal(buf.Length); try { Marshal.Copy(buf, 0, hbuf, buf.Length); HIDImports.WriteFile(fp, hbuf, buf.Length, IntPtr.Zero, no); // wait briefly for the write to complete if (evov.WaitOne(250)) { // successful completion - get the result UInt32 actual; int result = HIDImports.GetOverlappedResult(fp, no, out actual, 0); Overlapped.Unpack(no); Overlapped.Free(no); if (result == 0) { // the write failed - try re-opening the handle and go back // for another try TryReopenHandle(); continue; } else if (actual != buf.Length) { // length is wrong - the write failed return(false); } else { // success return(true); } } else { // The write timed out. Cancel the write and try reopening the handle. HIDImports.CancelIo(fp); Overlapped.Unpack(no); Overlapped.Free(no); if (TryReopenHandle()) { continue; } return(false); } } finally { Marshal.FreeHGlobal(hbuf); } } } // maximum retries exceeded - return failure return(false); }
public static List <DeviceInfo> FindDevices(FindDeviceCallback callback = null) { findDeviceDebugInfo.Clear(); // set up an empty return list List <DeviceInfo> devices = new List <DeviceInfo>(); // get the list of devices matching the HID class GUID Guid guid = Guid.Empty; HIDImports.HidD_GetHidGuid(out guid); IntPtr hdev = HIDImports.SetupDiGetClassDevs(ref guid, null, IntPtr.Zero, HIDImports.DIGCF_DEVICEINTERFACE); // set up the attribute structure buffer HIDImports.SP_DEVICE_INTERFACE_DATA diData = new HIDImports.SP_DEVICE_INTERFACE_DATA(); diData.cbSize = Marshal.SizeOf(diData); // read the devices in our list for (uint i = 0; HIDImports.SetupDiEnumDeviceInterfaces(hdev, IntPtr.Zero, ref guid, i, ref diData); ++i) { // get the size of the detail data structure UInt32 size = 0; HIDImports.SetupDiGetDeviceInterfaceDetail(hdev, ref diData, IntPtr.Zero, 0, out size, IntPtr.Zero); String debugInfo = "#" + i + " (" + hdev.ToString() + ")"; // now actually read the detail data structure HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA diDetail = new HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA(); diDetail.cbSize = (IntPtr.Size == 8) ? (uint)8 : (uint)5; HIDImports.SP_DEVINFO_DATA devInfoData = new HIDImports.SP_DEVINFO_DATA(); devInfoData.cbSize = Marshal.SizeOf(devInfoData); if (HIDImports.SetupDiGetDeviceInterfaceDetail(hdev, ref diData, ref diDetail, size, out size, out devInfoData)) { debugInfo += " path=" + diDetail.DevicePath; // create a file handle to access the device IntPtr fp = HIDImports.CreateFile( diDetail.DevicePath, HIDImports.GENERIC_READ_WRITE, HIDImports.SHARE_READ_WRITE, IntPtr.Zero, HIDImports.OPEN_EXISTING, 0, IntPtr.Zero); debugInfo += " fp=" + fp.ToString(); // read the attributes Thread.Sleep(1); HIDImports.HIDD_ATTRIBUTES attrs = new HIDImports.HIDD_ATTRIBUTES(); attrs.Size = Marshal.SizeOf(attrs); if (HIDImports.HidD_GetAttributes(fp, ref attrs)) { // Read the product name string. Note that String name = "<not available>"; byte[] nameBuf = new byte[256]; if (HIDImports.HidD_GetProductString(fp, nameBuf, 256)) { name = System.Text.Encoding.Unicode.GetString(nameBuf).TrimEnd('\0'); } else { debugInfo += " [GetProductString failed]"; } debugInfo += " productString=\"" + name + "\""; // if the vendor and product ID match an LedWiz, and the // product name contains "pinscape", it's one of ours DeviceInfo di; if (CheckIDMatch(attrs) && Regex.IsMatch(name, @"\b(?i)pinscape controller\b") && (di = DeviceInfo.Create( diDetail.DevicePath, name, attrs.VendorID, attrs.ProductID, attrs.VersionNumber)) != null) { debugInfo += " [device added]"; // add the device to our list devices.Add(di); // if there's a callback, invoke it if (callback != null) { callback(di, hdev, ref devInfoData); } } #if false // report the results for debugging Console.Out.WriteLine( "Product " + attrs.ProductID.ToString("X4") + ", vendor " + attrs.VendorID.ToString("X4") + ", version " + attrs.VersionNumber.ToString("X4") + ", name " + name); #endif } else { debugInfo += " [GetAttributes failed]"; } // done with the file handle if (fp.ToInt32() != 0 && fp.ToInt32() != -1) { HIDImports.CloseHandle(fp); } } else { debugInfo += " [GetDeviceInterfaceDetail failed]"; } findDeviceDebugInfo.Add(debugInfo); } // done with the device info list HIDImports.SetupDiDestroyDeviceInfoList(hdev); // return the device list return(devices); }
private IntPtr OpenFile() { return(HIDImports.CreateFile( path, HIDImports.GENERIC_READ_WRITE, HIDImports.SHARE_READ_WRITE, IntPtr.Zero, HIDImports.OPEN_EXISTING, HIDImports.EFileAttributes.Overlapped, IntPtr.Zero)); }
public byte[] ReadUSB() { // try reading a few times, in case we lose the connection briefly int rptLen = inputReportLength; for (int tries = 0; tries < 3; ++tries) { unsafe { // set up a non-blocking ("overlapped") read IntPtr buf = Marshal.AllocHGlobal((int)rptLen); try { Marshal.WriteByte(buf, 0); Overlapped o = new Overlapped(0, 0, evov.SafeWaitHandle.DangerousGetHandle(), null); NativeOverlapped *no = o.Pack(null, null); HIDImports.ReadFile(fp, buf, rptLen, IntPtr.Zero, no); // Wait briefly for the read to complete. But don't wait forever - we might // be talking to a device interface that doesn't provide the type of status // report we're looking for, in which case we don't want to get stuck waiting // for something that will never happen. If this is indeed the controller // interface we're interested in, it will respond within a few milliseconds // with our status report. if (evov.WaitOne(100)) { // The read completed successfully! Get the result. UInt32 readLen; int result = HIDImports.GetOverlappedResult(fp, no, out readLen, 0); Overlapped.Unpack(no); Overlapped.Free(no); if (result == 0) { // The read failed. Try re-opening the file handle in case we // dropped the connection, then re-try the whole read. TryReopenHandle(); continue; } else if (readLen != rptLen) { // The read length didn't match what we expected. This might be // a different device (not a Pinscape controller) or a different // version that we don't know how to talk to. In either case, // return failure. return(null); } else { // The read succeed and was the correct size. Return the data. byte[] retbuf = new byte[rptLen]; Marshal.Copy(buf, retbuf, 0, rptLen); return(retbuf); } } else { // The read timed out. This must not be our control interface after // all. Cancel the read and try reopening the handle. HIDImports.CancelIo(fp); Overlapped.Unpack(no); Overlapped.Free(no); if (TryReopenHandle()) { continue; } return(null); } } finally { Marshal.FreeHGlobal(buf); } } } // don't retry more than a few times return(null); }
private DeviceInfo(string path, string name, ushort vendorID, ushort productID, ushort version) { // remember the settings this.path = path; this.name = name; this.vendorID = (ushort)vendorID; this.productID = (ushort)productID; this.version = version; // set some defaults PlungerEnabled = true; JoystickEnabled = false; TvOnEnabled = false; // open the USB interface this.fp = OpenFile(); // presume valid isValid = true; // assume the maximum input (device to host) report length inputReportLength = 65; // Check the HID interface to see if the HID Usage type is // type 4, for Joystick. If so, the joystick interface is // enabled, which the device uses to send nudge and plunger // readings. If not, the joystick interface is disabled, so // the device only sends private status messages and query // responses. IntPtr ppdata; if (HIDImports.HidD_GetPreparsedData(this.fp, out ppdata)) { // Check the usage. If the joystick is enabled, the // usage will be 4 = Joystick (on usage page 1, "generic // desktop"). If not, the usage is 0 = Undefined, indicating // our private status and query interface. HIDImports.HIDP_CAPS caps; HIDImports.HidP_GetCaps(ppdata, out caps); bool isJoystick = caps.UsagePage == 1 && caps.Usage == 4; bool isPrivate = caps.UsagePage == 1 && caps.Usage == 0; if (isJoystick) { this.JoystickEnabled = true; } // Only count this as a control interface if it's the joystick // interface or the private interface. The device can expose // other interfaces for device-to-host inputs only, such as a // keyboard interface and a media key interface. Also ignore // it if it doesn't accept output reports (host-to-device), // as indicated by a zero output report length. if (!(isJoystick || isPrivate) || caps.OutputReportByteLength == 0) { isValid = false; } // remember the input report (device to host) length inputReportLength = caps.InputReportByteLength; // free the preparsed data HIDImports.HidD_FreePreparsedData(ppdata); } else { // couldn't get the HID caps - mark as invalid isValid = false; } // if it looks like a valid interface so far, try reading a status report byte[] buf; if (isValid) { buf = ReadUSB(); if (buf != null) { // parse the reponse this.PlungerEnabled = (buf[1] & 0x01) != 0; } else { // couldn't read a report - mark it as invalid isValid = false; } } // Ask about the TV ON feature. This is config variable 9; the // results are: [0] power status input pin; [1] latch output pin; // [2] relay trigger pin; [3,4] delay time in 10ms increments. // This is enabled only if all pins are assigned and the delay // is non-zero. if (isValid && (buf = QueryConfigVar(9)) != null) { int delay = buf[3] | (buf[4] << 8); this.TvOnEnabled = (buf[0] != 0xFF && buf[1] != 0xFF && buf[2] != 0xFF && delay != 0); } // figure the LedWiz unit number LedWizUnitNo = (vendorID == 0xFAFA ? ((productID & 0x0F) + 1) : 0); // get more data if the unit is responding if (isValid) { // get the CPU ID this.CPUID = QueryCPUID(); this.OpenSDAID = QueryOpenSDAID(); // get the pinscape unit number if ((buf = QueryConfigVar(2)) != null) { PinscapeUnitNo = buf[0]; } // get the build ID String s; QueryBuildID(out this.BuildDD, out this.BuildTT, out s); this.BuildID = s; } }
/// <summary> /// Reading in another Thread /// </summary> private void OnReadData(object threadSleepTime) { Log("Read Thread started"); try { int sleepTime = (int)threadSleepTime; while (readAsync) { byte[] buff = new byte[REPORT_LENGTH]; uint numberOfBytesRead = 0; NativeOverlapped overlapped = new NativeOverlapped(); overlapped.EventHandle = mre.SafeWaitHandle.DangerousGetHandle(); overlapped.OffsetHigh = 0; overlapped.OffsetLow = 0; bool result = HIDImports.ReadFile(this.SafeFileHandle.DangerousGetHandle(), buff, (uint)buff.Length, out numberOfBytesRead, ref overlapped); Log("ReadReport: " + result.ToString()); if (!result) { uint lastError = HIDImports.GetLastError(); if (lastError != HIDImports.ERROR_IO_PENDING) { Log("Read failed: " + lastError.ToString("X")); continue; } else { if (!HIDImports.GetOverlappedResult(this.SafeFileHandle.DangerousGetHandle(), ref overlapped, out numberOfBytesRead, true)) { lastError = HIDImports.GetLastError(); Log("Read failed: " + lastError.ToString("X")); continue; } if (overlapped.InternalHigh.ToInt32() == HIDImports.STATUS_PENDING || overlapped.InternalLow.ToInt32() == HIDImports.STATUS_PENDING) { Log("Read interrupted " + lastError.ToString("X")); if (!HIDImports.CancelIo(this.SafeFileHandle.DangerousGetHandle())) { lastError = HIDImports.GetLastError(); Log("CancelIO failed: " + lastError.ToString("X")); continue; } } } } // parse it if (buff != null) { if (ParseInputReport(buff)) { if (WiiControllerStateChanged != null) { WiiControllerStateChanged(this, new WiiControllerStateChangedEventArgs(this.WiiControllerState)); } } } Thread.Sleep(sleepTime); } } catch (ThreadAbortException ex) { Log("Thread abort!: " + ex.Message); } }
internal void OpenWiiControllerDeviceHandle(string devicePath, bool onlyOpen = false) { // open a read/write handle to our device using the DevicePath returned SafeFileHandle safeFileHandle = HIDImports.CreateFile(devicePath, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, HIDImports.EFileAttributes.Overlapped, IntPtr.Zero); // create an attributes struct and initialize the size HIDImports.HIDD_ATTRIBUTES attrib = new HIDImports.HIDD_ATTRIBUTES(); attrib.Size = Marshal.SizeOf(attrib); // get the attributes of the current device if (HIDImports.HidD_GetAttributes(safeFileHandle.DangerousGetHandle(), ref attrib)) { // if the vendor and product IDs match up if (attrib.VendorID == WiiInputManager.VID) { if (attrib.ProductID == WiiInputManager.PID_old || attrib.ProductID == WiiInputManager.PID_new) { // initialize this.WiiControllerState = new WiiControllerState(); this.SafeFileHandle = safeFileHandle; this.HIDDevicePath = devicePath; if (onlyOpen) { // get calibration infos this.SetCalibrationInfo(); this.IsConnected = true; // set LED this.SetLEDs(true, true, true, true); } else { // start reading this.readThread = BeginAsyncRead(10); Thread.Sleep(100); // get calibration infos this.SetCalibrationInfo(); this.IsConnected = true; // get status from the WiiContoller this.GetStatus(); // set LED this.SetLEDs(true, true, true, true); if (this.readThread != null) { this.readThread.Abort(); this.readThread = null; } this.readAsync = false; } } } } }
private static void SearchWiiController(WiiControllerFoundDelegate wiiControllerFound, ControllerType controllerType, bool breakIfFoundOne = false) { int index = 0; bool found = false; Guid guid; SafeFileHandle handle; // get the GUID of the HID class HIDImports.HidD_GetHidGuid(out guid); // get a handle to all devices that are part of the HID class // Fun fact: DIGCF_PRESENT worked on my machine just fine. I reinstalled Vista, and now it no longer finds the WiiController with that parameter enabled... IntPtr hDevInfo = HIDImports.SetupDiGetClassDevs(ref guid, null, IntPtr.Zero, HIDImports.DIGCF_DEVICEINTERFACE);// | HIDImports.DIGCF_PRESENT); // create a new interface data struct and initialize its size HIDImports.SP_DEVICE_INTERFACE_DATA diData = new HIDImports.SP_DEVICE_INTERFACE_DATA(); diData.cbSize = Marshal.SizeOf(diData); // get a device interface to a single device (enumerate all devices) while (HIDImports.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref guid, index, ref diData)) { UInt32 size; // get the buffer size for this device detail instance (returned in the size parameter) HIDImports.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref diData, IntPtr.Zero, 0, out size, IntPtr.Zero); // create a detail struct and set its size HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA diDetail = new HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA(); // on Win x86, cbSize must be 5 for some reason. On x64, apparently 8 is what it wants. diDetail.cbSize = (uint)(IntPtr.Size == 8 ? 8 : 5); // actually get the detail struct if (HIDImports.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref diData, ref diDetail, size, out size, IntPtr.Zero)) { Debug.WriteLine(string.Format("{0}: {1} - {2}", index, diDetail.DevicePath, Marshal.GetLastWin32Error())); // open a read/write handle to our device using the DevicePath returned bool closeHandle = true; handle = HIDImports.CreateFile(diDetail.DevicePath, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, HIDImports.EFileAttributes.Overlapped, IntPtr.Zero); // create an attributes struct and initialize the size HIDImports.HIDD_ATTRIBUTES attrib = new HIDImports.HIDD_ATTRIBUTES(); attrib.Size = Marshal.SizeOf(attrib); // get the attributes of the current device if (HIDImports.HidD_GetAttributes(handle.DangerousGetHandle(), ref attrib)) { // if the vendor and product IDs match up if (attrib.VendorID == WiiInputManager.VID) { if (attrib.ProductID == WiiInputManager.PID_old || attrib.ProductID == WiiInputManager.PID_new) { found = CheckWiiControllerType(handle, controllerType, diDetail, wiiControllerFound); if (found) { // it's the right WiiController Debug.WriteLine("Found one and open!"); } // stop at first found if (breakIfFoundOne) { break; } // dont close the handle closeHandle = false; } } } if (closeHandle) { handle.Close(); } } else { // failed to get the detail struct throw new WiiControllerException("SetupDiGetDeviceInterfaceDetail failed on index " + index); } // move to the next device index++; } // clean up our list HIDImports.SetupDiDestroyDeviceInfoList(hDevInfo); // if we didn't find a WiiController, throw an exception if (!found) { throw new WiiControllerNotFoundException("No WiiControllers found in HID device list."); } }