分享

vs2015 C#实现usb虚拟串口自动识别和重新连接

 goodwangLib 2019-04-05

主要接口

GetPortNum   获取虚拟串口所在的COM端口号

DeletePort      删除指定的虚拟串口

Rescan           重新扫描硬件(在DeletePort之后调用)

主要问题

1 在做DeletePort的时候,SetupDiCallClassInstaller返回false,使用GetLastError发现错误码为E0000235

已解决,将PlatformTarget改成x64.

2 DeletePort 后Rescan,COM端口号会增加,怀疑是删除不彻底

已解决,使用SetupDiCallClassInstaller代替SetupDiRemoveDevice.

  1. public class DevManager
  2. {
  3. /*
  4. 自动查找VID/PID匹配的USB设备当作Virutal COM Port口
  5. 输入值: 字符串,如VID_0483&PID_5740
  6. 返回值: 0 - 没有找到对应设备
  7. */
  8. public static int GetPortNum(String vid_pid)
  9. {
  10. ManagementObjectCollection USBControllerDeviceCollection
  11. = new ManagementObjectSearcher("SELECT * FROM Win32_USBControllerDevice").Get();
  12. if (USBControllerDeviceCollection != null)
  13. {
  14. foreach (ManagementObject USBControllerDevice in USBControllerDeviceCollection)
  15. {
  16. String Dependent = (USBControllerDevice["Dependent"] as String).Split(new Char[] { '=' })[1];
  17. if (Dependent.Contains(vid_pid))
  18. {
  19. ManagementObjectCollection PnPEntityCollection
  20. = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity WHERE DeviceID=" + Dependent).Get();
  21. if (PnPEntityCollection != null)
  22. {
  23. foreach (ManagementObject Entity in PnPEntityCollection)
  24. {
  25. String DevName = Entity["Name"] as String;// 设备名称
  26. String PortNum = Regex.Replace(DevName, @"[^\d.\d]", "");
  27. return Convert.ToInt32(PortNum);
  28. }
  29. }
  30. }
  31. }
  32. }
  33. return -1;
  34. }
  35. /*
  36. 删除指定的usb虚拟串口
  37. 输入值: 设备管理器下的设备名称,不包括后面的(COMXX)
  38. 返回值: 成功/失败
  39. */
  40. /*!!!BUG: 删除port后重新扫描,port number会加一;插拔或者通过设备管理器Uninstall port没有这个问题。!!!*/
  41. public static bool DeletePort(String portName = null)
  42. {
  43. bool ret = false;
  44. Guid classGuid = Guid.Empty;
  45. IntPtr hDevInfo = Win32.SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, Win32.DIGCF_ALLCLASSES | Win32.DIGCF_PRESENT);
  46. if (hDevInfo.ToInt32() == Win32.INVALID_HANDLE_VALUE)
  47. {
  48. Console.WriteLine("访问硬件设备失败");
  49. }
  50. else
  51. {
  52. int i = 0;
  53. int selected = 0;
  54. StringBuilder deviceName = new StringBuilder();
  55. deviceName.Capacity = Win32.MAX_DEV_LEN;
  56. do
  57. {
  58. SP_DEVINFO_DATA devInfoData = new SP_DEVINFO_DATA();
  59. StringBuilder _DeviceName = new StringBuilder("");
  60. _DeviceName.Capacity = 1000;
  61. devInfoData.cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
  62. devInfoData.classGuid = Guid.Empty;
  63. devInfoData.devInst = 0;
  64. devInfoData.reserved = IntPtr.Zero;
  65. bool result = Win32.SetupDiEnumDeviceInfo(hDevInfo, i, devInfoData);
  66. if (false == result)
  67. {
  68. break;
  69. }
  70. Win32.SetupDiGetDeviceRegistryProperty(hDevInfo, devInfoData, 0, 0, _DeviceName, (uint)_DeviceName.Capacity, IntPtr.Zero);
  71. if (_DeviceName.ToString().Equals(portName))
  72. {
  73. Log.Show(Log.Level.Operation, _DeviceName.ToString() + ":" + Win32.GetDeviceInstanceId(hDevInfo, devInfoData).ToString());
  74. selected = i;
  75. }
  76. ++i;
  77. } while (true);
  78. ret = Win32.Remove(selected, hDevInfo);
  79. }
  80. Win32.SetupDiDestroyDeviceInfoList(hDevInfo);
  81. return ret;
  82. }
  83. /*
  84. 重新扫描硬件
  85. 输入值: 无
  86. 返回值: 成功/失败
  87. */
  88. public static bool Rescan()
  89. {
  90. UInt32 devRoot = 0;
  91. if (CM_Locate_DevNode_Ex(ref devRoot, null, 0, IntPtr.Zero) != CR_SUCCESS)
  92. {
  93. return false;
  94. }
  95. if (CM_Reenumerate_DevNode_Ex(devRoot, 0, IntPtr.Zero) != CR_SUCCESS)
  96. {
  97. return false;
  98. }
  99. return true;
  100. }
  101. public static bool DisablePort(String portName = null)
  102. {
  103. bool ret = false;
  104. Guid classGuid = Guid.Empty;
  105. IntPtr hDevInfo = Win32.SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, Win32.DIGCF_ALLCLASSES | Win32.DIGCF_PRESENT);
  106. if (hDevInfo.ToInt32() == Win32.INVALID_HANDLE_VALUE)
  107. {
  108. Console.WriteLine("访问硬件设备失败");
  109. }
  110. else
  111. {
  112. int i = 0;
  113. int selected = 0;
  114. StringBuilder deviceName = new StringBuilder();
  115. deviceName.Capacity = Win32.MAX_DEV_LEN;
  116. do
  117. {
  118. SP_DEVINFO_DATA devInfoData = new SP_DEVINFO_DATA();
  119. StringBuilder _DeviceName = new StringBuilder("");
  120. _DeviceName.Capacity = 1000;
  121. devInfoData.cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
  122. devInfoData.classGuid = Guid.Empty;
  123. devInfoData.devInst = 0;
  124. devInfoData.reserved = IntPtr.Zero;
  125. bool result = Win32.SetupDiEnumDeviceInfo(hDevInfo, i, devInfoData);
  126. if (false == result)
  127. {
  128. break;
  129. }
  130. Win32.SetupDiGetDeviceRegistryProperty(hDevInfo, devInfoData, 0, 0, _DeviceName, (uint)_DeviceName.Capacity, IntPtr.Zero);
  131. if (_DeviceName.ToString().Equals("USB Serial Port"))
  132. {
  133. Log.Show(Log.Level.Operation, _DeviceName.ToString());
  134. selected = i;
  135. }
  136. ++i;
  137. } while (true);
  138. ret = Win32.StateChange(false, selected, hDevInfo);
  139. }
  140. Win32.SetupDiDestroyDeviceInfoList(hDevInfo);
  141. return ret;
  142. }
  143. #region setupapi.dll elements
  144. [StructLayout(LayoutKind.Sequential)]
  145. public struct SP_BROADCAST_HANDLE
  146. {
  147. public int dbch_size;
  148. public int dbch_devicetype;
  149. public int dbch_reserved;
  150. public IntPtr dbch_handle;
  151. public IntPtr dbch_hdevnotify;
  152. public Guid dbch_eventguid;
  153. public long dbch_nameoffset;
  154. public byte dbch_data;
  155. public byte dbch_data1;
  156. }
  157. [StructLayout(LayoutKind.Sequential)]
  158. public class DEV_BROADCAST_DEVICEINTERFACE
  159. {
  160. public int dbcc_size;
  161. public int dbcc_devicetype;
  162. public int dbcc_reserved;
  163. }
  164. [StructLayout(LayoutKind.Sequential)]
  165. public class SP_DEVINFO_DATA
  166. {
  167. public int cbSize;
  168. public Guid classGuid;
  169. public int devInst;
  170. public IntPtr reserved;
  171. };
  172. [StructLayout(LayoutKind.Sequential)]
  173. public class SP_DEVINSTALL_PARAMS
  174. {
  175. public int cbSize;
  176. public int Flags;
  177. public int FlagsEx;
  178. public IntPtr hwndParent;
  179. public IntPtr InstallMsgHandler;
  180. public IntPtr InstallMsgHandlerContext;
  181. public IntPtr FileQueue;
  182. public IntPtr ClassInstallReserved;
  183. public int Reserved;
  184. [MarshalAs(UnmanagedType.LPTStr)]
  185. public string DriverPath;
  186. };
  187. [StructLayout(LayoutKind.Sequential)]
  188. public class SP_PROPCHANGE_PARAMS
  189. {
  190. public SP_CLASSINSTALL_HEADER ClassInstallHeader = new SP_CLASSINSTALL_HEADER();
  191. public int StateChange;
  192. public int Scope;
  193. public int HwProfile;
  194. };
  195. [StructLayout(LayoutKind.Sequential)]
  196. public class SP_CLASSINSTALL_HEADER
  197. {
  198. public int cbSize;
  199. public int InstallFunction;
  200. };
  201. public class Win32
  202. {
  203. [DllImport("kernel32.dll")]
  204. public static extern uint GetLastError();
  205. [DllImport("user32.dll", CharSet = CharSet.Auto)]
  206. public static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, DEV_BROADCAST_DEVICEINTERFACE NotificationFilter, UInt32 Flags);
  207. [DllImport("user32.dll", CharSet = CharSet.Auto)]
  208. public static extern UInt32 UnregisterDeviceNotification(IntPtr hHandle);
  209. [DllImport("setupapi.dll", SetLastError = true)]
  210. public static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid,
  211. [MarshalAs(UnmanagedType.LPStr)]String Enumerator, IntPtr hwndParent, Int32 Flags);
  212. [DllImport("setupapi.dll")]
  213. public static extern IntPtr SetupDiGetClassDevsEx(ref Guid ClassGuid,
  214. [MarshalAs(UnmanagedType.LPStr)]String Enumerator,
  215. IntPtr hwndParent, Int32 Flags, IntPtr DeviceInfoSet,
  216. [MarshalAs(UnmanagedType.LPStr)]String MachineName,
  217. IntPtr Reserved);
  218. [DllImport("setupapi.dll", SetLastError = true)]
  219. public static extern Int32 SetupDiDestroyDeviceInfoList(IntPtr lpInfoSet);
  220. [DllImport("setupapi.dll", SetLastError = true)]
  221. public static extern Boolean SetupDiEnumDeviceInfo(IntPtr lpInfoSet, Int32 dwIndex, SP_DEVINFO_DATA devInfoData);
  222. [DllImport("setupapi.dll", SetLastError = true)]
  223. public static extern Boolean SetupDiGetDeviceRegistryProperty(IntPtr lpInfoSet, SP_DEVINFO_DATA DeviceInfoData, UInt32 Property,
  224. UInt32 PropertyRegDataType, StringBuilder PropertyBuffer, UInt32 PropertyBufferSize, IntPtr RequiredSize);
  225. [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
  226. public static extern Boolean SetupDiSetClassInstallParams(IntPtr DeviceInfoSet, SP_DEVINFO_DATA DeviceInfoData, SP_PROPCHANGE_PARAMS ClassInstallParams, int ClassInstallParamsSize);
  227. [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
  228. public static extern Boolean SetupDiCallClassInstaller(UInt32 InstallFunction, IntPtr DeviceInfoSet, SP_DEVINFO_DATA DeviceInfoData);
  229. [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
  230. public static extern Boolean SetupDiRemoveDevice(IntPtr DeviceInfoSet, SP_DEVINFO_DATA DeviceInfoData);
  231. [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
  232. public static extern Boolean SetupDiClassNameFromGuid(ref Guid ClassGuid, StringBuilder className, Int32 ClassNameSize, ref Int32 RequiredSize);
  233. [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
  234. public static extern Boolean SetupDiGetClassDescription(ref Guid ClassGuid, StringBuilder classDescription, Int32 ClassDescriptionSize, ref Int32 RequiredSize);
  235. [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
  236. public static extern Boolean SetupDiGetDeviceInstanceId(IntPtr DeviceInfoSet, SP_DEVINFO_DATA DeviceInfoData, StringBuilder DeviceInstanceId, Int32 DeviceInstanceIdSize, ref Int32 RequiredSize);
  237. [DllImport("setupapi.dll", SetLastError = true)]
  238. public static extern bool SetupDiGetDeviceRegistryProperty(IntPtr DeviceInfoSet, SP_DEVINFO_DATA DeviceInfoData, int Property, uint PropertyRegDataType, StringBuilder PropertyBuffer, uint PropertyBufferSize, IntPtr RequiredSize);
  239. public const int DIGCF_ALLCLASSES = (0x00000004);
  240. public const int DIGCF_PRESENT = (0x00000002);
  241. public const int INVALID_HANDLE_VALUE = -1;
  242. public const int SPDRP_DEVICEDESC = (0x00000000);
  243. public const int MAX_DEV_LEN = 200;
  244. public const int DEVICE_NOTIFY_WINDOW_HANDLE = (0x00000000);
  245. public const int DEVICE_NOTIFY_SERVICE_HANDLE = (0x00000001);
  246. public const int DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = (0x00000004);
  247. public const int DBT_DEVTYP_DEVICEINTERFACE = (0x00000005);
  248. public const int DBT_DEVNODES_CHANGED = (0x0007);
  249. public const int WM_DEVICECHANGE = (0x0219);
  250. <pre code_snippet_id="1839989" snippet_file_name="blog_20160819_1_8546058" name="code" class="csharp"> public const int DIF_REMOVE = (0x00000005);
public const int DIF_PROPERTYCHANGE = (0x00000012); public const int DICS_FLAG_GLOBAL = (0x00000001); public const int DICS_FLAG_CONFIGSPECIFIC = (0x00000002); public const int DICS_ENABLE = (0x00000001); public const int DICS_DISABLE = (0x00000002); public static bool Remove(int SelectedItem, IntPtr DevInfo) { bool result = false; SP_DEVINFO_DATA devInfoData = new SP_DEVINFO_DATA(); ; devInfoData.cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA)); if (true == SetupDiEnumDeviceInfo(DevInfo, SelectedItem, devInfoData)) { if (true == SetupDiCallClassInstaller(Win32.DIF_REMOVE,DevInfo, devInfoData)) { result = true; } else { uint ret = (uint)Win32.GetLastError(); Log.Show(Log.Level.Operation, "Remove Device failed(" + ret.ToString() + ")"); } } return result; } public static bool StateChange(bool Enable, int SelectedItem, IntPtr DevInfo) { bool result = false; SP_DEVINFO_DATA devInfoData = new SP_DEVINFO_DATA(); ; devInfoData.cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA)); if (true == SetupDiEnumDeviceInfo(DevInfo, SelectedItem, devInfoData)) { SP_PROPCHANGE_PARAMS pcp = new SP_PROPCHANGE_PARAMS(); ; pcp.ClassInstallHeader.cbSize = Marshal.SizeOf(typeof(SP_CLASSINSTALL_HEADER)); pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE; pcp.Scope = DICS_FLAG_CONFIGSPECIFIC; pcp.StateChange = (Enable ? DICS_ENABLE : DICS_DISABLE); if (true == SetupDiSetClassInstallParams(DevInfo, devInfoData, pcp, Marshal.SizeOf(pcp))) { if (true == SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DevInfo, devInfoData)) { result = true; } else { uint ret = (uint)Win32.GetLastError(); Log.Show(Log.Level.Operation, "Disable Device failed(" + ret.ToString() + ")"); } } } return result; } public static String GetClassNameFromGuid(Guid guid) { String result = String.Empty; StringBuilder className = new StringBuilder(); Int32 iRequiredSize = 0; Int32 iSize = 0; bool b = SetupDiClassNameFromGuid(ref guid, className, iSize, ref iRequiredSize); className = new StringBuilder(iRequiredSize); iSize = iRequiredSize; b = SetupDiClassNameFromGuid(ref guid, className, iSize, ref iRequiredSize); if (true == b) { result = className.ToString(); } return result; } public static String GetClassDescriptionFromGuid(Guid guid) { String result = String.Empty; StringBuilder classDesc = new StringBuilder(0); Int32 iRequiredSize = 0; Int32 iSize = 0; bool b = SetupDiGetClassDescription(ref guid, classDesc, iSize, ref iRequiredSize); classDesc = new StringBuilder(iRequiredSize); iSize = iRequiredSize; b = SetupDiGetClassDescription(ref guid, classDesc, iSize, ref iRequiredSize); if (true == b) { result = classDesc.ToString(); } return result; } public static String GetDeviceInstanceId(IntPtr DeviceInfoSet, SP_DEVINFO_DATA DeviceInfoData) { String result = String.Empty; StringBuilder id = new StringBuilder(0); Int32 iRequiredSize = 0; Int32 iSize = 0; bool b = SetupDiGetDeviceInstanceId(DeviceInfoSet, DeviceInfoData, id, iSize, ref iRequiredSize); id = new StringBuilder(iRequiredSize); iSize = iRequiredSize; b = SetupDiGetDeviceInstanceId(DeviceInfoSet, DeviceInfoData, id, iSize, ref iRequiredSize); if (true == b) { result = id.ToString(); } return result; } } #endregion #region setupapi.dll elements used by Rescan [DllImport("setupapi.dll", SetLastError = true)] static extern int CM_Locate_DevNode_Ex(ref UInt32 pdnDevInst, string pDeviceID, int ulFlags, IntPtr hMachine); [DllImport("setupapi.dll")] static extern UInt32 CM_Reenumerate_DevNode_Ex(UInt32 dnDevInst, UInt32 ulFlags, IntPtr hMachine); const uint CR_SUCCESS = 0x00000000; #endregion }



  1. public const int DIF_PROPERTYCHANGE = (0x00000012);

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多