changeset 50:556b4fb511bd

Added HWLib and USBLib functions to support USB Driver Installer
author Ivo Smits <Ivo@UCIS.nl>
date Mon, 30 Sep 2013 19:23:09 +0200
parents 15ddb1e0e2a5
children e3f37686b15a
files USBLib/Communication/WindowsUsbDeviceRegistry.cs USBLib/Internal/Windows/SetupApi.cs USBLib/Windows/Devices/DeviceNode.cs
diffstat 3 files changed, 120 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/USBLib/Communication/WindowsUsbDeviceRegistry.cs	Tue Jul 16 13:16:04 2013 +0200
+++ b/USBLib/Communication/WindowsUsbDeviceRegistry.cs	Mon Sep 30 19:23:09 2013 +0200
@@ -9,6 +9,29 @@
 	public abstract class WindowsUsbDeviceRegistry {
 		public DeviceNode DeviceNode { get; private set; }
 
+		public static Boolean DecodeDeviceIDs(DeviceNode device, out int vendorID, out int productID, out int revision, out int interfaceID) {
+			String[] hwids = device.GetPropertyStringArray(CMRDP.HARDWAREID);
+			String hwid = null;
+			if (hwids == null || hwids.Length < 1 || hwids[0].Length == 0) {
+				hwid = device.DeviceID;
+			} else {
+				hwid = hwids[0];
+			}
+			vendorID = productID = revision = interfaceID = -1;
+			foreach (String token in hwid.Split(new Char[] { '\\', '#', '&' }, StringSplitOptions.None)) {
+				if (token.StartsWith("VID_", StringComparison.InvariantCultureIgnoreCase)) {
+					if (!Int32.TryParse(token.Substring(4), NumberStyles.HexNumber, null, out vendorID)) vendorID = -1;
+				} else if (token.StartsWith("PID_", StringComparison.InvariantCultureIgnoreCase)) {
+					if (!Int32.TryParse(token.Substring(4), NumberStyles.HexNumber, null, out productID)) productID = -1;
+				} else if (token.StartsWith("REV_", StringComparison.InvariantCultureIgnoreCase)) {
+					if (!Int32.TryParse(token.Substring(4), NumberStyles.Integer, null, out revision)) revision = -1;
+				} else if (token.StartsWith("MI_", StringComparison.InvariantCultureIgnoreCase)) {
+					if (!Int32.TryParse(token.Substring(3), NumberStyles.HexNumber, null, out interfaceID)) interfaceID = -1;
+				}
+			}
+			return vendorID != -1 && productID != -1;
+		}
+
 		// Parsed out of the device ID
 		private bool mIsDeviceIDParsed;
 		private byte mInterfaceID;
@@ -22,8 +45,6 @@
 		public String DeviceID { get; private set; }
 		public String SymbolicName { get { return DevicePath; } }
 
-		private static Regex RegHardwareID = null;
-
 		protected WindowsUsbDeviceRegistry(DeviceNode device, String interfacepath) {
 			DeviceNode = device;
 			DeviceID = device.DeviceID;
@@ -39,28 +60,12 @@
 
 		private void parseDeviceID() {
 			if (mIsDeviceIDParsed) return;
-			if (RegHardwareID == null) {
-				RegexOptions OPTIONS = RegexOptions.CultureInvariant | RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase;
-				string PATTERN = "(Vid_(?<Vid>[0-9A-F]{1,4}))|(Pid_(?<Pid>[0-9A-F]{1,4}))|(Rev_(?<Rev>[0-9]{1,4}))|(MI_(?<MI>[0-9A-F]{1,2}))";
-				RegHardwareID = new Regex(PATTERN, OPTIONS);
-			}
-			String[] HardwareIDs = DeviceNode.GetPropertyStringArray(CMRDP.HARDWAREID);
-			String HardwareID = null;
-			if (HardwareIDs == null || HardwareIDs.Length < 1 || HardwareIDs[0].Length == 0) {
-				HardwareID = DeviceID;
-			} else {
-				HardwareID = HardwareIDs[0];
-			}
-			foreach (Match match in RegHardwareID.Matches(HardwareID)) {
-				Group group = match.Groups["Vid"];
-				if (group.Success) ushort.TryParse(group.Value, NumberStyles.HexNumber, null, out mVid);
-				group = match.Groups["Pid"];
-				if (group.Success) ushort.TryParse(group.Value, NumberStyles.HexNumber, null, out mPid);
-				group = match.Groups["Rev"];
-				if (group.Success) ushort.TryParse(group.Value, NumberStyles.Integer, null, out mRevision);
-				group = match.Groups["MI"];
-				if (group.Success) Byte.TryParse(group.Value, NumberStyles.HexNumber, null, out mInterfaceID);
-			}
+			int vid, pid, rev, mid;
+			if (!DecodeDeviceIDs(DeviceNode, out vid, out pid, out rev, out mid)) return;
+			mVid = (UInt16)vid;
+			mPid = (UInt16)pid;
+			mRevision = (UInt16)rev;
+			mInterfaceID = (Byte)mid;
 			mIsDeviceIDParsed = true;
 		}
 		public int Vid {
--- a/USBLib/Internal/Windows/SetupApi.cs	Tue Jul 16 13:16:04 2013 +0200
+++ b/USBLib/Internal/Windows/SetupApi.cs	Mon Sep 30 19:23:09 2013 +0200
@@ -41,14 +41,21 @@
 		public static extern bool SetupDiGetCustomDeviceProperty(SafeDeviceInfoSetHandle DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, string CustomPropertyName, DICUSTOMDEVPROP Flags, out RegistryValueKind PropertyRegDataType, Byte[] PropertyBuffer, int PropertyBufferSize, out int RequiredSize);
 		[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Ansi)]
 		public static extern bool SetupDiGetDeviceInstanceIdA(SafeDeviceInfoSetHandle DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, StringBuilder DeviceInstanceId, int DeviceInstanceIdSize, out int RequiredSize);
-		[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
-		public static extern bool SetupDiGetDeviceInterfacePropertyKeys(SafeDeviceInfoSetHandle DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, byte[] propKeyBuffer, int propKeyBufferElements, out int RequiredPropertyKeyCount, int Flags);
 		[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
 		public static extern bool SetupDiGetDeviceRegistryProperty(SafeDeviceInfoSetHandle DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, SPDRP Property, out RegistryValueKind PropertyRegDataType, byte[] PropertyBuffer, int PropertyBufferSize, out int RequiredSize);
 		[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
 		public static extern bool SetupDiGetDeviceRegistryProperty(SafeDeviceInfoSetHandle DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, SPDRP iProperty, out int PropertyRegDataType, IntPtr PropertyBuffer, int PropertyBufferSize, out int RequiredSize);
 		[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
 		public static extern bool SetupDiGetDeviceRegistryProperty(SafeDeviceInfoSetHandle DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, SPDRP iProperty, out int PropertyRegDataType, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder PropertyBuffer, int PropertyBufferSize, out int RequiredSize);
+		[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
+		public static extern bool SetupDiSetDeviceRegistryProperty(SafeDeviceInfoSetHandle DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, SPDRP Property, Byte[] PropertyBuffer, UInt32 PropertyBufferSize);
+
+		[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
+		public static extern IntPtr SetupDiOpenDevRegKey(SafeDeviceInfoSetHandle DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, UInt32 Scope, UInt32 HwProfile, UInt32 KeyType, UInt32 samDesired);
+		[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
+		public static extern bool SetupDiGetDeviceProperty(SafeDeviceInfoSetHandle DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, ref DEVPROPKEY PropertyKey, out UInt32 PropertyType, Byte[] PropertyBuffer, UInt32 PropertyBufferSize, out UInt32 RequiredSize, UInt32 Flags);
+		[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
+		public static extern bool SetupDiGetDeviceInterfacePropertyKeys(SafeDeviceInfoSetHandle DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, byte[] propKeyBuffer, int propKeyBufferElements, out int RequiredPropertyKeyCount, int Flags);
 
 		[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
 		public static extern bool SetupDiGetDeviceInstanceId(SafeDeviceInfoSetHandle DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, StringBuilder DeviceInstanceId, int DeviceInstanceIdSize, out int RequiredSize);
@@ -84,7 +91,8 @@
 		public static extern CR CM_Enumerate_Classes(UInt32 ulClassIndex, out Guid ClassGuid, UInt32 ulFlags);
 		[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
 		public static extern CR CM_Get_DevNode_Status(out UInt32 pulStatus, out UInt32 pulProblemNumber, UInt32 dnDevInst, UInt32 ulFlags);
-
+		[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
+		public static extern CR CM_Reenumerate_DevNode(UInt32 dnDevInst, UInt32 ulFlags);
 
 		//public const int DIGCF_DEFAULT = 0x00000001;  // only valid with DIGCF_DEVICEINTERFACE
 		public const int DIGCF_PRESENT = 0x00000002;
@@ -196,6 +204,12 @@
 		}
 	}
 
+	[StructLayout(LayoutKind.Sequential)]
+	struct DEVPROPKEY {
+		public Guid fmtid;
+		public UInt32 pid;
+	}
+
 	enum CR {
 		SUCCESS = (0x00000000),
 		DEFAULT = (0x00000001),
--- a/USBLib/Windows/Devices/DeviceNode.cs	Tue Jul 16 13:16:04 2013 +0200
+++ b/USBLib/Windows/Devices/DeviceNode.cs	Mon Sep 30 19:23:09 2013 +0200
@@ -1,9 +1,11 @@
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
+using System.Reflection;
 using System.Runtime.InteropServices;
 using System.Text;
 using Microsoft.Win32;
+using Microsoft.Win32.SafeHandles;
 using UCIS.USBLib.Internal.Windows;
 
 namespace UCIS.HWLib.Windows.Devices {
@@ -52,7 +54,10 @@
 			}
 		}
 		public static IList<DeviceNode> GetDevices(String enumerator) {
-			using (SafeDeviceInfoSetHandle dis = SetupApi.SetupDiGetClassDevsA(IntPtr.Zero, enumerator, IntPtr.Zero, DICFG.PRESENT | DICFG.ALLCLASSES)) {
+			return GetDevices(enumerator, true);
+		}
+		public static IList<DeviceNode> GetDevices(String enumerator, Boolean present) {
+			using (SafeDeviceInfoSetHandle dis = SetupApi.SetupDiGetClassDevsA(IntPtr.Zero, enumerator, IntPtr.Zero, (present ? DICFG.PRESENT : 0) | DICFG.ALLCLASSES)) {
 			//using (SafeDeviceInfoSetHandle dis = SetupApi.SetupDiGetClassDevsA(IntPtr.Zero, enumerator, IntPtr.Zero, DICFG.ALLCLASSES | DICFG.DEVICEINTERFACE)) {
 				return GetDevicesInSet(dis);
 			}
@@ -126,6 +131,17 @@
 			return SetupApi.GetAsStringArray(buffer, buffer.Length);
 		}
 
+		public void SetProperty(SPDRP property, Byte[] value) {
+			using (SafeDeviceInfoSetHandle dis = SetupApi.SetupDiGetClassDevsA(IntPtr.Zero, DeviceID, IntPtr.Zero, DICFG.DEVICEINTERFACE | DICFG.ALLCLASSES)) {
+				if (dis.IsInvalid) throw new Win32Exception(Marshal.GetLastWin32Error());
+				SP_DEVINFO_DATA dd = new SP_DEVINFO_DATA(true);
+				if (!SetupApi.SetupDiEnumDeviceInfo(dis, 0, ref dd))
+					throw new Win32Exception(Marshal.GetLastWin32Error());
+				if (!SetupApi.SetupDiSetDeviceRegistryProperty(dis, ref dd, property, value, (uint)value.Length))
+					throw new Win32Exception(Marshal.GetLastWin32Error());
+			}
+		}
+
 		public Byte[] GetCustomProperty(String name) {
 			using (SafeDeviceInfoSetHandle dis = SetupApi.SetupDiGetClassDevsA(IntPtr.Zero, DeviceID, IntPtr.Zero, DICFG.DEVICEINTERFACE | DICFG.ALLCLASSES)) {
 				if (dis.IsInvalid) throw new Win32Exception(Marshal.GetLastWin32Error());
@@ -158,6 +174,63 @@
 			return SetupApi.GetAsStringArray(buffer, buffer.Length);
 		}
 
+		public RegistryKey OpenRegistryKey(UInt32 scope, UInt32 hwProfile, UInt32 keyType, UInt32 samDesired) {
+			using (SafeDeviceInfoSetHandle dis = SetupApi.SetupDiGetClassDevsA(IntPtr.Zero, DeviceID, IntPtr.Zero, DICFG.DEVICEINTERFACE | DICFG.ALLCLASSES)) {
+				if (dis.IsInvalid) throw new Win32Exception(Marshal.GetLastWin32Error());
+				SP_DEVINFO_DATA dd = new SP_DEVINFO_DATA(true);
+				if (!SetupApi.SetupDiEnumDeviceInfo(dis, 0, ref dd))
+					return null;
+				IntPtr handle = SetupApi.SetupDiOpenDevRegKey(dis, ref dd, scope, hwProfile, keyType, samDesired);
+				if (handle == (IntPtr)(-1)) return null;
+				return RegistryKeyFromHandle(handle, true, (samDesired & (0x00000002 | 0x00000004 | 0x00000020)) != 0);
+			}
+		}
+
+		private RegistryKey RegistryKeyFromHandle(IntPtr hKey, bool writable, bool ownsHandle) {
+			BindingFlags privateConstructors = BindingFlags.Instance | BindingFlags.NonPublic;
+			Type safeRegistryHandleType = typeof(SafeHandleZeroOrMinusOneIsInvalid).Assembly.GetType("Microsoft.Win32.SafeHandles.SafeRegistryHandle");
+			Type[] safeRegistryHandleCtorTypes = new Type[] { typeof(IntPtr), typeof(bool) };
+			ConstructorInfo safeRegistryHandleCtorInfo = safeRegistryHandleType.GetConstructor(privateConstructors, null, safeRegistryHandleCtorTypes, null);
+			Object safeHandle = safeRegistryHandleCtorInfo.Invoke(new Object[] { hKey, ownsHandle });
+			Type registryKeyType = typeof(RegistryKey);
+			Type[] registryKeyConstructorTypes = new Type[] { safeRegistryHandleType, typeof(bool) };
+			ConstructorInfo registryKeyCtorInfo = registryKeyType.GetConstructor(privateConstructors, null, registryKeyConstructorTypes, null);
+			RegistryKey resultKey = (RegistryKey)registryKeyCtorInfo.Invoke(new Object[] { safeHandle, writable });
+			return resultKey;
+		}
+
+		public Byte[] GetDeviceProperty(Guid fmtid, UInt32 pid) {
+			using (SafeDeviceInfoSetHandle dis = SetupApi.SetupDiGetClassDevsA(IntPtr.Zero, DeviceID, IntPtr.Zero, DICFG.DEVICEINTERFACE | DICFG.ALLCLASSES)) {
+				if (dis.IsInvalid) throw new Win32Exception(Marshal.GetLastWin32Error());
+				SP_DEVINFO_DATA dd = new SP_DEVINFO_DATA(true);
+				if (!SetupApi.SetupDiEnumDeviceInfo(dis, 0, ref dd))
+					return null;
+				byte[] propBuffer = new byte[256];
+				UInt32 requiredSize;
+				UInt32 propertyType;
+				DEVPROPKEY propertyKey = new DEVPROPKEY() { fmtid = fmtid, pid = pid };
+				if (!SetupApi.SetupDiGetDeviceProperty(dis, ref dd, ref propertyKey, out propertyType, propBuffer, (uint)propBuffer.Length, out requiredSize, 0))
+					return null;
+				if (requiredSize > propBuffer.Length) {
+					propBuffer = new Byte[requiredSize];
+					if (!SetupApi.SetupDiGetDeviceProperty(dis, ref dd, ref propertyKey, out propertyType, propBuffer, (uint)propBuffer.Length, out requiredSize, 0))
+						throw new Win32Exception(Marshal.GetLastWin32Error());
+				}
+				if (requiredSize < propBuffer.Length) Array.Resize(ref propBuffer, (int)requiredSize);
+				return propBuffer;
+			}
+		}
+		public String GetDevicePropertyString(Guid fmtid, UInt32 pid) {
+			Byte[] buffer = GetDeviceProperty(fmtid, pid);
+			if (buffer == null) return null;
+			return SetupApi.GetAsString(buffer, buffer.Length);
+		}
+
+		public void Reenumerate(UInt32 flags) {
+			CR ret = SetupApi.CM_Reenumerate_DevNode(DevInst, flags);
+			CMException.Throw(ret, "CM_Reenumerate_DevNode");
+		}
+
 		public String DeviceDescription { get { return GetPropertyString(CMRDP.DEVICEDESC); } }
 		public String[] HardwareID { get { return GetPropertyStringArray(CMRDP.HARDWAREID); } }
 		public String[] CompatibleIDs { get { return GetPropertyStringArray(CMRDP.COMPATIBLEIDS); } }