zoukankan      html  css  js  c++  java
  • Delphi 如何判断当前网卡是物理网卡(有线网卡,无线网卡)还是虚拟网卡

    MAC地址作为硬件唯一标识,在很多时候会被使用,如在软件授权方面,很多软件在产生机器码时会采用CPUID或MAC地址,或使用MAC地址来做一对一绑定。

    相信很多人会碰到以下问题:

    1)获取的是VMWare的网卡MAC地址

    2)获取的是VPN的网卡MAC地址

    VMWare或VPN软件的安装卸载都会导致获取的MAC地址变化,所以我们需要正确获取当前物理网卡的MAC地址。

    下面是根据网卡实例ID判断是否为物理网卡的Delphi代码(RAD Studio 10.1版本的):

    const
      NCF_VIRTUAL  =$01; //  说明组件是个虚拟适配器
      NCF_SOFTWARE_ENUMERATED = $02;//  说明组件是一个软件模拟的适配器
      NCF_PHYSICAL = $04; //  说明组件是一个物理适配器
      NCF_HIDDEN = $08;  //说明组件不显示用户接口
      NCF_NO_SERVICE = $10; //  说明组件没有相关的服务(设备驱动程序)
      NCF_NOT_USER_REMOVABLE = $20;  // 说明不能被用户删除(例如,通过控制面板或设备管理器)
      NCF_MULTIPORT_INSTANCED_ADAPTER  = $40; //  说明组件有多个端口,每个端 口作为单独的设备安装。
                                              //  每个 端口有自己的hw_id(组件ID) 并可被单独安装,
                                              //  这只适合于 EISA适配器
      NCF_HAS_UI = $80; // 说明组件支持用户接口(例如,Advanced Page或Customer  Properties Sheet)
      NCF_FILTER = $400; //  说明组件是一个过滤器
    
      function IsPhysicalAdapter(sAdapterGUID: string; bOnlyPCI: Boolean=False):Boolean;
      const
        NET_CARD_KEY_PATH =
        'SYSTEMCurrentControlSetControlClass{4d36e972-e325-11ce-bfc1-08002be10318}';
      var
        Reg: TRegistry;
        sList: TStrings;
        vCharacteristics: DWORD;
        I: Integer;
      begin
        Result := False;
        sList:=TStringList.Create;
        Reg:=TRegistry.Create;
        try
          Reg.RootKey := HKEY_LOCAL_MACHINE;
          if not Reg.OpenKey(NET_CARD_KEY_PATH, False) then
            Exit;
          Reg.GetKeyNames(sList);
          Reg.CloseKey;
          if sList.Count>0 then
          for I := 0 to Pred(sList.Count) do
          if Reg.OpenKey(NET_CARD_KEY_PATH+''+sList.Strings[I], False) then
          begin
            try
              if sAdapterGUID.ToUpper.Trim.Equals(Reg.ReadString('NetCfgInstanceId').ToUpper.Trim) then
              begin
                vCharacteristics := DWORD(Reg.ReadInteger('Characteristics'));
                Result := (NCF_PHYSICAL and vCharacteristics) = NCF_PHYSICAL;
                Break;
              end;
            finally
              Reg.CloseKey;
            end;
          end;
        finally
          Reg.Free;
          sList.Free;
        end;
      end;

    上面IsPhysicalAdapter的参数网卡实例ID通过GetAdaptersInfo即可得到,综合起来的具体代码如下:

    注意:

    调用 GetAdaptersInfo 可以直接引用下面2个单元

    Winapi.IpTypes,
    Winapi.IpHlpApi

    function GetMacAddress(): string;
    var
      dwRet: DWORD;
      AI,Work : PIpAdapterInfo;
      sGUID, sTmp, sLastMAC, sFirstMAC: string;
      I: Integer;
      uSize: ULONG;
    begin
      Result := '';
      sLastMAC := '';
      sFirstMAC := '';
      uSize := SizeOf(TIpAdapterInfo);
      GetMem(AI,uSize);
      dwRet := GetAdaptersInfo(AI,uSize);
      if (dwRet = ERROR_BUFFER_OVERFLOW)then
      begin
        FreeMem(AI);
        GetMem(AI,uSize);
        dwRet := GetAdaptersInfo(AI,uSize);
      end;
      try
        if (dwRet <> ERROR_SUCCESS)then
          Exit;
        Work := AI;
        while Work<>nil do
        begin
          try
            sGUID := string(AnsiString(Work.AdapterName));
            sTmp := string(AnsiString(Work.Description));
            // 名称描述出现VMWare,直接忽略
            if Pos('VMWare', sTmp)>0 then
              Continue;
            // 配置的ID地址不正常,忽略
            if Work.AddressLength=0 then
              Continue;
    
            // 将网卡MAC地址转成字符串
            sLastMAC := '';
            for I:=0 to Work.AddressLength-1 do
            begin
              sLastMAC := sLastMAC + Format('%.2x', [Work.Address[I]]);
            end;
    
            if sFirstMAC='' then
              sFirstMAC := sLastMAC;
    
            // 不是物理网卡,忽略
            if not IsPhysicalAdapter(sGUID) then
              Continue;
    
            Result := sLastMAC;
    
            //找到第一个物理网卡后退出
            Break;
          finally
            Work := Work.Next;
          end;
        end;
        // 找不到物理网卡MAC,返回第一个即可
        if Result='' then
          Result := sFirstMAC;
      finally
        FreeMem(AI);
      end;
    end;

    理论上从软件层面是无法直接判断一个网络适配器硬件到底是有线还是无线的,因为不管是miniPCIE还是普通PCI接口的网卡,都可以是有线也可以是无线,

    但是也有以下的方法判断:

    1)网卡可以通过名称描述上判断:一般有线网卡名称上带PCI,无线带WLAN 或Wireless,此方法不依赖于网络是否连接,但对那些自定义驱动名称的高人当然无效

    2)获取网络配置信息,也就是获取网络连接里的整个列表,包含物理网卡和宽带连接,VPN等各种连接设备信息,再查看连接类型,根据MSDN的说明,类型包含ETHERNET/PPP/IEEE80211等等,甚至包含移动手机卡的类型。
    3)获取网络速率,和2)一样也是获取网络连接的配置信息,100/10Mbps一般是有线,54Mbps或其他一般为WLAN连接,虽说WLAN并不包含一个最高的速度标准,但是普通2.4G或5G的WIFI速度也快不到哪去。

    注:

    方法 2)和3)中的网络类型和网络速率均是通过 API GetIfEntry配合GetAdaptersInfo 获取(或直接通过 GetIfTable获取)网络配置信息得到的,代码MSDN也有

    具体查看MSDN中对此函数返回的结构体MIB_IFROW字段dwType和dwSpeed的说明,除了类型速率,也包含其他非常多的信息。

  • 相关阅读:
    通过HttpListener实现简单的Http服务
    WCF心跳判断服务端及客户端是否掉线并实现重连接
    NHibernate初学六之关联多对多关系
    NHibernate初学五之关联一对多关系
    EXTJS 4.2 资料 跨域的问题
    EXTJS 4.2 资料 控件之Grid 那些事
    EXTJS 3.0 资料 控件之 GridPanel属性与方法大全
    EXTJS 3.0 资料 控件之 Toolbar 两行的用法
    EXTJS 3.0 资料 控件之 combo 用法
    EXTJS 4.2 资料 控件之 Store 用法
  • 原文地址:https://www.cnblogs.com/caibirdy1985/p/5816362.html
Copyright © 2011-2022 走看看