【转载】 用 Windows API “GetAdaptersInfo” 获取 MAC 时遇到的问题
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 时遇到的问题的更多相关文章
- 【转载】Windows api数据类型
最近在接触windows api函数,看到了很多之前没有看到过的数据类型,发现“个人图书馆”中有个帖子说的挺详细的,特地搬运过来 Windows 数据类型 Delphi 数据类型 描述 LPSTR P ...
- Windows API的消息处理机制
上个学期找实习失利,让我觉得自己基础打得不够牢固,所以在华为实习的这三个月里,每天下班都在复习理论课的知识,顺便刷了一个月的 LeetCode.本来以为找工作是势在必得了,结果这个学期秋季校招的坑爹经 ...
- Windows API 磁盘
这里的磁盘就是你的C,D,E,F,G盘啦 那么来看看吧windows Api来获取信息的呢 (1)DWORD GetLogicalDrives(void) 返回值是一个32位整形 32位每一位表示一个 ...
- Windows API 错误码
在多数情况下,windows API在发生错误时很少抛出异常,多数是通过函数返回值进行处理.(windows api中无返回值的函数很少.) windows api错误处理通常按照以下方式:首先api ...
- 获取mac地址方法之一 GetAdaptersInfo()
GetAdaptersInfo -20151116 防止返回的mac出现null 20151116 From:http://blog.csdn.net/weiyumingwww/article/det ...
- c++ windows 获取mac地址
c++ windows 获取mac地址 GetAdaptersInfo 用windows api获取mac地址和硬盘id编号 aa
- 获取信息的有关Windows API(最有意思是OpenProcess和GetProcessMemoryInfo)
1.窗口信息MS为我们提供了打开特定桌面和枚举桌面窗口的函数.hDesk = OpenDesktop(lpszDesktop, 0, FALSE, DESKTOP_ENUMERATE);// 打开我们 ...
- 使用 SendARP 获取 MAC 地址(使用SendARP API函数,很多相关文章)
ARP 协议地址解析协议(ARP)是通过解析网路层地址来找寻数据链路层地址的一个在网络协议包中极其重要的网络传输协议.ARP 最初在 1982 年的 RFC 826 中提出并纳入互联网标准 STD 3 ...
- 【转载】获取MAC地址方法大全
From:http://blog.csdn.net/han2814675/article/details/6223617 Windows平台下用C++代码取得机器的MAC地址并不是一件简单直接的事情. ...
随机推荐
- Mixed mode assembly is built against version 'v2.0.50727' of the runtime and cannot be loaded in the 4.0 runtime without additional configuration info
Mixed mode assembly is built against version 'v2.0.50727' of the runtime and cannot be loaded in the ...
- Linux 进程管理 进程优先级管理
Linux进程优先级 Linux 是一个多用户.多任务的操作系统,系统中通常运行着非常多的进程.但是 CPU 在一个时钟周期内只能运算一条指令(现在的 CPU 采用了多线程.多核心技术,所以在一个时钟 ...
- [转]hadoop2.x常用端口及定义方法
端口 Hadoop集群的各部分一般都会使用到多个端口,有些是daemon之间进行交互之用,有些是用于RPC访问以及HTTP访问.而随着Hadoop周边组件的增多,完全记不住哪个端口对应哪个应用,特收集 ...
- OpenCL 学习step by step (5) 使用二维NDRange workgroup
http://www.cnblogs.com/mikewolf2002/archive/2012/09/07/2675634.html 在本教程中,我们使用二维NDRange来设置workgroup, ...
- spring security使用数据库验证的逻辑处理
前面做了多个示例,包括使用jdbc和hibernate两种方式访问数据库获取用户信息和权限信息,其中一些关键步骤如下: 我们在SecurityConfig中配置覆盖configure方法时候,可以 ...
- spring security结合数据库验证用户-注解方式
项目目录结构如下: 首先数据库的建立和数据导入,以及一些类的依赖参考XML配置方式,需要修改一些配置. 一.在AppConfig文件中添加DataSource的配置 @Bean(name = &quo ...
- Luogu-3648 [APIO2014]序列分割
Luogu-3648 [APIO2014]序列分割 题目链接 题解: 首先要发现一个重要的性质:分割的顺序是不会影响答案的 证明: 首先对于没有交的两段区间,显然先后顺序改变不会有影响 而对于在同一段 ...
- 【bzoj5452】[Hnoi2016]大数(莫队)
题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=4542 首先若p=2,5则这题就是道傻逼题,前缀和搞一下没了.如果p为其他质数,那么可以 ...
- C++中虚函数和纯虚函数的区别与总结
首先:强调一个概念 定义一个函数为虚函数,不代表函数为不被实现的函数. 定义他为虚函数是为了允许用基类的指针来调用子类的这个函数. 定义一个函数为纯虚函数,才代表函数没有被实现. 定义纯虚函数是为了实 ...
- Dos命令的巧用 - 转载
Dos命令的巧用 豪华绚丽的Windows让人们把DOS抛到遥远的记忆角落,然而,真正有价值的东西不会轻易退出历史的舞台.很多人都已经习惯于 Windows的图形化用户界面,熟不知古老的DOS命令却可 ...