通过串口号获得该串口号对应的设备信息(如设备范例ID等)
前几天一直在处理标题上面的那个问题,搜遍千山万水,反复实验,最后终于找到一种方法,本文不仅仅介绍这种方法,还介绍了解决问题的整个过程,还有一些通用的东西,希望能向那些和我一样一点不懂C/C++,不懂win32APi,从事C#winform开发的C#菜鸟提供点帮助,让大家少走弯路。
我们采用trouble shooting的方式开始:
问题描述:如标题,要求是不能用wmi,因为本程序是要运行在98机器上的,而98机器要运行wmi得先安装wmicore组件,太麻烦,所以决定用win32 api实现,如何实现?
实现步骤:
1.如何找到自己想要的api(事先不知道api函数名)?
首先当然是一阵海搜啦,百度,google(google一般不太爱用,一是觉得它没有百度方便,二是它不是国产),最后得到一个答案:在MSDN里找,接下来问题就变为如何在msdn里面找自己想要的api,通过一顿实验,最后找到方法:打开msdn(注:本文说讲msdn均为vs2005自带的,不是mdsn网站,网站速度太慢,没效率),使用索引--刷选框中选中platform sdk,搜索词为win32 api--选中第一项,这个主提是关于api的,里面有好多有用的子主题:base services(把api按照功能分类),data type(api函数里的c数据类型描述),reference(api参考,包括了几乎所有的api函数),最后通过问题描述,在device management reference里面找到了我想要的api,大家可以用这种方式找到自己想要的api
2.找到了api,如何使用它们?
又是一顿海搜,最后得到2篇介绍用c#调用api的文章,各有侧重点,推荐大家去看下,http://www.czvec.com/jt/main/output.asp?haxz2=358,http://blog.csdn.net/zhoufoxcn/archive/2006/09/22/1265520.aspx#811513,我在这里通过一个例子向大家讲解如何使用api:
HDEVINFO SetupDiGetClassDevs(
const GUID* ClassGuid,
PCTSTR Enumerator,
HWND hwndParent,
DWORD Flags
);
刚看到这个函数声明不禁有点晕,我们一点一点来,这个函数可以获得某一分类的所有设备信息集合(device information set),参看文档说明,发现前面几个参数设置和最后一个参数flags有关系,先看flags,它有DIGCF_ALLCLASSES,DIGCF_DEVICEINTERFACE,DIGCF_PRESENT,DIGCF_PROFILE等值,但是这些值等于多少,怎么设置?不要担心,在这页文档的最后有一个requiments表格,里面有很多有用信息,如我们要dllimport的dll文件名(Setupapi.dll),当前api支持的平台,还有header(Setupapi.h,此文件定义了DIGCF_ALLCLASSES等字符串所对应的值),好,下一步是如何得到Setupapi.h呢?最笨的办法是搜索本地硬盘,最后我在D:/Program Files/Microsoft Visual Studio 8/VC/PlatformSDK/Include里面找到了它,那个目录下还有很多别的header文件,flag值的设定搞定了。
HWND hwndParent这个参数表示的是应用程序某个窗口的句柄,没什么用,随便设置下。
PCTSTR Enumerator这个参数分2中情况,在设置了DIGCF_DEVICEINTERFACE时它表示一个PNP设备(pnp=plug and play,即插即用,我猜的)的名字,这时候返回的将是存储了此设备的所有接口的设备信息集合,如果没设置DIGCF_DEVICEINTERFACE,又要使用该参数,则该参数表示的是某一类型的名称(这个名称可以在注册表HKLM/SYSTEM/CurrentControlSet/Enum中找到,本文为“USB”),此时函数返回的是该类型下的所有设备信息集合(USB则能得到所有USB设备的集合)
const GUID* ClassGuid,参数说明:它表示某类设备类型的guid,通过设置它你能得到所有guid等于该参数的设备信息集合,前提是你没把flag参数设为DIGCF_ALLCLASSES(如果设置了此项,则你输入的guid无效,它将直接返回当前机器的所有设备信息集合(Enumerator=null)或者Enumerator类型的设备信息集合),但是,如何去寻找这个guid呢,可以通过查阅设备的驱动程序信息文件(*.inf文件,它包含在设备驱动程序安装包里,或者你直接到c:/windows/inf(98.XP等),c:winnt/inf(2000)等下面去找,inf文件记录了特定设备的很多信息,如pid,vid,classguid等信息),查到guid字符串,直接调用new Guid(string)就可以得到一个guid了
参数讲解完毕,下面是如何把C语言数据字段映射到C#数据字段,要是大家看了前面我推荐的2篇文章的话就知道映射的一般原则了,大体上按照如下原则进行:首先看清楚当前参数的作用,这很重要,因为一个C数据字段可以映射为好几个C#数据字段,而且都不会出错,例如HWND你可以把它映射为IntPtr,也可以映射为UINT32,PCTSTR可以把它映射为string,也可以映射为UINT32(string的地址),本例子为了调用方面,我用的是string,一般有以下几个诀窍:string一般用来做输入参数(因为它长度固定),stringBuilder用来作为接收(输出)参数,如果一个参数表示的是某个对象(不是基础值类型,例如一个窗口,一个设备等)的指针,最好还是用IntPtr类型(句柄),structure类型一定要用ref或者out关键字把它变成按引用传递(当然,如果你不想用ref等,那就别定义structure了,用class替代,至于副作用麻,我也不清楚),还有很多别的技巧,看了那2篇文章大家就清楚了。通过调用device management下的api,我们可以获得各种设备的设备信息,还可以对设备进行某些操作(安装新设备,卸载设备,启动,禁用设备等)
我想看完本文,大家就对如何调用win32APi有个皮毛了解了吧,一些简单调用也会用了吧。下面是我的标题问题的答案,(感觉可以用CreateFile得到对应于某串口号的设备句柄,但是得到句柄后如何才能得到该设备的信息呢?此时好像用device management下的api没用,希望有高人能给我提供此解决方案,不胜感激!)
HardWareOperation.cs
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace ForGeneralUse
{
public class HardWareOperation
{
/// <summary>
/// 设备的各项属性,注意有些属性是不通用的,例如SPDRP_FRIENDLYNAME只适用于端口设备
/// </summary>
public enum SPDRP_
{
SPDRP_DEVICEDESC = ( 0x00000000 ), // DeviceDesc (R/W)
SPDRP_HARDWAREID = ( 0x00000001 ), // HardwareID (R/W)
SPDRP_SERVICE = ( 0x00000004 ), // Service (R/W)
SPDRP_CLASS = ( 0x00000007 ), // Class (R--tied to ClassGUID)
SPDRP_CLASSGUID = ( 0x00000008 ), // ClassGUID (R/W)
SPDRP_DRIVER = ( 0x00000009 ), // Driver (R/W)
SPDRP_CONFIGFLAGS = ( 0x0000000A ), // ConfigFlags (R/W)
SPDRP_MFG = ( 0x0000000B ), // Mfg (R/W)
SPDRP_FRIENDLYNAME = ( 0x0000000C ), // FriendlyName (R/W)
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = ( 0x0000000E ), // PhysicalDeviceObjectName (R)
SPDRP_CAPABILITIES = ( 0x0000000F ), // Capabilities (R)
SPDRP_REMOVAL_POLICY_HW_DEFAULT = ( 0x00000020 ), // Hardware Removal Policy (R)
SPDRP_INSTALL_STATE = ( 0x00000022 ), // Device Install State (R)
}
public const int DIGCF_ALLCLASSES = ( 0x00000004 );
public const int DIGCF_DEVICEINTERFACE = 0x00000010 ;
public const int DIGCF_PRESENT = ( 0x00000002 );
public const int INVALID_HANDLE_VALUE = - 1 ;
public const int MAX_DEV_LEN = 1000 ;
/// <summary>
/// 获取一个指定类别或全部类别的所有已安装设备的信息
/// </summary>
/// <param name="gClass"> 该类别对应的guid </param>
/// <param name="iEnumerator"> 类别名称(在HKLMSYSTEMCurrentControlSetEnum内获取) </param>
/// <param name="hParent"> 应用程序定义的窗口句柄 </param>
/// <param name="nFlags"> 获取的模式 </param>
/// <returns> 设备信息集合的句柄 </returns>
[DllImport( " setupapi.dll " , SetLastError = true , CharSet = CharSet.Auto)]
public static extern IntPtr SetupDiGetClassDevs( ref Guid gClass, String enumerator, IntPtr hParent, UInt32 nFlags);
/// <summary>
/// 获得该设备的设备范例ID
/// </summary>
/// <param name="DeviceInfoSet"> 设备信息集合 </param>
/// <param name="DeviceInfoData"> 表示该设备 </param>
/// <param name="DeviceInstanceId"> 设备范例ID(输出) </param>
/// <param name="DeviceInstanceIdSize"> 该ID所占大小(字节) </param>
/// <param name="RequiredSize"> 需要多少字节 </param>
/// <returns> 是否成功 </returns>
[DllImport( " setupapi.dll " , SetLastError = true )]
public static extern bool SetupDiGetDeviceInstanceId(IntPtr DeviceInfoSet,
HardWareOperation.SP_DEVINFO_DATA DeviceInfoData,
StringBuilder DeviceInstanceId,
UInt32 DeviceInstanceIdSize,
UInt32 RequiredSize);
/// <summary>
/// 枚举指定设备信息集合的成员,并将数据放在SP_DEVINFO_DATA中
/// </summary>
/// <param name="lpInfoSet"> 设备信息集合句柄 </param>
/// <param name="dwIndex"> 元素索引 </param>
/// <param name="devInfoData"> 表示一个设备(作为输出) </param>
/// <returns> 是否成功 </returns>
[DllImport( " setupapi.dll " , SetLastError = true )]
public static extern bool SetupDiEnumDeviceInfo(IntPtr lpInfoSet, UInt32 dwIndex, SP_DEVINFO_DATA devInfoData);
/// <summary>
/// 获取指定设备的属性
/// </summary>
/// <param name="lpInfoSet"> 设备信息集合 </param>
/// <param name="DeviceInfoData"> 表示该设备 </param>
/// <param name="Property"> 表示要获取哪项属性 </param>
/// <param name="PropertyRegDataType"> 注册类型 </param>
/// <param name="PropertyBuffer"> 属性(输出) </param>
/// <param name="PropertyBufferSize"> 存储属性的字节大小 </param>
/// <param name="RequiredSize"> 需要的字节大小 </param>
/// <returns> 是否成功 </returns>
[DllImport( " setupapi.dll " , SetLastError = true )]
public static extern bool SetupDiGetDeviceRegistryProperty(IntPtr lpInfoSet,
SP_DEVINFO_DATA DeviceInfoData,
UInt32 Property,
UInt32 PropertyRegDataType,
StringBuilder PropertyBuffer,
UInt32 PropertyBufferSize,
IntPtr RequiredSize);
/// <summary>
/// 销毁一个设备信息集合,并且释放所有关联的内存
/// </summary>
/// <param name="lpInfoSet"> 设备信息集合 </param>
/// <returns></returns>
[DllImport( " setupapi.dll " , SetLastError = true )]
public static extern bool SetupDiDestroyDeviceInfoList(IntPtr lpInfoSet);
/// <summary>
/// 设备信息数据
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public class SP_DEVINFO_DATA
{
public int cbSize; // 本结构的大小(字节表示)
public Guid classGuid; // 本结构所表示的设备的GUID
public int devInst; // 设备句柄
public ulong reserved; // 没用
};
}
}
一个调用方法GetHardWareIdFromPortName
/// 通过串口号获得对应设备的硬件ID(里面包括该串口设备的vid和pid)
/// </summary>
/// <param name="portName">串口号</param>
/// <returns></returns>
private string GetHardWareIdFromPortName(string portName)
string [] ports = System.IO.Ports.SerialPort.GetPortNames();
int k;
if ( ! DataOperator.IsContainElement(ports, portName, out k)) return null ;
Guid myGUID = Guid.Empty;
string enumerator = " USB " ;
try
... {
IntPtr hDevInfo = HardWareOperation.SetupDiGetClassDevs(ref myGUID, enumerator, IntPtr.Zero, HardWareOperation.DIGCF_ALLCLASSES | HardWareOperation.DIGCF_PRESENT);
if (hDevInfo.ToInt32() == HardWareOperation.INVALID_HANDLE_VALUE)
...{
throw new Exception("没有该类设备");
}
HardWareOperation.SP_DEVINFO_DATA deviceInfoData;//想避免在api中使用ref,就把structure映射成类
deviceInfoData = new HardWareOperation.SP_DEVINFO_DATA();
deviceInfoData.cbSize = 28;//如果要使用SP_DEVINFO_DATA,一定要给该项赋值28=16+4+4+4
deviceInfoData.devInst = 0;
deviceInfoData.classGuid = System.Guid.Empty;
deviceInfoData.reserved = 0;
UInt32 i;
StringBuilder property = new StringBuilder(HardWareOperation.MAX_DEV_LEN);
for (i = 0; HardWareOperation.SetupDiEnumDeviceInfo(hDevInfo, i, deviceInfoData); i++)
...{
// Console.Write(deviceInfoData.classGuid.ToString());
// HardWareOperation.SetupDiGetDeviceInstanceId(hDevInfo, deviceInfoData, porperty, (uint)porperty.Capacity, 0);
HardWareOperation.SetupDiGetDeviceRegistryProperty(hDevInfo, deviceInfoData,
(uint)HardWareOperation.SPDRP_.SPDRP_CLASS,
0, property, (uint)property.Capacity, IntPtr.Zero);
if (property.ToString().ToLower() != "ports") continue;//首先看看是不是串口设备(有些USB设备不是串口设备)
HardWareOperation.SetupDiGetDeviceRegistryProperty(hDevInfo, deviceInfoData,
(uint)HardWareOperation.SPDRP_.SPDRP_FRIENDLYNAME,
0, property, (uint)property.Capacity, IntPtr.Zero);
if (!property.ToString().ToLower().Contains(portName.ToLower())) continue;//找到对应于portName的设备
HardWareOperation.SetupDiGetDeviceRegistryProperty(hDevInfo, deviceInfoData,
(uint)HardWareOperation.SPDRP_.SPDRP_HARDWAREID,
0, property, (uint)property.Capacity, IntPtr.Zero);
break;
}
HardWareOperation.SetupDiDestroyDeviceInfoList(hDevInfo);
return property.ToString();
}
catch (Exception ex)
... {
MessageBox.Show(ex.Message);
return null;
}
}
以上就是我这几天得到的一点学习收获,这也是我第一篇blog,仅以此文纪念我那些烧死的脑细胞。
通过串口号获得该串口号对应的设备信息(如设备范例ID等)相关推荐
- 已解决:PC插上串口工具后PC端口com那里有个黄色叹号,无法使用串口工具
PC插上串口工具后PC端口com那里有个黄色叹号,无法使用串口工具 原文链接:http://blog.csdn.net/csdnhuaong/article/details/68945601 解决WI ...
- 自动识别查找特定的串口号 比如设备管理器中Modem属性里的串口 按这个方法可以获取设备管理器任意信息。C++
1.目标: 自动识别查找特定的串口号 2.注册表里搜串口号 设备管理器中所有的信息都在注册表中有,那么我直接在注册表里搜COM143. 搜到了这个,但这里有2个名称key相同的.后面193,192还是 ...
- C# 25. 获取windows串口号对应的串口(设备)名称
//SerialPortFindTool using System; using System.Collections.Generic; using System.Linq; using System ...
- QT入门第十四天 串口通信协议+收发数据+波特率+数据位+停止位+奇偶校验+串口识别射频RFID的卡号
QT入门第十四天 串口通信[QT入门第十四天 串口通信协议+收发数据+波特率+数据位+停止位+奇偶校验+串口识别射频RFID的卡号 第一章 常见的硬件通信接口协议 [1]硬件通信接口协议 [2]使用串 ...
- 串口通信模块5:串口操作自定义类(2)
1.ProcessErrorMessage()函数的实现: ProcessErrorMessage()函数负责处理并提示错误信息,其实现过程如下: void CMySerial::ProcessErr ...
- espflashdownloadtool连接串口失败_关于串口下载问题和超时
串口下载适用于mini.精英.战舰.探索者.阿波罗429 不适用于阿波罗767,H743,号令者1052 保证板子在独立供电状态下,电源灯处于亮灯状态下, USB线接板子上USB_232, RXD 和 ...
- ch340串口驱动_关于串口下载问题和超时
串口下载适用于mini.精英.战舰.探索者.阿波罗429 不适用于阿波罗767,H743,号令者1052 保证板子在独立供电状态下,电源灯处于亮灯状态下, USB线接板子上USB_232, RXD 和 ...
- linux驱动向不同串口发数据,Linux串口(serial、uart)驱动程序设计
一.核心数据结构 串口驱动有3个核心数据结构,它们都定义在 1.uart_driver uart_driver包含了串口设备名.串口驱动名.主次设备号.串口控制台(可选)等信息,还封装了tty_dri ...
- android 串口一直打开_串口通讯你真的会了吗?不妨来看看这些经验
平时使用串口打印出现乱码的绝大部分原因是串口波特率没对.那么我们怎么测量实际的波特率呢?在这之前,顺便一起回顾一下波特率的概念. 什么是波特率.比特率? 比特率(Bitrate)表示每秒钟传输的二进制 ...
最新文章
- 如何在全局程序集缓存 (GAC) 中安装 DLL 文件
- python教程哪个版本好-终于清楚python入门最好的教程
- Android MediaRecorder架构详解
- maven打包jar单独配置log4j.properites文件记录日志
- BZOJ 2442: [Usaco2011 Open]修剪草坪 单调队列
- Java编程初学者应该了解的编程框架
- 学习方法之07克服拖延症,每个人都有一个拖延的理由
- C# 子类实例化基类 基类使用不了子类的方法_C#委托事件机制:事件的完整声明,触发和事件的本质(6)...
- 【Redis使用规范】
- 局域网文档服务器搭建,局域网服务器的搭建.pdf
- 西门子840d高级编程手册_840D NC 高级编程简单介绍
- 苹果app项目退款教程
- SpringBoot如何自定义starter启动器?看这里
- Docker容器运行
- 【python】一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?
- 记录Linux下安装elasticSearch时遇到的一些错误
- CentOS6.5安装Chromium谷歌浏览器
- 鸿蒙初辟原无姓,打破顽冥须悟空
- 二叉排序树,平衡二叉树和哈夫曼树
- js+css让背景图片动起来
热门文章
- 关于Salesforce里的Trigger
- angular ng zorro框架日期框无法自适应宽度的解决方法
- jQuery中的slideUp()、slideDown()、hide()、show() 的比较
- 网页版ASN1解码工具使用教程
- Spark官方文档整理:spark-core
- 还活着哈。 ..:D
- http://blog.csdn.net/neiloid/article/details/7037093#
- Chrome调试工具
- 华为p20如何连接计算机,华为P20pro如何快捷传文件到电脑?华为P20pro快捷传文件到电脑的方法...
- 2022 主站及创作侧年度总结 - 相信未来、期待未来