前几天一直在处理标题上面的那个问题,搜遍千山万水,反复实验,最后终于找到一种方法,本文不仅仅介绍这种方法,还介绍了解决问题的整个过程,还有一些通用的东西,希望能向那些和我一样一点不懂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;
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

/// <summary>
        /// 通过串口号获得对应设备的硬件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等)相关推荐

  1. 已解决:PC插上串口工具后PC端口com那里有个黄色叹号,无法使用串口工具

    PC插上串口工具后PC端口com那里有个黄色叹号,无法使用串口工具 原文链接:http://blog.csdn.net/csdnhuaong/article/details/68945601 解决WI ...

  2. 自动识别查找特定的串口号 比如设备管理器中Modem属性里的串口 按这个方法可以获取设备管理器任意信息。C++

    1.目标: 自动识别查找特定的串口号 2.注册表里搜串口号 设备管理器中所有的信息都在注册表中有,那么我直接在注册表里搜COM143. 搜到了这个,但这里有2个名称key相同的.后面193,192还是 ...

  3. C# 25. 获取windows串口号对应的串口(设备)名称

    //SerialPortFindTool using System; using System.Collections.Generic; using System.Linq; using System ...

  4. QT入门第十四天 串口通信协议+收发数据+波特率+数据位+停止位+奇偶校验+串口识别射频RFID的卡号

    QT入门第十四天 串口通信[QT入门第十四天 串口通信协议+收发数据+波特率+数据位+停止位+奇偶校验+串口识别射频RFID的卡号 第一章 常见的硬件通信接口协议 [1]硬件通信接口协议 [2]使用串 ...

  5. 串口通信模块5:串口操作自定义类(2)

    1.ProcessErrorMessage()函数的实现: ProcessErrorMessage()函数负责处理并提示错误信息,其实现过程如下: void CMySerial::ProcessErr ...

  6. espflashdownloadtool连接串口失败_关于串口下载问题和超时

    串口下载适用于mini.精英.战舰.探索者.阿波罗429 不适用于阿波罗767,H743,号令者1052 保证板子在独立供电状态下,电源灯处于亮灯状态下, USB线接板子上USB_232, RXD 和 ...

  7. ch340串口驱动_关于串口下载问题和超时

    串口下载适用于mini.精英.战舰.探索者.阿波罗429 不适用于阿波罗767,H743,号令者1052 保证板子在独立供电状态下,电源灯处于亮灯状态下, USB线接板子上USB_232, RXD 和 ...

  8. linux驱动向不同串口发数据,Linux串口(serial、uart)驱动程序设计

    一.核心数据结构 串口驱动有3个核心数据结构,它们都定义在 1.uart_driver uart_driver包含了串口设备名.串口驱动名.主次设备号.串口控制台(可选)等信息,还封装了tty_dri ...

  9. android 串口一直打开_串口通讯你真的会了吗?不妨来看看这些经验

    平时使用串口打印出现乱码的绝大部分原因是串口波特率没对.那么我们怎么测量实际的波特率呢?在这之前,顺便一起回顾一下波特率的概念. 什么是波特率.比特率? 比特率(Bitrate)表示每秒钟传输的二进制 ...

最新文章

  1. 如何在全局程序集缓存 (GAC) 中安装 DLL 文件
  2. python教程哪个版本好-终于清楚python入门最好的教程
  3. Android MediaRecorder架构详解
  4. maven打包jar单独配置log4j.properites文件记录日志
  5. BZOJ 2442: [Usaco2011 Open]修剪草坪 单调队列
  6. Java编程初学者应该了解的编程框架
  7. 学习方法之07克服拖延症,每个人都有一个拖延的理由
  8. C# 子类实例化基类 基类使用不了子类的方法_C#委托事件机制:事件的完整声明,触发和事件的本质(6)...
  9. 【Redis使用规范】
  10. 局域网文档服务器搭建,局域网服务器的搭建.pdf
  11. 西门子840d高级编程手册_840D NC 高级编程简单介绍
  12. 苹果app项目退款教程
  13. SpringBoot如何自定义starter启动器?看这里
  14. Docker容器运行
  15. 【python】一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?
  16. 记录Linux下安装elasticSearch时遇到的一些错误
  17. CentOS6.5安装Chromium谷歌浏览器
  18. 鸿蒙初辟原无姓,打破顽冥须悟空
  19. 二叉排序树,平衡二叉树和哈夫曼树
  20. js+css让背景图片动起来

热门文章

  1. 关于Salesforce里的Trigger
  2. angular ng zorro框架日期框无法自适应宽度的解决方法
  3. jQuery中的slideUp()、slideDown()、hide()、show() 的比较
  4. 网页版ASN1解码工具使用教程
  5. Spark官方文档整理:spark-core
  6. 还活着哈。 ..:D
  7. http://blog.csdn.net/neiloid/article/details/7037093#
  8. Chrome调试工具
  9. 华为p20如何连接计算机,华为P20pro如何快捷传文件到电脑?华为P20pro快捷传文件到电脑的方法...
  10. 2022 主站及创作侧年度总结 - 相信未来、期待未来