using System; using System.Diagnostics; using System.Text; using System.Runtime.InteropServices; using System.Windows.Forms; using DTS.Common.Utilities.Logging; namespace DTS.Common.USBFramework { public class DeviceManagement : IDisposable { public void Dispose() { } public bool DeviceNameMatch(Message m, string mydevicePathName) { // Purpose : Compares two device path names. Used to find out if the device name // : of a recently attached or removed device matches the name of a // : device the application is communicating with. // Accepts : m - a WM_DEVICECHANGE message. A call to RegisterDeviceNotification // : causes WM_DEVICECHANGE messages to be passed to an OnDeviceChange routine. // : mydevicePathName - a device pathname returned by SetupDiGetDeviceInterfaceDetail // : in an SP_DEVICE_INTERFACE_DETAIL_DATA structure. // Returns : True if the names match, False if not. var devBroadcastDeviceInterface = new DeviceManagementDeclarations.DEV_BROADCAST_DEVICEINTERFACE_1(); var devBroadcastHeader = new DeviceManagementDeclarations.DEV_BROADCAST_HDR(); // The LParam parameter of Message is a pointer to a DEV_BROADCAST_HDR structure. Marshal.PtrToStructure(m.LParam, devBroadcastHeader); if (devBroadcastHeader.dbch_devicetype != DeviceManagementDeclarations.DBT_DEVTYP_DEVICEINTERFACE) return false; // The dbch_devicetype parameter indicates that the event applies to a device interface. // So the structure in LParam is actually a DEV_BROADCAST_INTERFACE structure, // which begins with a DEV_BROADCAST_HDR. // Obtain the number of characters in dbch_name by subtracting the 28 bytes // in the other members of the structure and dividing by 2 because there are // 2 bytes per character. var stringSize = Convert.ToInt32((devBroadcastHeader.dbch_size - 28) / 2); // The dbcc_name parameter of DevBroadcastDeviceInterface contains the device name. // Trim dbcc_name to match the size of the string. devBroadcastDeviceInterface.dbcc_name = new char[stringSize + 1]; // Marshal data from the unmanaged block pointed to by m.LParam // to the managed object DevBroadcastDeviceInterface. Marshal.PtrToStructure(m.LParam, devBroadcastDeviceInterface); // Store the device name in a String. var deviceNameString = new string(devBroadcastDeviceInterface.dbcc_name, 0, stringSize); //Debug.WriteLine("Device Name = " + DeviceNameString); //Debug.WriteLine(""); // Compare the name of the newly attached device with the name of the device // the application is accessing (mydevicePathName). // Set ignorecase True. if (string.Compare(deviceNameString, mydevicePathName, true) == 0) { // The name matches. return true; } // It's a different device. return false; } public bool FindDeviceFromGuid(System.Guid myGuid, ref string[] devicePathName) { // Purpose : Uses SetupDi API functions to retrieve the device path name of an // : attached device that belongs to an interface class. // Accepts : myGuid - an interface class GUID. // : devicePathName - a pointer to an array of strings that will contain // : the device path names of attached devices. // Returns : True if at least one device is found, False if not. var lastDevice = false; var bufferSize = 0; var myDeviceInterfaceDetailData = new DeviceManagementDeclarations.SP_DEVICE_INTERFACE_DETAIL_DATA(); var myDeviceInterfaceData = new DeviceManagementDeclarations.SP_DEVICE_INTERFACE_DATA(); int result; var detailDataBuffer = IntPtr.Zero; // *** // API function: SetupDiGetClassDevs // Purpose: // Retrieves a device information set for a specified group of devices. // SetupDiEnumDeviceInterfaces uses the device information set. // Accepts: // An interface class GUID // Null to retrieve information for all device instances // An optional handle to a top-level window (unused here) // Flags to limit the returned information to currently present devices // and devices that expose interfaces in the class specified by the GUID. // Returns: // A handle to a device information set for the devices. // *** var deviceInfoSet = DeviceManagementDeclarations.SetupDiGetClassDevs(ref myGuid, null, 0, DeviceManagementDeclarations.DIGCF_PRESENT | DeviceManagementDeclarations.DIGCF_DEVICEINTERFACE); var deviceFound = false; var memberIndex = 0; try { do { // Begin with 0 and increment through the device information set until // no more devices are available. // The cbSize element of the MyDeviceInterfaceData structure must be set to // the structure's size in bytes. // The size is 28 bytes for 32-bit code and 32 bits for 64-bit code. myDeviceInterfaceData.cbSize = Marshal.SizeOf(myDeviceInterfaceData); // *** // API function: // SetupDiEnumDeviceInterfaces() // Purpose: Retrieves a handle to a SP_DEVICE_INTERFACE_DATA // structure for a device. // On return, MyDeviceInterfaceData contains the handle to a // SP_DEVICE_INTERFACE_DATA structure for a detected device. // Accepts: // A DeviceInfoSet returned by SetupDiGetClassDevs. // An interface class GUID. // An index to specify a device in a device information set. // A pointer to a handle to a SP_DEVICE_INTERFACE_DATA structure for a device. // Returns: // Non-zero on success, zero on True. // *** result = DeviceManagementDeclarations.SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref myGuid, memberIndex, ref myDeviceInterfaceData); // Find out if a device information set was retrieved. if (result == 0) { lastDevice = true; } else { // A device is present. // *** // API function: // SetupDiGetDeviceInterfaceDetail() // Purpose: // Retrieves an SP_DEVICE_INTERFACE_DETAIL_DATA structure // containing information about a device. // To retrieve the information, call this function twice. // The first time returns the size of the structure. // The second time returns a pointer to the data. // Accepts: // A DeviceInfoSet returned by SetupDiGetClassDevs // An SP_DEVICE_INTERFACE_DATA structure returned by SetupDiEnumDeviceInterfaces // A pointer to an SP_DEVICE_INTERFACE_DETAIL_DATA structure to receive information // about the specified interface. // The size of the SP_DEVICE_INTERFACE_DETAIL_DATA structure. // A pointer to a variable that will receive the returned required size of the // SP_DEVICE_INTERFACE_DETAIL_DATA structure. // A pointer to an SP_DEVINFO_DATA structure to receive information about the device. // Returns: // Non-zero on success, zero on failure. // *** DeviceManagementDeclarations.SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref myDeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, IntPtr.Zero); // Store the structure's size. myDeviceInterfaceDetailData.cbSize = Marshal.SizeOf(myDeviceInterfaceDetailData); // Allocate memory for the MyDeviceInterfaceDetailData Structure using the returned buffer size. detailDataBuffer = Marshal.AllocHGlobal(bufferSize); // Store cbSize in the first 4 bytes of the array if (IS64_BIT_PROCESS) { Marshal.WriteInt64(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8); } else { Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8); } // Call SetupDiGetDeviceInterfaceDetail again. // This time, pass a pointer to DetailDataBuffer // and the returned required buffer size. DeviceManagementDeclarations.SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref myDeviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, IntPtr.Zero); // Skip over cbsize (4 bytes) to get the address of the devicePathName. var pdevicePathName = IS64_BIT_PROCESS ? new IntPtr(detailDataBuffer.ToInt64() + 4) : new IntPtr(detailDataBuffer.ToInt32() + 4); // Get the String containing the devicePathName. var singledevicePathName = Marshal.PtrToStringAuto(pdevicePathName); devicePathName[memberIndex] = singledevicePathName; // Free the memory allocated previously by AllocHGlobal. Marshal.FreeHGlobal(detailDataBuffer); detailDataBuffer = IntPtr.Zero; deviceFound = true; } memberIndex++; } while (!lastDevice); } finally { if (detailDataBuffer != IntPtr.Zero) { // Free the memory allocated previously by AllocHGlobal. Marshal.FreeHGlobal(detailDataBuffer); } if (deviceInfoSet != IntPtr.Zero) { // *** // API function // summary // Frees the memory reserved for the DeviceInfoSet returned by SetupDiGetClassDevs. // parameters // DeviceInfoSet returned by SetupDiGetClassDevs. // returns // True on success. // *** // Trim the array to the number of devices found. // *** // API function: // SetupDiDestroyDeviceInfoList // Purpose: // Frees the memory reserved for the DeviceInfoSet returned by SetupDiGetClassDevs. // Accepts: // A DeviceInfoSet returned by SetupDiGetClassDevs. // Returns: // True on success, False on failure. // *** DeviceManagementDeclarations.SetupDiDestroyDeviceInfoList(deviceInfoSet); } } return deviceFound; } public bool RegisterForDeviceNotifications(IntPtr formHandle, Guid classGuid, ref IntPtr deviceNotificationHandle) { // Purpose : Request to receive a notification when a device is attached or removed. // Accepts : devicePathName - a handle to a device. // : formHandle - a handle to the window that will receive device events. // : classGuid - an interface class GUID. // // Returns : True on success, False on failure. // A DEV_BROADCAST_DEVICEINTERFACE header holds information about the request. var dbdi = new DeviceManagementDeclarations.DEV_BROADCAST_DEVICEINTERFACE(); // Set the parameters in the DEV_BROADCAST_DEVICEINTERFACE structure. // Set the size. var size = Marshal.SizeOf(dbdi); dbdi.dbcc_size = size; // Request to receive notifications about a class of devices. dbdi.dbcc_devicetype = DeviceManagementDeclarations.DBT_DEVTYP_DEVICEINTERFACE; dbdi.dbcc_reserved = 0; // Specify the interface class to receive notifications about. dbdi.dbcc_classguid = classGuid; dbdi.dbcc_name = ""; // Allocate memory for the buffer that holds the DEV_BROADCAST_DEVICEINTERFACE structure. var buffer = Marshal.AllocHGlobal(size); // Copy the DEV_BROADCAST_DEVICEINTERFACE structure to the buffer. // Set fDeleteOld True to prevent memory leaks. Marshal.StructureToPtr(dbdi, buffer, true); // *** // API function: // RegisterDeviceNotification // Purpose: // Request to receive notification messages when a device in an interface class // is attached or removed. // Accepts: // Aa handle to the window that will receive device events // A pointer to a DEV_BROADCAST_DEVICEINTERFACE to specify the type of // device to send notifications for, // DEVICE_NOTIFY_WINDOW_HANDLE to indicate that Handle is a window handle. // Returns: // A device notification handle or NULL on failure. // *** deviceNotificationHandle = DeviceManagementDeclarations.RegisterDeviceNotification(formHandle, buffer, DeviceManagementDeclarations.DEVICE_NOTIFY_WINDOW_HANDLE); // Marshal data from the unmanaged block DevBroadcastDeviceInterfaceBuffer to // the managed object DevBroadcastDeviceInterface //Marshal.PtrToStructure(buffer, dbdi); // Free the memory allocated previously by AllocHGlobal. Marshal.FreeHGlobal(buffer); // Find out if RegisterDeviceNotification was successful. if (IS64_BIT_PROCESS) { return deviceNotificationHandle.ToInt64() != IntPtr.Zero.ToInt64(); } return deviceNotificationHandle.ToInt32() != IntPtr.Zero.ToInt32(); } static readonly bool IS64_BIT_PROCESS = (IntPtr.Size == 8); public void StopReceivingDeviceNotifications(IntPtr deviceNotificationHandle) { // Purpose : Requests to stop receiving notification messages when a device in an // interface class is attached or removed. // Accepts : deviceNotificationHandle - a handle returned previously by // RegisterDeviceNotification // *** // API function: UnregisterDeviceNotification // Purpose: Stop receiving notification messages. // Accepts: a handle returned previously by RegisterDeviceNotification // Returns: True on success, False on failure. // *** // Ignore failures. DeviceManagementDeclarations.UnregisterDeviceNotification(deviceNotificationHandle); } public bool GetDeviceRegistryProperty(Guid myGuid) { const int maxDevLen = 1000; // Create a HDEVINFO with all present devices. var deviceInfoSet = DeviceManagementDeclarations.SetupDiGetClassDevs(ref myGuid, // null? null, // Enumerator 0, DeviceManagementDeclarations.DIGCF_PRESENT | DeviceManagementDeclarations.DIGCF_ALLCLASSES); if ((int)deviceInfoSet == FileIODeclarations.INVALID_HANDLE_VALUE) { // Insert error handling here. return false; } // Enumerate through all devices in Set. DeviceManagementDeclarations.SP_DEVINFO_DATA deviceInfoData; deviceInfoData.cbSize = Marshal.SizeOf(typeof(DeviceManagementDeclarations.SP_DEVINFO_DATA)); for (uint i = 0; DeviceManagementDeclarations.SetupDiEnumDeviceInfo(deviceInfoSet, i, out deviceInfoData); i++) { var deviceName = new StringBuilder(maxDevLen); if (!DeviceManagementDeclarations.SetupDiGetDeviceRegistryPropertyA(deviceInfoSet, deviceInfoData, DeviceManagementDeclarations.SPDRP_DRIVER, 0, deviceName, maxDevLen, IntPtr.Zero)) { //incorrect device name: DeviceManagementDeclarations.SetupDiDestroyDeviceInfoList(deviceInfoSet); deviceName = new StringBuilder(""); return false; } APILogger.Log(deviceName.ToString()); } if (DeviceManagementDeclarations.GetLastError() != DeviceManagementDeclarations.NO_ERROR && DeviceManagementDeclarations.GetLastError() != DeviceManagementDeclarations.ERROR_NO_MORE_ITEMS) { try { var theGuid = Guid.Empty; if (null != myGuid) { theGuid = myGuid; } var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); APILogger.Log("GetDeviceRegistryProperty(", theGuid.ToString(), ") error - ", w32.Message); } catch { } // Insert error handling here. return false; } // Cleanup DeviceManagementDeclarations.SetupDiDestroyDeviceInfoList(deviceInfoSet); return true; } } }