From:http://blog.csdn.net/weiyumingwww/article/details/17554461

前段时间有个项目需要获取客户端的 MAC 地址,用作统计去重的参考数据。从网上查到的获取 MAC 地址的代码,大多是用同一段代码修改的。于是我也用了那段代码。代码如下:

void GetMAC(BYTE mac[BUF_SIZE])
{
ULONG size_pointer;
PIP_ADAPTER_INFO pip_adapter_info = NULL; if(ERROR_BUFFER_OVERFLOW != GetAdaptersInfo(NULL, &size_pointer))
{
wsprintfA((LPSTR)mac, "GetMAC Failed! ErrorCode: %d", GetLastError());
return;
} pip_adapter_info = (PIP_ADAPTER_INFO)calloc(size_pointer, sizeof(BYTE));
if(NULL == pip_adapter_info)
{
lstrcpyA((LPSTR)mac, "GetMAC Failed! Because calloc failed!");
return;
} if(ERROR_SUCCESS != GetAdaptersInfo(pip_adapter_info, &size_pointer))
{
wsprintfA((LPSTR)mac, "GetMAC Failed! ErrorCode: %d", GetLastError());
free(pip_adapter_info);
return;
} for(int i = ; i < ; ++i)
{
wsprintfA((LPSTR)mac, "%02X", pip_adapter_info->Address[i]);
mac += ;
}
}

在自己的电脑上、虚拟机上测试了一番,没有发现问题,觉得一切 OK 之后将产品发布出去,结果发现许多机器返回的 MAC 为 NULL!!!

问题严重,又重新研究了一番,发现之所以 MAC 返回 NULL 是因为这段代码并不完整:用 GetAdaptersInfo 获取到的不一定是网卡信息,有可能是别的适配器信息,不是网卡就没有 MAC,结果当然为 NULL!花了点时间把代码完善了一下,加了个检测适配器特性的函数,完成了该功能,最终的代码如下:

