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

[cpp] view plaincopy
  1. void GetMAC(BYTE mac[BUF_SIZE])
  2. {
  3. ULONG size_pointer;
  4. PIP_ADAPTER_INFO pip_adapter_info = NULL;
  5. if(ERROR_BUFFER_OVERFLOW != GetAdaptersInfo(NULL, &size_pointer))
  6. {
  7. wsprintfA((LPSTR)mac, "GetMAC Failed! ErrorCode: %d", GetLastError());
  8. return;
  9. }
  10. pip_adapter_info = (PIP_ADAPTER_INFO)calloc(size_pointer, sizeof(BYTE));
  11. if(NULL == pip_adapter_info)
  12. {
  13. lstrcpyA((LPSTR)mac, "GetMAC Failed! Because calloc failed!");
  14. return;
  15. }
  16. if(ERROR_SUCCESS != GetAdaptersInfo(pip_adapter_info, &size_pointer))
  17. {
  18. wsprintfA((LPSTR)mac, "GetMAC Failed! ErrorCode: %d", GetLastError());
  19. free(pip_adapter_info);
  20. return;
  21. }
  22. for(int i = 0; i < 6; ++i)
  23. {
  24. wsprintfA((LPSTR)mac, "%02X", pip_adapter_info->Address[i]);
  25. mac += 2;
  26. }
  27. }

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

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

