当前位置:编程文档 >> VC++ >> 取得系统中网卡MAC地址的两种方法
首页

取得系统中网卡MAC地址的两种方法

所属类别:VC++
推荐指数:★★★☆
文档人气:234
本周人气:11
发布日期:2008-7-14

  第一种方法使用Microsoft的Netbios   API。   这是一套通过Winsock提供底层网络支持的命令。使用Netbios的最大缺点是您必须在系统中安装了Netbios服务(如果您在windows网络中启用了文件共享的话,这就不是问题了)。除此此外,这种方法又快又准确。    
   
  Netbios   API只包括了一个函数,就叫做Netbios。这个函数使用网络控制块(network   control   block)结构作为参数,这个结构告诉函数要做什么。结构的定义如下:    
  typedef   struct   _NCB   {    
  UCHAR   ncb_command;    
  UCHAR   ncb_retcode;    
  UCHAR   ncb_lsn;    
  UCHAR   ncb_num;    
  PUCHAR   ncb_buffer;    
  WORD   ncb_length;    
  UCHAR   ncb_callname[NCBNAMSZ];    
  UCHAR   ncb_name[NCBNAMSZ];    
  UCHAR   ncb_rto;    
  UCHAR   ncb_sto;    
  void   (CALLBACK   *ncb_post)   (struct   _NCB   *);    
  UCHAR   ncb_lana_num;    
  UCHAR   ncb_cmd_cplt;    
  #ifdef   _WIN64    
  UCHAR   ncb_reserve[18];    
  #else    
  UCHAR   ncb_reserve[10];    
  #endif    
  HANDLE   ncb_event;    
  }   NCB,   *PNCB;    
   
   
   
  重点在于ncb_command   成员。这个成员告诉Netbios该作什么。我们使用三个命令来探测MAC地址。他们在MSDN的定义如下:    
  命令描述:    
  NCBENUM   Windows   NT/2000:   列举系统中网卡的数量。使用此命令后,ncb_buffer成员指向由LANA_ENUM结构填充的缓冲区。    
  NCBENUM   不是标准的   NetBIOS   3.0   命令。    
   
  NCBRESET   重置网卡。网卡在接受新的NCB命令之前必须重置。    
  NCBASTAT   接受本地或远程接口卡的状态。使用此命令后,ncb_buffer成员指向由ADAPTER_STATUS结构填充的缓冲区,随后是NAME_BUFFER结构的数组。    
   
  下面就是取得您系统MAC地址的步骤:    
  1》列举所有的接口卡。    
  2》重置每块卡以取得它的正确信息。    
  3》查询接口卡,取得MAC地址并生成标准的冒号分隔格式。    
   
  下面就是实例源程序。    
  netbios.cpp    
   
  #include   <windows.h>    
  #include   <stdlib.h>    
  #include   <stdio.h>    
  #include   <iostream>    
  #include   <string>    
   
  using   namespace   std;    
  #define   bzero(thing,sz)   memset(thing,0,sz)    
   
  bool   GetAdapterInfo(int   adapter_num,   string   &mac_addr)    
  {    
  //   重置网卡,以便我们可以查询    
  NCB   Ncb;    
  memset(&Ncb,   0,   sizeof(Ncb));    
  Ncb.ncb_command   =   NCBRESET;    
  Ncb.ncb_lana_num   =   adapter_num;    
  if   (Netbios(&Ncb)   !=   NRC_GOODRET)   {    
  mac_addr   =   "bad   (NCBRESET):   ";    
  mac_addr   +=   string(Ncb.ncb_retcode);    
  return   false;    
  }    
   
  //   准备取得接口卡的状态块    
  bzero(&Ncb,sizeof(Ncb);    
  Ncb.ncb_command   =   NCBASTAT;    
  Ncb.ncb_lana_num   =   adapter_num;    
  strcpy((char   *)   Ncb.ncb_callname,   "*");    
  struct   ASTAT    
  {    
  ADAPTER_STATUS   adapt;    
  NAME_BUFFER   NameBuff[30];    
  }   Adapter;    
  bzero(&Adapter,sizeof(Adapter));    
  Ncb.ncb_buffer   =   (unsigned   char   *)&Adapter;    
  Ncb.ncb_length   =   sizeof(Adapter);    
   
  //   取得网卡的信息,并且如果网卡正常工作的话,返回标准的冒号分隔格式。    
  if   (Netbios(&Ncb)   ==   0)    
  {    
  char   acMAC[18];    
  sprintf(acMAC,   "%02X:%02X:%02X:%02X:%02X:%02X",    
  int   (Adapter.adapt.adapter_address[0]),    
  int   (Adapter.adapt.adapter_address[1]),    
  int   (Adapter.adapt.adapter_address[2]),    
  int   (Adapter.adapt.adapter_address[3]),    
  int   (Adapter.adapt.adapter_address[4]),    
  int   (Adapter.adapt.adapter_address[5]));    
  mac_addr   =   acMAC;    
  return   true;    
  }    
  else    
  {    
  mac_addr   =   "bad   (NCBASTAT):   ";    
  mac_addr   +=   string(Ncb.ncb_retcode);    
  return   false;    
  }    
  }    
   
  int   main()    
  {    
  //   取得网卡列表    
  LANA_ENUM   AdapterList;    
  NCB   Ncb;    
  memset(&Ncb,   0,   sizeof(NCB));    
  Ncb.ncb_command   =   NCBENUM;    
  Ncb.ncb_buffer   =   (unsigned   char   *)&AdapterList;    
  Ncb.ncb_length   =   sizeof(AdapterList);    
  Netbios(&Ncb);    
   
  //   取得本地以太网卡的地址    
  string   mac_addr;    
  for   (int   i   =   0;   i   <   AdapterList.length   -   1;   ++i)    
  {    
  if   (GetAdapterInfo(AdapterList.lana,   mac_addr))    
  {    
  cout   <<   "Adapter   "   <<   int   (AdapterList.lana)   <<    
  "'s   MAC   is   "   <<   mac_addr   <<   endl;    
  }    
  else    
  {    
  cerr   <<   "Failed   to   get   MAC   address!   Do   you"   <<   endl;    
  cerr   <<   "have   the   NetBIOS   protocol   installed?"   <<   endl;    
  break;    
  }    
  }    
   
  return   0;    
  }    
第二种方法-使用COM   GUID   API    
  这种方法使用COM   API创建一个GUID(全局唯一标识符)并从那里继承MAC地址。GUID通常用来标识COM组件以及系统中的其他对象。它们是由MAC地址(结合其他东西)计算得来的,表面上MAC地址就包含在其中。我说表面上是因为事实上并没有包含。    
  我提供这种方法更多的是为了作为反面教材。您也许用这种方法能够得到MAC地址,但有时候您只会得到随机的十六进制数值。    
  下面的例子十分简单,无需多讲。我们使用CoCreateGuid创建GUID,并将最后六个字节放入字符串中。它们可能是MAC地址,但并不是必然的。    
   
  uuid.cpp    
  #include   <windows.h>    
  #include   <iostream>    
  #include   <conio.h>    
   
  using   namespace   std;    
   
  int   main()    
  {    
  cout   <<   "MAC   address   is:   ";    
   
  //   向COM要求一个UUID。如果机器中有以太网卡,    
  //   UUID最后的六个字节(Data4的2-7字节)应该是本地以太网卡的MAC地址。    
  GUID   uuid;    
  CoCreateGuid(&uuid);    
  //   Spit   the   address   out    
  char   mac_addr[18];    
  sprintf(mac_addr,"%02X:%02X:%02X:%02X:%02X:%02X",    
  uuid.Data4[2],uuid.Data4[3],uuid.Data4[4],    
  uuid.Data4[5],uuid.Data4[6],uuid.Data4[7]);    
  cout   <<   mac_addr   <<   endl;    
  getch();    
  return   0;    
  }    
   
   
  第三种方法-   使用SNMP扩展API    
  我要讨论的第三种方法是使用Windows的SNMP(简单网络管理协议)扩展来取得MAC地址。在我的经验里,这个协议很简单。代码也是直勾勾的向前的。基本步骤和Netbios相同:    
  1》取得网卡列表    
  2》查询每块卡的类型和MAC地址    
  3》保存当前网卡    
  我个人对SNMP了解不多,但如我刚刚所言,代码十分清楚。    
   
  snmp.cpp    
  #include   <snmp.h>    
  #include   <conio.h>    
  #include   <stdio.h>    
   
  typedef   bool(WINAPI   *   pSnmpExtensionInit)   (    
  IN   DWORD   dwTimeZeroReference,    
  OUT   HANDLE   *   hPollForTrapEvent,    
  OUT   AsnObjectIdentifier   *   supportedView);    
   
  typedef   bool(WINAPI   *   pSnmpExtensionTrap)   (    
  OUT   AsnObjectIdentifier   *   enterprise,    
  OUT   AsnInteger   *   genericTrap,    
  OUT   AsnInteger   *   specificTrap,    
  OUT   AsnTimeticks   *   timeStamp,    
  OUT   RFC1157VarBindList   *   variableBindings);    
   
  typedef   bool(WINAPI   *   pSnmpExtensionQuery)   (    
  IN   BYTE   requestType,    
  IN   OUT   RFC1157VarBindList   *   variableBindings,    
  OUT   AsnInteger   *   errorStatus,    
  OUT   AsnInteger   *   errorIndex);    
   
  typedef   bool(WINAPI   *   pSnmpExtensionInitEx)   (    
  OUT   AsnObjectIdentifier   *   supportedView);    
   
  void   main()    
  {    
  HINSTANCE   m_hInst;    
  pSnmpExtensionInit   m_Init;    
  pSnmpExtensionInitEx   m_InitEx;    
  pSnmpExtensionQuery   m_Query;    
  pSnmpExtensionTrap   m_Trap;    
  HANDLE   PollForTrapEvent;    
  AsnObjectIdentifier   SupportedView;    
  UINT   OID_ifEntryType[]   =   {1,   3,   6,   1,   2,   1,   2,   2,   1,   3};    
  UINT   OID_ifEntryNum[]   =   {1,   3,   6,   1,   2,   1,   2,   1};    
  UINT   OID_ipMACEntAddr[]   =   {1,   3,   6,   1,   2,   1,   2,   2,   1,   6};    
  AsnObjectIdentifier   MIB_ifMACEntAddr   =    
  {   sizeof(OID_ipMACEntAddr)   sizeof(UINT),   OID_ipMACEntAddr   };    
  AsnObjectIdentifier   MIB_ifEntryType   =    
  {sizeof(OID_ifEntryType)   sizeof(UINT),   OID_ifEntryType};    
  AsnObjectIdentifier   MIB_ifEntryNum   =    
  {sizeof(OID_ifEntryNum)   sizeof(UINT),   OID_ifEntryNum};    
  RFC1157VarBindList   varBindList;    
  RFC1157VarBind   varBind[2];    
  AsnInteger   errorStatus;    
  AsnInteger   errorIndex;    
  AsnObjectIdentifier   MIB_NULL   =   {0,   0};    
  int   ret;    
  int   dtmp;    
  int   i   =   0,   j   =   0;    
  bool   found   =   false;    
  char   TempEthernet[13];    
  m_Init   =   NULL;    
  m_InitEx   =   NULL;    
  m_Query   =   NULL;    
  m_Trap   =   NULL;    
   
  /*   载入SNMP   DLL并取得实例句柄   */    
  m_hInst   =   LoadLibrary("inetmib1.dll");    
  if   (m_hInst   <   (HINSTANCE)   HINSTANCE_ERROR)    
  {    
  m_hInst   =   NULL;    
  return;    
  }    
  m_Init   =    
  (pSnmpExtensionInit)   GetProcAddress(m_hInst,   "SnmpExtensionInit");    
  m_InitEx   =    
  (pSnmpExtensionInitEx)   GetProcAddress(m_hInst,    
  "SnmpExtensionInitEx");    
  m_Query   =    
  (pSnmpExtensionQuery)   GetProcAddress(m_hInst,    
  "SnmpExtensionQuery");    
  m_Trap   =    
  (pSnmpExtensionTrap)   GetProcAddress(m_hInst,   "SnmpExtensionTrap");    
  m_Init(GetTickCount(),   &PollForTrapEvent,   &SupportedView);    
   
  /*   初始化用来接收m_Query查询结果的变量列表   */    
  varBindList.list   =   varBind;    
  varBind[0].name   =   MIB_NULL;    
  varBind[1].name   =   MIB_NULL;    
   
  /*   在OID中拷贝并查找接口表中的入口数量   */    
  varBindList.len   =   1;   /*   Only   retrieving   one   item   */    
  SNMP_oidcpy(&varBind[0].name,   &MIB_ifEntryNum);    
  ret   =    
  m_Query(ASN_RFC1157_GETNEXTREQUEST,   &varBindList,   &errorStatus,    
  &errorIndex);    
  printf("#   of   adapters   in   this   system   :   %in",    
  varBind[0].value.asnValue.number);    
  varBindList.len   =   2;    
   
  /*   拷贝OID的ifType-接口类型   */    
  SNMP_oidcpy(&varBind[0].name,   &MIB_ifEntryType);    
   
  /*   拷贝OID的ifPhysAddress-物理地址   */    
  SNMP_oidcpy(&varBind[1].name,   &MIB_ifMACEntAddr);    
   
  do    
  {    
   
  /*   提交查询,结果将载入   varBindList。    
  可以预料这个循环调用的次数和系统中的接口卡数量相等   */    
  ret   =    
  m_Query(ASN_RFC1157_GETNEXTREQUEST,   &varBindList,   &errorStatus,    
  &errorIndex);    
  if   (!ret)    
  ret   =   1;    
  else    
  /*   确认正确的返回类型   */    
  ret   =    
  SNMP_oidncmp(&varBind[0].name,   &MIB_ifEntryType,    
  MIB_ifEntryType.idLength);   if   (!ret)   {    
  j++;    
  dtmp   =   varBind[0].value.asnValue.number;    
  printf("Interface   #%i   type   :   %in",   j,   dtmp);    
   
  /*   Type   6   describes   ethernet   interfaces   */    
  if   (dtmp   ==   6)    
  {    
   
  /*   确认我们已经在此取得地址   */    
  ret   =    
  SNMP_oidncmp(&varBind[1].name,   &MIB_ifMACEntAddr,    
  MIB_ifMACEntAddr.idLength);    
  if   ((!ret)   &&   (varBind[1].value.asnValue.address.stream   !=   NULL))    
  {    
  if((varBind[1].value.asnValue.address.stream[0]   ==   0x44)    
  &&   (varBind[1].value.asnValue.address.stream[1]   ==   0x45)    
  &&   (varBind[1].value.asnValue.address.stream[2]   ==   0x53)    
  &&   (varBind[1].value.asnValue.address.stream[3]   ==   0x54)    
  &&   (varBind[1].value.asnValue.address.stream[4]   ==   0x00))    
  {    
  /*   忽略所有的拨号网络接口卡   */    
  printf("Interface   #%i   is   a   DUN   adaptern",   j);    
  continue;    
  }    
  if   ((varBind[1].value.asnValue.address.stream[0]   ==   0x00)    
  &&   (varBind[1].value.asnValue.address.stream[1]   ==   0x00)    
  &&   (varBind[1].value.asnValue.address.stream[2]   ==   0x00)    
  &&   (varBind[1].value.asnValue.address.stream[3]   ==   0x00)    
  &&   (varBind[1].value.asnValue.address.stream[4]   ==   0x00)    
  &&   (varBind[1].value.asnValue.address.stream[5]   ==   0x00))    
  {    
  /*   忽略由其他的网络接口卡返回的NULL地址   */    
  printf("Interface   #%i   is   a   NULL   addressn",   j);    
  continue;    
  }    
  sprintf(TempEthernet,   "%02x%02x%02x%02x%02x%02x",    
  varBind[1].value.asnValue.address.stream[0],    
  varBind[1].value.asnValue.address.stream[1],    
  varBind[1].value.asnValue.address.stream[2],    
  varBind[1].value.asnValue.address.stream[3],    
  varBind[1].value.asnValue.address.stream[4],    
  varBind[1].value.asnValue.address.stream[5]);    
  printf("MAC   Address   of   interface   #%i:   %sn",   j,    
  TempEthernet);}    
  }    
  }    
  }   while   (!ret);   /*   发生错误终止。   */    
  getch();    
   
  FreeLibrary(m_hInst);    
  /*   解除绑定   */    
  SNMP_FreeVarBind(&varBind[0]);    
  SNMP_FreeVarBind(&varBind[1]);    
  }     
   
 

 

文档说明:

     

相关文档


读取评论列表……