#include <windows.h>
#include <iphlpapi.h> // API GetAdaptersInfo 头文件
#include <shlwapi.h> // API StrCmpIA 头文件
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "shlwapi.lib")
#include <Strsafe.h> // API StringCbPrintfA 头文件
#include <shellapi.h> // API lstrcpyA 头文件 //////////////////////////////////////
// 功能:获取适配器特性
// 参数:
// adapter_name 适配器 ID
// 返回值:成功则返回由参数指定的适配器的特性标志,是一个 DWORD 值,失败返回 0
//
UINT GetAdapterCharacteristics(char* adapter_name)
{
if(adapter_name == NULL || adapter_name[] == )
return ; HKEY root = NULL;
// 打开存储适配器信息的注册表根键
if(ERROR_SUCCESS != RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}", , KEY_READ, &root))
return ; DWORD subkeys = ;
// 获取该键下的子键数
if(ERROR_SUCCESS != RegQueryInfoKeyA(root, NULL, NULL, NULL, &subkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
subkeys = ; DWORD ret_value = ;
for(DWORD i = ; i < subkeys; i++)
{
// 每个适配器用一个子键存储,子键名为从 0 开始的 4 位数
char subkey[MAX_SIZE];
memset(subkey, , MAX_SIZE);
StringCbPrintfA(subkey, MAX_SIZE, "%04u", i); // 打开该子键
HKEY hKey = NULL;
if(ERROR_SUCCESS != RegOpenKeyExA(root, subkey, , KEY_READ, &hKey))
continue; // 获取该子键对应的适配器 ID,存于 name 中
char name[MAX_PATH];
DWORD type = ;
DWORD size = MAX_PATH;
if(ERROR_SUCCESS != RegQueryValueExA(hKey, "NetCfgInstanceId", NULL, &type, (LPBYTE)name, &size))
{
RegCloseKey(hKey);
continue;
} // 对比该适配器 ID 是不是要获取特性的适配器 ID
if(StrCmpIA(name, adapter_name) != )
{
RegCloseKey(hKey);
continue;
} // 读取该适配器的特性标志,该标志存储于值 Characteristics 中
DWORD val = ;
size = ;
LSTATUS ls = RegQueryValueExA(hKey, "Characteristics", NULL, &type, (LPBYTE)&val, &size);
RegCloseKey(hKey); if(ERROR_SUCCESS == ls)
{
ret_value = val;
break;
}
} RegCloseKey(root);
return ret_value;
} //////////////////////////////////////
// 功能:获取 Mac 地址的二进制数据
// 参数:
// mac 用于输出 Mac 地址的二进制数据的缓冲区指针
// 返回值:成功返回 mac 地址的长度,失败返回 0,失败时 mac 中保存一些简单的错误信息,可适当修改,用于调试
//
int GetMAC(BYTE mac[BUF_SIZE])
{
#define NCF_PHYSICAL 0x4
DWORD AdapterInfoSize = ;
if(ERROR_BUFFER_OVERFLOW != GetAdaptersInfo(NULL, &AdapterInfoSize))
{
StringCbPrintfA((LPSTR)mac, BUF_SIZE, "GetMAC Failed! ErrorCode: %d", GetLastError());
return ;
} void* buffer = malloc(AdapterInfoSize);
if(buffer == NULL)
{
lstrcpyA((LPSTR)mac, "GetMAC Failed! Because malloc failed!");
return ;
} PIP_ADAPTER_INFO pAdapt = (PIP_ADAPTER_INFO)buffer;
if(ERROR_SUCCESS != GetAdaptersInfo(pAdapt, &AdapterInfoSize))
{
StringCbPrintfA((LPSTR)mac, BUF_SIZE, "GetMAC Failed! ErrorCode: %d", GetLastError());
free(buffer);
return ;
} int mac_length = ;
while(pAdapt)
{
if(pAdapt->AddressLength >= && pAdapt->AddressLength <= )
{
memcpy(mac, pAdapt->Address, pAdapt->AddressLength);
mac_length = pAdapt->AddressLength; UINT flag = GetAdapterCharacteristics(pAdapt->AdapterName);
bool is_physical = ((flag & NCF_PHYSICAL) == NCF_PHYSICAL);
if(is_physical)
break;
}
pAdapt = pAdapt->Next;
}
free(buffer);
return mac_length;
} //////////////////////////////////////
// 功能:获取 Mac 地址,使用时直接调用此函数即可
// 参数:
// mac 用于存储 Mac 地址的缓冲区指针
// 返回值:无返回值,函数执行完后会把 Mac 地址以16进制的形式存于参数指定的缓冲区中,若有错误,缓冲区中保存的是错误信息
//
void GetMacAddress( char* mac )
{
BYTE buf[BUF_SIZE];
memset(buf, , BUF_SIZE); int len = GetMAC(buf);
if(len <= )
{
lstrcpyA(mac, (LPCSTR)buf);
return;
} if(len == )
StringCbPrintfA(mac, BUF_SIZE, "%02X-%02X-%02X-%02X-%02X-%02X", buf[], buf[], buf[], buf[], buf[], buf[]);
else
StringCbPrintfA(mac, BUF_SIZE, "%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X", buf[], buf[], buf[], buf[], buf[], buf[], buf[], buf[]);
}

编译环境: VS2008 + Windows SDK 7.1

函数功能在 Windows 2000、Windows XP、Windows 2003、Vista、 Win7 32位和 Win7 64 位下均测试通过。

 

【转载】 用 Windows API “GetAdaptersInfo” 获取 MAC 时遇到的问题的更多相关文章

  1. 【转载】Windows api数据类型

    最近在接触windows api函数,看到了很多之前没有看到过的数据类型,发现“个人图书馆”中有个帖子说的挺详细的,特地搬运过来 Windows 数据类型 Delphi 数据类型 描述 LPSTR P ...

  2. Windows API的消息处理机制

    上个学期找实习失利,让我觉得自己基础打得不够牢固,所以在华为实习的这三个月里,每天下班都在复习理论课的知识,顺便刷了一个月的 LeetCode.本来以为找工作是势在必得了,结果这个学期秋季校招的坑爹经 ...

  3. Windows API 磁盘

    这里的磁盘就是你的C,D,E,F,G盘啦 那么来看看吧windows Api来获取信息的呢 (1)DWORD GetLogicalDrives(void) 返回值是一个32位整形 32位每一位表示一个 ...

  4. Windows API 错误码

    在多数情况下,windows API在发生错误时很少抛出异常,多数是通过函数返回值进行处理.(windows api中无返回值的函数很少.) windows api错误处理通常按照以下方式:首先api ...

  5. 获取mac地址方法之一 GetAdaptersInfo()

    GetAdaptersInfo -20151116 防止返回的mac出现null 20151116 From:http://blog.csdn.net/weiyumingwww/article/det ...

  6. c++ windows 获取mac地址

    c++ windows 获取mac地址 GetAdaptersInfo 用windows api获取mac地址和硬盘id编号 aa

  7. 获取信息的有关Windows API(最有意思是OpenProcess和GetProcessMemoryInfo)

    1.窗口信息MS为我们提供了打开特定桌面和枚举桌面窗口的函数.hDesk = OpenDesktop(lpszDesktop, 0, FALSE, DESKTOP_ENUMERATE);// 打开我们 ...

  8. 使用 SendARP 获取 MAC 地址(使用SendARP API函数,很多相关文章)

    ARP 协议地址解析协议(ARP)是通过解析网路层地址来找寻数据链路层地址的一个在网络协议包中极其重要的网络传输协议.ARP 最初在 1982 年的 RFC 826 中提出并纳入互联网标准 STD 3 ...

  9. 【转载】获取MAC地址方法大全

    From:http://blog.csdn.net/han2814675/article/details/6223617 Windows平台下用C++代码取得机器的MAC地址并不是一件简单直接的事情. ...

随机推荐

  1. java 创建包含枚举的常量类

    参考 public class Constants { public static enum ServiceStatus{ NORMAL(1,"正常办理"),CHANGEING(2 ...

  2. rails 单数 复数 大写 小写转换 下划线 驼峰命名

    downcase 变小写 pluralize 复数 singularize 单数 camelcase 驼峰 underscore : “MyScore”.undersocre  ==> my_s ...

  3. Visual Studio各版本区别

    Visual Studio 是微软公司推出的开发环境,Visual Studio 可以用来创建 Windows 平台下的 Windows 应用程序和网络应用程序,也可以用来创建网络服务.智能设备应用程 ...

  4. APP测试的要点

    APP测试要点 功能性 UI界面 安装与卸载 升级 登录测试 离线测试 安全性测试 兼容性(操作系统,屏幕尺寸,分辨率,厂家) 消息推送 前后台切换 网络环境(wifi/2G/3G/4G/无网) 异常 ...

  5. 服务端Job调度程序

    GIT地址:https://github.com/youbl/PlanServer

  6. HttpStatusCode

    https://docs.microsoft.com/en-us/dotnet/api/system.net.httpstatuscode?view=netframework-4.7.2 422 Un ...

  7. quartz(7)-源码分析

    定时器启动 上图通过spring加载quartz <bean id="scheduler" class="org.springframework.schedulin ...

  8. UVA 10288 Coupons (概率)

    题意:有n种纸片无限张,随机抽取,问平均情况下抽多少张可以保证抽中所有类型的纸片 题解:假设自己手上有k张,抽中已经抽过的概率为 s=k/n:那抽中下一张没被抽过的纸片概率为 (再抽一张中,两张中,三 ...

  9. nagios无法载入静态资源

    使用nginx+nagios无法载入静态资源,看了下url中增加了一个/nagios 查看是/usr/local/nagios/etc/cgi.conf中url_html_path=/nagios 将 ...

  10. nginx重新加载配置(不停服)

    RT,改变配置想让它生效而不停止服务,如下两种方式都可以: 1) nginx -t;  nginx -s reload2) nginx -t;  kill -HUP