[cpp] view plaincopy
  1. #include <windows.h>
  2. #include <iphlpapi.h>       // API GetAdaptersInfo 头文件
  3. #include <shlwapi.h>        // API StrCmpIA 头文件
  4. #pragma comment(lib, "iphlpapi.lib")
  5. #pragma comment(lib, "shlwapi.lib")
  6. #include <Strsafe.h>        // API StringCbPrintfA 头文件
  7. #include <shellapi.h>       // API lstrcpyA 头文件
  8. //
  9. // 功能:获取适配器特性
  10. // 参数:
  11. //   adapter_name 适配器 ID
  12. // 返回值:成功则返回由参数指定的适配器的特性标志,是一个 DWORD 值,失败返回 0
  13. //
  14. UINT GetAdapterCharacteristics(char* adapter_name)
  15. {
  16. if(adapter_name == NULL || adapter_name[0] == 0)
  17. return 0;
  18. HKEY root = NULL;
  19. // 打开存储适配器信息的注册表根键
  20. if(ERROR_SUCCESS != RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}", 0, KEY_READ, &root))
  21. return 0;
  22. DWORD subkeys = 0;
  23. // 获取该键下的子键数
  24. if(ERROR_SUCCESS != RegQueryInfoKeyA(root, NULL, NULL, NULL, &subkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
  25. subkeys = 100;
  26. DWORD ret_value = 0;
  27. for(DWORD i = 0; i < subkeys; i++)
  28. {
  29. // 每个适配器用一个子键存储,子键名为从 0 开始的 4 位数
  30. char subkey[MAX_SIZE];
  31. memset(subkey, 0, MAX_SIZE);
  32. StringCbPrintfA(subkey, MAX_SIZE, "%04u", i);
  33. // 打开该子键
  34. HKEY hKey = NULL;
  35. if(ERROR_SUCCESS != RegOpenKeyExA(root, subkey, 0, KEY_READ, &hKey))
  36. continue;
  37. // 获取该子键对应的适配器 ID,存于 name 中
  38. char name[MAX_PATH];
  39. DWORD type = 0;
  40. DWORD size = MAX_PATH;
  41. if(ERROR_SUCCESS != RegQueryValueExA(hKey, "NetCfgInstanceId", NULL, &type, (LPBYTE)name, &size))
  42. {
  43. RegCloseKey(hKey);
  44. continue;
  45. }
  46. // 对比该适配器 ID 是不是要获取特性的适配器 ID
  47. if(StrCmpIA(name, adapter_name) != 0)
  48. {
  49. RegCloseKey(hKey);
  50. continue;
  51. }
  52. // 读取该适配器的特性标志,该标志存储于值 Characteristics 中
  53. DWORD val = 0;
  54. size = 4;
  55. LSTATUS ls = RegQueryValueExA(hKey, "Characteristics", NULL, &type, (LPBYTE)&val, &size);
  56. RegCloseKey(hKey);
  57. if(ERROR_SUCCESS == ls)
  58. {
  59. ret_value = val;
  60. break;
  61. }
  62. }
  63. RegCloseKey(root);
  64. return ret_value;
  65. }
  66. //
  67. // 功能:获取 Mac 地址的二进制数据
  68. // 参数:
  69. //   mac 用于输出 Mac 地址的二进制数据的缓冲区指针
  70. // 返回值:成功返回 mac 地址的长度,失败返回 0,失败时 mac 中保存一些简单的错误信息,可适当修改,用于调试
  71. //
  72. int GetMAC(BYTE mac[BUF_SIZE])
  73. {
  74. #define NCF_PHYSICAL 0x4
  75. DWORD AdapterInfoSize = 0;
  76. if(ERROR_BUFFER_OVERFLOW != GetAdaptersInfo(NULL, &AdapterInfoSize))
  77. {
  78. StringCbPrintfA((LPSTR)mac, BUF_SIZE, "GetMAC Failed! ErrorCode: %d", GetLastError());
  79. return 0;
  80. }
  81. void* buffer = malloc(AdapterInfoSize);
  82. if(buffer == NULL)
  83. {
  84. lstrcpyA((LPSTR)mac, "GetMAC Failed! Because malloc failed!");
  85. return 0;
  86. }
  87. PIP_ADAPTER_INFO pAdapt = (PIP_ADAPTER_INFO)buffer;
  88. if(ERROR_SUCCESS != GetAdaptersInfo(pAdapt, &AdapterInfoSize))
  89. {
  90. StringCbPrintfA((LPSTR)mac, BUF_SIZE, "GetMAC Failed! ErrorCode: %d", GetLastError());
  91. free(buffer);
  92. return 0;
  93. }
  94. int mac_length = 0;
  95. while(pAdapt)
  96. {
  97. if(pAdapt->AddressLength >= 6 && pAdapt->AddressLength <= 8)
  98. {
  99. memcpy(mac, pAdapt->Address, pAdapt->AddressLength);
  100. mac_length = pAdapt->AddressLength;
  101. UINT flag = GetAdapterCharacteristics(pAdapt->AdapterName);
  102. bool is_physical = ((flag & NCF_PHYSICAL) == NCF_PHYSICAL);
  103. if(is_physical)
  104. break;
  105. }
  106. pAdapt = pAdapt->Next;
  107. }
  108. free(buffer);
  109. return mac_length;
  110. }
  111. //
  112. // 功能:获取 Mac 地址,使用时直接调用此函数即可
  113. // 参数:
  114. //   mac 用于存储 Mac 地址的缓冲区指针
  115. // 返回值:无返回值,函数执行完后会把 Mac 地址以16进制的形式存于参数指定的缓冲区中,若有错误,缓冲区中保存的是错误信息
  116. //
  117. void GetMacAddress( char* mac )
  118. {
  119. BYTE buf[BUF_SIZE];
  120. memset(buf, 0, BUF_SIZE);
  121. int len = GetMAC(buf);
  122. if(len <= 0)
  123. {
  124. lstrcpyA(mac, (LPCSTR)buf);
  125. return;
  126. }
  127. if(len == 6)
  128. StringCbPrintfA(mac, BUF_SIZE, "%02X-%02X-%02X-%02X-%02X-%02X", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
  129. else
  130. StringCbPrintfA(mac, BUF_SIZE, "%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
  131. }

编译环境: VS2008 + Windows SDK 7.1

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

用 Windows API “GetAdaptersInfo” 获取 MAC 时遇到的问题相关推荐

  1. php获取主板序列号,PHP获取通过windows系统命令wmic获取MAC地址、CPU序列号、主板序列号...

    在项目中,客户需要系统在win系统上获取MAC地址.CPU序列号和主板序列号等,在网上搜索下,通过windows系统命令wmic可以获取,测试基本可行,HardwareInfo.php源代码如下: $ ...

  2. GetAdaptersInfo获取MAC地址

    源代码: #include<atlbase.h> #include<atlconv.h> #include"iphlpapi.h" #pragma comm ...

  3. Windows API ——GetLogicalDriveStrings——获取逻辑驱动器

    1 TCHAR buffer[MAX_PATH]; 2 DWORD dwVal=::GetLogicalDriveStrings(MAX_PATH,buffer); 3 4 CString str; ...

  4. C# 使用Windows API获取系统当前鼠标信息(图案)

    通过使用Windows API来获取当前鼠标的图案,不论是系统图片还是自定义图标都能够获取到,在这个示例中,为了方便测试,给Form1添加了一个KeyPress事件,在程序激活状态下,将鼠标移动到任意 ...

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

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

  6. Windows/Linux获取Mac地址和CPU序列号实现

    UUID(Universally Unique Identifier)即通用唯一标识符,是指在一台机器上生成的数字,保证在全球范围的唯一性.可用的开源库如libuuid,可参考https://blog ...

  7. Windows API的时间结构体、时间转换及时间获取

    Windows API的时间结构体.时间转换及时间获取   时间格式:DosDateTime <====>FileTime <====> SystemTime |        ...

  8. C# 获得窗体句柄并发送消息(利用windows API可在不同进程中获取)

    C#使用Windows API获取窗口句柄控制其他程序窗口 编写程序模拟鼠标和键盘操作可以方便的实现你需要的功能,而不需要对方程序为你开放接口.比如,操作飞信定时发送短信等.我之前开发过飞信耗子,用的 ...

  9. C#通过Windows API捕获窗,获取窗口文本(FindWindow、GetWindowText),附录:Windows窗口消息大全、Windows API大全

    文章目录 一.前言 二.使用Spy++工具分析窗口 三.C#通过Windows API捕获窗口,获取窗口文本 四.附录:Windows窗口消息 五.Windows API大全 1.API之网络函数 2 ...

最新文章

  1. 2022-2028年中国TAC薄膜行业市场全景评估及投资前景规划报告
  2. 03--MySQL自学教程:MySQL安装后的目录结构介绍和简单语法指令
  3. 集成学习算法之boosting、bagging和随机森林算法原理
  4. 网站搭建从零开始(四) 服务器的配置
  5. 图片优化_网站里的图片应该如何优化
  6. 人类首张黑洞照片咋拍的:7千TB数据太大网络传不了只能飞机运
  7. 华为云教你7天玩转电商应用性能调优,课程免费速来报名!
  8. FinTech领域的风险控制——策略篇
  9. 修改tomcat默认端口号
  10. html验证码 按住向右滑动,js实现滑动滑块验证登录
  11. tolua++ 参考手册
  12. 台式计算机配置单,最新台式电脑组装配置推荐
  13. RuoYi-flowable工作流管理
  14. 穿膜肽TAT修饰载荧光探针香豆素-6脂质体
  15. 北京折叠——一部刚要开始就已经结束的科幻小说
  16. 测试开发人员与开发人员_我是真正的开发人员还是优秀的Googler?
  17. 【联盛德W806上手笔记】六、7816/UART 控制器
  18. linux 查看dhcp dns,RHEL6 DNS+DHCP+DDNS
  19. mysql 页分裂_InnoDB中的页合并与分裂
  20. vue项目获取下拉框选中id_vue获取下拉框值

热门文章

  1. 在Mac OS X中配置Apache + PHP + MySQL
  2. GCT之数学公式(代数部分)
  3. apimonitor 神器啊 。。
  4. 解决 DNS general: warning: *** POKED TIMER ***
  5. 借东西的小人阿莉埃蒂
  6. 我看.Net My Services
  7. docker 命令详解
  8. Linux搭建Maven私服, 使项目公用Android aar
  9. 【逆向】UE4 渲染流程分析
  10. Linux基础操作优化