首 页 | 新 闻 | 技术中心 | 第二书店 | 《程序员》 | 《开发高手》 | 社 区 | 黄 页 | 人 才
移 动专 题SUNIBM微 软微 创精 华Donews人 邮
我的技术中心 
我的分类 我的文档
全部文章 发表文章
专栏管理 使用说明



 RSS 订阅 
最新文档列表
Windows/.NET
.NET  (rss)    
Visual C++  (rss)    
Delphi  (rss)    
Visual Basic  (rss)    
ASP  (rss)    
JavaScript  (rss)    
Java/Linux
Java  (rss)    
Perl  (rss)    
综合
其他开发语言  (rss)    
文件格式  (rss)    
企业开发
游戏开发  (rss)    
网站制作技术  (rss)    
数据库
数据库开发  (rss)    
软件工程
其他  (rss)    

积极原创作者 
tellmenow (22)
cutemouse (22)
softj (78)
iiprogram (69)
qdzx2008 (50)
goodboy1881 (14)
wangchinaking (58)
fancyhf (1)
harrymeng (41)
yjz0065 (113)
CSDN - 文档中心 - Visual C++ 阅读:13925   评论: 14    参与评论
标题   取得系统中网卡MAC地址的三种方法     选择自 cker 的 Blog
关键字   NIC MAC CKER
出处   www.borland.com

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

做好的程序员一如做人。多看多想或许他山之石可以攻玉,但永远不要成为代码的奴隶。 CKER
原著:Borland
                                                                               翻译: CKER


 

Translated by CKER

第一种方法使用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[i], mac_addr))
    {
      cout << "Adapter " << int (AdapterList.lana[i]) <<
              "'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;
}


file://---------------------------------------------------------------------------


第二种方法-使用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]);
}


相关文章
对该文的评论
tywood ( 2005-03-06)
楼主给出的三个方法,我怎么都用不了,Panr老大的方法要DDK支持?
还有没有更好的方法啊?
tywood ( 2005-03-06)
补充一下,错误码是
error C2440: 'type cast' cannot convert from 'unsigned char' to 'class ...
tywood ( 2005-03-06)
方法1的下面这句我编译不通过:
    mac_addr += string(Ncb.ncb_retcode);
哪位老大告诉我是什么回事
SamWord ( 2004-11-21)
第二个有语法错误
SamWord ( 2004-11-21)
Thanks a lot!