/*public static bool GetDeviceByClass(String DeviceInterfaceGUID) { IntPtr IntPtrBuffer = Marshal.AllocHGlobal(BUFFER_SIZE); Win32Wrapper.WinErrors LastError; bool Status = false; Guid DeviceGUID = new Guid(DeviceInterfaceGUID); try { //Get a handle to a device information set that contains all installed devices that support the Device Infomation Interface (GUID) IntPtr h = Win32Wrapper.SetupDiGetClassDevs(ref DeviceGUID, IntPtr.Zero, IntPtr.Zero, (uint)(Win32Wrapper.DIGCF.DIGCF_PRESENT | Win32Wrapper.DIGCF.DIGCF_DEVICEINTERFACE)); if (h.ToInt32() != INVALID_HANDLE_VALUE) { bool Success = true; uint i = 0; UInt32 RequiredSize = 0; while (Success) { //Create a Device Interface Data structure Win32Wrapper.SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new Win32Wrapper.SP_DEVICE_INTERFACE_DATA(); DeviceInterfaceData.cbSize = (uint)Marshal.SizeOf(DeviceInterfaceData); //Start the enumeration Success = Win32Wrapper.SetupDiEnumDeviceInterfaces(h, IntPtr.Zero, ref DeviceGUID, i, ref DeviceInterfaceData); if (!Success) { LastError = (Win32Wrapper.WinErrors)Marshal.GetLastWin32Error(); if (LastError == Win32Wrapper.WinErrors.ERROR_NO_MORE_ITEMS) { Status = false; break; } else { throw new Exception("Error enumerating devices!"); } } //Create a Device Info Data structure Win32Wrapper.SP_DEVINFO_DATA DeviceInfoData = new Win32Wrapper.SP_DEVINFO_DATA(); DeviceInfoData.cbSize = (uint)Marshal.SizeOf(DeviceInfoData); //Success = Win32Wrapper.SetupDiGetDeviceInterfaceDetail(h, ref DeviceInterfaceData, ref IntPtr.Zero, 0, out RequiredSize, ref IntPtr.Zero); //Create a Device Interface Detail Data structure Win32Wrapper.SP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData = new Win32Wrapper.SP_DEVICE_INTERFACE_DETAIL_DATA(); if (IntPtr.Size == 8) //For 64 bit operating systems DeviceInterfaceDetailData.cbSize = 8; else DeviceInterfaceDetailData.cbSize = (uint)(4 + Marshal.SystemDefaultCharSize); //For 32 bit systems Success = Win32Wrapper.SetupDiGetDeviceInterfaceDetail(h, ref DeviceInterfaceData, ref DeviceInterfaceDetailData, BUFFER_SIZE, out RequiredSize, ref DeviceInfoData); if (!Success) { throw new Exception("Error enumerating devices!"); } //Is this Port a USB Port? Ask the parent, then it's parent and so on UInt32 StartingDevice = DeviceInfoData.DevInst; Win32Wrapper.CRErrorCodes CRResult; bool IsRemainDevices = true; while(IsRemainDevices) { UInt32 hParentDevice = 0; CRResult = (Win32Wrapper.CRErrorCodes)Win32Wrapper.CM_Get_Parent(out hParentDevice, StartingDevice, 0); if (CRResult == Win32Wrapper.CRErrorCodes.CR_NO_SUCH_DEVNODE) { IsRemainDevices = true;//We hit the top of the PNP tree break; } if (CRResult != Win32Wrapper.CRErrorCodes.CR_SUCCESS) { throw new Exception("Error calling CM_Get_Parent: " + CRResult.ToString()); } if (IsRemainDevices) { CRResult = (Win32Wrapper.CRErrorCodes)Win32Wrapper.CM_Get_Device_ID(hParentDevice, IntPtrBuffer, BUFFER_SIZE, 0); if (CRResult != Win32Wrapper.CRErrorCodes.CR_SUCCESS) { throw new Exception("Error calling CM_Get_Device_ID: " + CRResult.ToString()); } String DeviceID = Marshal.PtrToStringAuto(IntPtrBuffer); if (DeviceID.StartsWith("USB\\")) { Status = true; } } //Do the next parent StartingDevice = hParentDevice; } //End of while(IsRemainDevices) i++; } //End of while (Success) } //End of if (h.ToInt32() != INVALID_HANDLE_VALUE) Win32Wrapper.SetupDiDestroyDeviceInfoList(h); //Clean up the old structure we no longer need. return Status; } catch (Exception ex) { throw ex; } finally { Marshal.FreeHGlobal(IntPtrBuffer); } }*/ /// <summary> /// Enumerate all USB devices and look for the device whose VID and PID are provided. /// </summary> /// <param name="VID">The Vendor ID of the USB Device.</param> /// <param name="PID">The Product ID of the USB Device.</param> /// <param name="DP">A Device Properties structure.</param> /// <param name="GetCOMPort">Set to True to retrieve the COM Port number if the Device is emulating a Serial Port.</param> /// <returns>True the Device is found.</returns> public static bool GetUSBDevice(UInt32 VID, UInt32 PID, ref DeviceProperties DP, bool GetCOMPort) { IntPtr IntPtrBuffer = Marshal.AllocHGlobal(BUFFER_SIZE); IntPtr h = IntPtr.Zero; Win32Wrapper.WinErrors LastError; bool Status = false; try { string DevEnum = "USB"; string ExpectedDeviceID = "VID_" + VID.ToString("X4") + "&" + "PID_" + PID.ToString("X4"); ExpectedDeviceID = ExpectedDeviceID.ToLowerInvariant(); h = Win32Wrapper.SetupDiGetClassDevs(IntPtr.Zero, DevEnum, IntPtr.Zero, (int)(Win32Wrapper.DIGCF.DIGCF_PRESENT | Win32Wrapper.DIGCF.DIGCF_ALLCLASSES)); if (h.ToInt32() != INVALID_HANDLE_VALUE) { bool Success = true; uint i = 0; while (Success) { if (Success) { UInt32 RequiredSize = 0; UInt32 RegType = 0; IntPtr Ptr = IntPtr.Zero; //Create a Device Info Data structure Win32Wrapper.SP_DEVINFO_DATA DevInfoData = new Win32Wrapper.SP_DEVINFO_DATA(); DevInfoData.cbSize = (uint)Marshal.SizeOf(DevInfoData); Success = Win32Wrapper.SetupDiEnumDeviceInfo(h, i, ref DevInfoData); if (Success) { //Get the required buffer size //First query for the size of the hardware ID, so we can know how big a buffer to allocate for the data. Win32Wrapper.SetupDiGetDeviceRegistryProperty(h, ref DevInfoData, (UInt32)Win32Wrapper.SPDRP.SPDRP_HARDWAREID, ref RegType, IntPtr.Zero, 0, ref RequiredSize); LastError = (Win32Wrapper.WinErrors)Marshal.GetLastWin32Error(); if (LastError == Win32Wrapper.WinErrors.ERROR_INSUFFICIENT_BUFFER) { if (RequiredSize > BUFFER_SIZE) { Status = false; } else { if (Win32Wrapper.SetupDiGetDeviceRegistryProperty(h, ref DevInfoData, (UInt32)Win32Wrapper.SPDRP.SPDRP_HARDWAREID, ref RegType, IntPtrBuffer, BUFFER_SIZE, ref RequiredSize)) { string HardwareID = Marshal.PtrToStringAuto(IntPtrBuffer); HardwareID = HardwareID.ToLowerInvariant(); if (HardwareID.Contains(ExpectedDeviceID)) { Status = true; //Found device if (Win32Wrapper.SetupDiGetDeviceRegistryProperty(h, ref DevInfoData, (UInt32)Win32Wrapper.SPDRP.SPDRP_FRIENDLYNAME, ref RegType, IntPtrBuffer, BUFFER_SIZE, ref RequiredSize)) { DP.FriendlyName = Marshal.PtrToStringAuto(IntPtrBuffer); } if (Win32Wrapper.SetupDiGetDeviceRegistryProperty(h, ref DevInfoData, (UInt32)Win32Wrapper.SPDRP.SPDRP_DEVTYPE, ref RegType, IntPtrBuffer, BUFFER_SIZE, ref RequiredSize)) { DP.DeviceType = Marshal.PtrToStringAuto(IntPtrBuffer); } if (Win32Wrapper.SetupDiGetDeviceRegistryProperty(h, ref DevInfoData, (UInt32)Win32Wrapper.SPDRP.SPDRP_CLASS, ref RegType, IntPtrBuffer, BUFFER_SIZE, ref RequiredSize)) { DP.DeviceClass = Marshal.PtrToStringAuto(IntPtrBuffer); } if (Win32Wrapper.SetupDiGetDeviceRegistryProperty(h, ref DevInfoData, (UInt32)Win32Wrapper.SPDRP.SPDRP_MFG, ref RegType, IntPtrBuffer, BUFFER_SIZE, ref RequiredSize)) { DP.DeviceManufacturer = Marshal.PtrToStringAuto(IntPtrBuffer); } if (Win32Wrapper.SetupDiGetDeviceRegistryProperty(h, ref DevInfoData, (UInt32)Win32Wrapper.SPDRP.SPDRP_LOCATION_INFORMATION, ref RegType, IntPtrBuffer, BUFFER_SIZE, ref RequiredSize)) { DP.DeviceLocation = Marshal.PtrToStringAuto(IntPtrBuffer); } if (Win32Wrapper.SetupDiGetDeviceRegistryProperty(h, ref DevInfoData, (UInt32)Win32Wrapper.SPDRP.SPDRP_LOCATION_PATHS, ref RegType, IntPtrBuffer, BUFFER_SIZE, ref RequiredSize)) { DP.DevicePath = Marshal.PtrToStringAuto(IntPtrBuffer); } if (Win32Wrapper.SetupDiGetDeviceRegistryProperty(h, ref DevInfoData, (UInt32)Win32Wrapper.SPDRP.SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, ref RegType, IntPtrBuffer, BUFFER_SIZE, ref RequiredSize)) { DP.DevicePhysicalObjectName = Marshal.PtrToStringAuto(IntPtrBuffer); } if (Win32Wrapper.SetupDiGetDeviceRegistryProperty(h, ref DevInfoData, (UInt32)Win32Wrapper.SPDRP.SPDRP_DEVICEDESC, ref RegType, IntPtrBuffer, BUFFER_SIZE, ref RequiredSize)) { DP.DeviceDescription = Marshal.PtrToStringAuto(IntPtrBuffer); } if (GetCOMPort) { //Open the Device Parameters Key of the device IntPtr hDeviceRegistryKey; hDeviceRegistryKey = Win32Wrapper.SetupDiOpenDevRegKey(h, ref DevInfoData, (UInt32)Win32Wrapper.DICS_FLAG.DICS_FLAG_GLOBAL, 0, (UInt32)Win32Wrapper.DIREG.DIREG_DEV, (UInt32)Win32Wrapper.REGKEYSECURITY.KEY_READ); if (hDeviceRegistryKey.ToInt32() == INVALID_HANDLE_VALUE) { LastError = (Win32Wrapper.WinErrors)Marshal.GetLastWin32Error(); break; //throw new Exception("Error opening the Registry: " + LastError.ToString()); } else { UInt32 Type = 0; System.Text.StringBuilder Data = new System.Text.StringBuilder(BUFFER_SIZE); UInt32 Size = (UInt32)Data.Capacity; int Result; Result = Win32Wrapper.RegQueryValueEx(hDeviceRegistryKey, "PortName", 0, out Type, Data, ref Size); if (Result == (int)Win32Wrapper.WinErrors.ERROR_SUCCESS) { DP.COMPort = Data.ToString(); } //Close Registry Win32Wrapper.RegCloseKey(hDeviceRegistryKey); } } break; } else { Status = false; } //End of if (HardwareID.Contains(ExpectedDeviceID)) } } //End of if (RequiredSize > BUFFER_SIZE) } //End of if (LastError == Win32Wrapper.WinErrors.ERROR_INSUFFICIENT_BUFFER) } // End of if (Success) } // End of if (Success) else { LastError = (Win32Wrapper.WinErrors)Marshal.GetLastWin32Error(); Status = false; } i++; } // End of while (Success) } //End of if (h.ToInt32() != INVALID_HANDLE_VALUE) return Status; } catch (Exception ex) { throw ex; } finally { Win32Wrapper.SetupDiDestroyDeviceInfoList(h); //Clean up the old structure we no longer need. Marshal.FreeHGlobal(IntPtrBuffer); } }
// FUNCTION: GetUSBDevicePath() // PURPOSE: Check if a USB device is currently plugged in with a matching VendorID and ProductID // INPUT: Uses globally declared String DevicePath, globally declared GUID, and the MY_DEVICE_ID constant. // OUTPUT: Returns BOOL. TRUE when device with matching VID/PID found. FALSE if device with VID/PID could not be found. // When returns TRUE, the globally accessable "DetailedInterfaceDataStructure" will contain the device path // to the USB device with the matching VID/PID. bool GetUSBDevicePath() { /* Before we can "connect" our application to our USB embedded device, we must first find the device. USB can have many devices simultaneously connected, so somehow we have to find our device only. This is done with the Vendor ID (VID) and Product ID (PID). Each USB product line should have a unique combination of VID and PID. Microsoft has created a number of functions which are useful for finding plug and play devices. Documentation for each function used can be found in the MSDN library. We will be using the following functions (unmanaged C functions): SetupDiGetClassDevs() //provided by setupapi.dll, which comes with Windows SetupDiEnumDeviceInterfaces() //provided by setupapi.dll, which comes with Windows GetLastError() //provided by kernel32.dll, which comes with Windows SetupDiDestroyDeviceInfoList() //provided by setupapi.dll, which comes with Windows SetupDiGetDeviceInterfaceDetail() //provided by setupapi.dll, which comes with Windows SetupDiGetDeviceRegistryProperty() //provided by setupapi.dll, which comes with Windows CreateFile() //provided by kernel32.dll, which comes with Windows In order to call these unmanaged functions, the Marshal class is very useful. We will also be using the following unusual data types and structures. Documentation can also be found in the MSDN library: PSP_DEVICE_INTERFACE_DATA PSP_DEVICE_INTERFACE_DETAIL_DATA SP_DEVINFO_DATA HDEVINFO HANDLE GUID The ultimate objective of the following code is to get the device path, which will be used elsewhere for getting read and write handles to the USB device. Once the read/write handles are opened, only then can this PC application begin reading/writing to the USB device using the WriteFile() and ReadFile() functions. Getting the device path is a multi-step round about process, which requires calling several of the SetupDixxx() functions provided by setupapi.dll. */ try { IntPtr deviceInfoTable = IntPtr.Zero; Win32Wrapper.SP_DEVICE_INTERFACE_DATA interfaceDataStructure = new Win32Wrapper.SP_DEVICE_INTERFACE_DATA(); Win32Wrapper.SP_DEVICE_INTERFACE_DETAIL_DATA detailedInterfaceDataStructure = new Win32Wrapper.SP_DEVICE_INTERFACE_DETAIL_DATA(); Win32Wrapper.SP_DEVINFO_DATA devInfoData = new Win32Wrapper.SP_DEVINFO_DATA(); uint interfaceIndex = 0; uint dwRegType = 0; uint dwRegSize = 0; uint dwRegSize2 = 0; uint structureSize = 0; IntPtr propertyValueBuffer = IntPtr.Zero; uint errorStatus; uint loopCounter = 0; // First populate a list of plugged in devices (by specifying "DIGCF_PRESENT"), which are of the specified class GUID. deviceInfoTable = Win32Wrapper.SetupDiGetClassDevs(ref Win32Wrapper.InterfaceClassGuid, IntPtr.Zero, IntPtr.Zero, Win32Wrapper.DIGCF_PRESENT | Win32Wrapper.DIGCF_DEVICEINTERFACE); if (deviceInfoTable != IntPtr.Zero) { // Now look through the list we just populated. We are trying to see if any of them match our device. while (true) { interfaceDataStructure.cbSize = (uint)Marshal.SizeOf(interfaceDataStructure); if (Win32Wrapper.SetupDiEnumDeviceInterfaces(deviceInfoTable, IntPtr.Zero, ref Win32Wrapper.InterfaceClassGuid, interfaceIndex, ref interfaceDataStructure)) { errorStatus = (uint)Marshal.GetLastWin32Error(); if (errorStatus == Win32Wrapper.ERROR_NO_MORE_ITEMS) // Did we reach the end of the list of matching devices in the DeviceInfoTable? { // Cound not find the device. Must not have been attached. Win32Wrapper.SetupDiDestroyDeviceInfoList(deviceInfoTable); // Clean up the old structure we no longer need. return false; } } else // Else some other kind of unknown error ocurred... { errorStatus = (uint)Marshal.GetLastWin32Error(); Win32Wrapper.SetupDiDestroyDeviceInfoList(deviceInfoTable); // Clean up the old structure we no longer need. return false; } // Now retrieve the hardware ID from the registry. The hardware ID contains the VID and PID, which we will then // check to see if it is the correct device or not. // Initialize an appropriate SP_DEVINFO_DATA structure. We need this structure for SetupDiGetDeviceRegistryProperty(). devInfoData.cbSize = (uint)Marshal.SizeOf(devInfoData); Win32Wrapper.SetupDiEnumDeviceInfo(deviceInfoTable, interfaceIndex, ref devInfoData); // First query for the size of the hardware ID, so we can know how big a buffer to allocate for the data. Win32Wrapper.SetupDiGetDeviceRegistryProperty(deviceInfoTable, ref devInfoData, Win32Wrapper.SPDRP_HARDWAREID, ref dwRegType, IntPtr.Zero, 0, ref dwRegSize); // Allocate a buffer for the hardware ID. // Should normally work, but could throw exception "OutOfMemoryException" if not enough resources available. propertyValueBuffer = Marshal.AllocHGlobal((int)dwRegSize); // Retrieve the hardware IDs for the current device we are looking at. PropertyValueBuffer gets filled with a // REG_MULTI_SZ (array of null terminated strings). To find a device, we only care about the very first string in the // buffer, which will be the "device ID". The device ID is a string which contains the VID and PID, in the example // format "Vid_04d8&Pid_003f". Win32Wrapper.SetupDiGetDeviceRegistryProperty(deviceInfoTable, ref devInfoData, Win32Wrapper.SPDRP_HARDWAREID, ref dwRegType, propertyValueBuffer, dwRegSize, ref dwRegSize2); // Now check if the first string in the hardware ID matches the device ID of the USB device we are trying to find. String DeviceIDFromRegistry = Marshal.PtrToStringUni(propertyValueBuffer); // Make a new string, fill it with the contents from the PropertyValueBuffer Marshal.FreeHGlobal(propertyValueBuffer); // No longer need the PropertyValueBuffer, free the memory to prevent potential memory leaks // Convert both strings to lower case. This makes the code more robust/portable accross OS Versions DeviceIDFromRegistry = DeviceIDFromRegistry.ToLowerInvariant(); DeviceIDToFind = DeviceIDToFind.ToLowerInvariant(); // Now check if the hardware ID we are looking at contains the correct VID/PID if (DeviceIDFromRegistry.Contains(DeviceIDToFind)) { // Device must have been found. In order to open I/O file handle(s), we will need the actual device path first. // We can get the path by calling SetupDiGetDeviceInterfaceDetail(), however, we have to call this function twice: The first // time to get the size of the required structure/buffer to hold the detailed interface data, then a second time to actually // get the structure (after we have allocated enough memory for the structure.) detailedInterfaceDataStructure.cbSize = (uint)Marshal.SizeOf(detailedInterfaceDataStructure); // First call populates "StructureSize" with the correct value Win32Wrapper.SetupDiGetDeviceInterfaceDetail(deviceInfoTable, ref interfaceDataStructure, IntPtr.Zero, 0, ref structureSize, IntPtr.Zero); // Need to call SetupDiGetDeviceInterfaceDetail() again, this time specifying a pointer to a SP_DEVICE_INTERFACE_DETAIL_DATA buffer with the correct size of RAM allocated. // First need to allocate the unmanaged buffer and get a pointer to it. IntPtr pUnmanagedDetailedInterfaceDataStructure = IntPtr.Zero; // Declare a pointer. pUnmanagedDetailedInterfaceDataStructure = Marshal.AllocHGlobal((int)structureSize); // Reserve some unmanaged memory for the structure. detailedInterfaceDataStructure.cbSize = 6; // Initialize the cbSize parameter (4 bytes for DWORD + 2 bytes for unicode null terminator) Marshal.StructureToPtr(detailedInterfaceDataStructure, pUnmanagedDetailedInterfaceDataStructure, false); //Copy managed structure contents into the unmanaged memory buffer. // Now call SetupDiGetDeviceInterfaceDetail() a second time to receive the device path in the structure at pUnmanagedDetailedInterfaceDataStructure. if (Win32Wrapper.SetupDiGetDeviceInterfaceDetail(deviceInfoTable, ref interfaceDataStructure, pUnmanagedDetailedInterfaceDataStructure, structureSize, IntPtr.Zero, IntPtr.Zero)) { // Need to extract the path information from the unmanaged "structure". The path starts at (pUnmanagedDetailedInterfaceDataStructure + sizeof(DWORD)). IntPtr pToDevicePath = new IntPtr((uint)pUnmanagedDetailedInterfaceDataStructure.ToInt32() + 4); //Add 4 to the pointer (to get the pointer to point to the path, instead of the DWORD cbSize parameter) DevicePath = Marshal.PtrToStringUni(pToDevicePath); // Now copy the path information into the globally defined DevicePath String. // We now have the proper device path, and we can finally use the path to open I/O handle(s) to the device. Win32Wrapper.SetupDiDestroyDeviceInfoList(deviceInfoTable); // Clean up the old structure we no longer need. Marshal.FreeHGlobal(pUnmanagedDetailedInterfaceDataStructure); // No longer need this unmanaged SP_DEVICE_INTERFACE_DETAIL_DATA buffer. We already extracted the path information. return true; // Returning the device path in the global DevicePath String } else // Some unknown failure occurred { uint errorCode = (uint)Marshal.GetLastWin32Error(); Win32Wrapper.SetupDiDestroyDeviceInfoList(deviceInfoTable); // Clean up the old structure. Marshal.FreeHGlobal(pUnmanagedDetailedInterfaceDataStructure); // No longer need this unmanaged SP_DEVICE_INTERFACE_DETAIL_DATA buffer. We already extracted the path information. return false; } } interfaceIndex++; // Keep looping until we either find a device with matching VID and PID, or until we run out of devices to check. // However, just in case some unexpected error occurs, keep track of the number of loops executed. // If the number of loops exceeds a very large number, exit anyway, to prevent inadvertent infinite looping. if (++loopCounter == 10000000) // Surely there aren't more than 10 million devices attached to any forseeable PC... return false; } } return false; } catch { // something went wrong if we get here -- maybe a Marshal.AllocHGlobal() failed due to insufficient resources or something. return false; } }