[译]获取系统网络MAC地址的三种方法[1]

[入库:2006年2月23日] [更新:2007年3月24日]

本文简介:

[原文]
[未经许可,不得转载]


获取系统网络MAC地址的三种方法--Borland开发人员支持团队

摘要: 本文描述了通过编程获取系统网卡(NIC)的MAC地址的三种不同方法。

三种方法得到系统MAC地址

曾经在新闻组和网页上不停的搜索,试图找到一种简单的方法来得到系统中的MAC(网络适配卡)地址。你也许猜想网上有很多可用的例子,因为非常多的人(尤其在新闻组中)需要这个问题答案。但事实并非这样。这些例子是我嘿咻嘿咻搜索的成果和一些我自己的实践。 注意:这三个例子中没有一个是通过粘贴ipconfig.exe /all的输出来实现。


目的
篇文章的目的是给出一些得知你MAC地址的简单方法。我会解释这些代码是如何工作的,并给出一些简单的例子来阐述。我假设你已经掌握了下面的概念:
  • Borland C++Builder
  • 简单的网络概念
  • 一些 Win32 API

方法一 - 用Netbios API

个方法是通过微软的Netbios API来得到你机器的MAC地址。这些API是一组提供比所谓的Winsock更底层的网络支持的命令。通过Netbios来得到地质这种方法的确定就是你必须安装了Netbios(如果你在一个Windows网络上并使用了文件共享,就没有这个问题)。另外,这个方法快速又准确。

Netbios API只包含了一个简称为Netbios的函数。这个函数通过一个网络控制块结构作为参数,来告诉函数需要做什么。这个结构的定义如下:

 
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中的定义如下:

CommandDescription
NCBENUMWindows NT/2000: Enumerates LAN adapter (LANA) numbers. When this code is specified, the ncb_buffer member points to a buffer to be filled with a LANA_ENUM structure.

NCBENUM is not a standard NetBIOS 3.0 command.
NCBRESETResets a LAN adapter. An adapter must be reset before it can accept any other NCB command that specifies the same number in the ncb_lana_num member.
NCBASTATRetrieves the status of either a local or remote adapter. When this code is specified, the ncb_buffer member points to a buffer to be filled with an ADAPTER_STATUS structure, followed by an array of NAME_BUFFER structures.

这就是得到一个或多个系统MAC地址的步骤:

  • 枚举所有的网络适配器
  • 重启每一个适配器以便得到它的正确信息
  • 查询该适配器来得到MAC地址并将地址填入标准的colon-separated格式(指用冒号分割的格式)
下面的代码简单是这些概念的简单示例。有关Netbios函数的更多信息请参考微软帮助文件或MSDN。

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);

// 取适配器信息,如果成功按标准colon-delimited格式返回它
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;
}


//---------------------------------------------------------------------------


方法二 - COM GUID API

个方法用COM API来创建一个GUID(globably unique identifier,全局唯一标识符)并且从那里得到MAC地址。GUID是用来一般地标识系统中的COM组件或者其他对象。他们通过MAC地址(再加上其他东西)计算出来的,并且在表面上看还将地址保留在GUID中。我说表面上的原因是这还不确定。我提供这个方法作主要是作为一个不要做什么的例子。按这种方法你有可能最终得到MAC地址,但也可能你最后会得到一些随机的十六进制数。

这个方法非常的简单,并不需要太多的解释。我们通过CoCreateGuid创建一个GUID并将最后的6字节存入一个字符串。这应该就是MAC地址,但就像我说的一样,并没有方法可以保证。

uuid.cpp
#include <windows.h>
#include <iostream>
#include <conio.h>

using namespace std;

int main()
{
cout << "MAC address is: ";

// 请求COM为我们创建一个UUID。如果本机有一个以太网适配器,UUID的最后
// 6字节(包含Data4的2-7字节)应该就是本地以太网适配器的MAC地址。
GUID uuid;
CoCreateGuid(&uuid);
// 将地址分割出来
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;
}

本文关键:[译]获取系统网络MAC地址的三种方法
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top