参考资料
- 中文版:Windows网络编程(第二版).pdf
- 英文版:Network Programming for Windows[2Ed 2002 MS Press].CHM
- MSDN 2005
准备工作
MSDN
在MSDN上查找 GetAdaptersAddresses ,可以得到这个函数的详细解释,另外有这个API的示例代码,拷贝过来:
PIP_ADAPTER_ADDRESSES pAddresses;
pAddresses = (IP_ADAPTER_ADDRESSES*) malloc(sizeof(IP_ADAPTER_ADDRESSES));
ULONG outBufLen = 0;
DWORD dwRetVal = 0;
// Make an initial call to GetAdaptersAddresses to get the
// size needed into the outBufLen variable
if (GetAdaptersAddresses(AF_INET,
0,
NULL,
pAddresses,
&outBufLen) == ERROR_BUFFER_OVERFLOW) {
GlobalFree(pAddresses);
pAddresses = (IP_ADAPTER_ADDRESSES*) malloc(outBufLen);
}
// Make a second call to GetAdapters Addresses to get the
// actual data we want
if ((dwRetVal = GetAdaptersAddresses(AF_INET,
0,
NULL,
pAddresses,
&outBufLen)) == NO_ERROR) {
// If successful, output some information from the data we received
while (pAddresses) {
printf("\tFriendly name: %S\n", pAddresses->FriendlyName);
printf("\tDescription: %S\n", pAddresses->Description);
pAddresses = pAddresses->Next;
}
}
else {
printf("Call to GetAdaptersAddresses failed.\n");
LPVOID lpMsgBuf;
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dwRetVal,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL )) {
printf("\tError: %s", lpMsgBuf);
}
LocalFree( lpMsgBuf );
}
《网络编程》
在这本书中的IP Helper Functions一章,讲到ipconfig。也可以重点参考。不截图、不拷贝代码了。
VS版本问题
在新一些的VS版本上,是有api直接获取的。在MSDN2005上可以查到
对于那个的api,估计2005或以上都可以。——2003是否支持就不好说了,得实际验证一下。
至少 VC6上是没有这个api的。
VS2010
在以上参考资料的基础上,给出下面的示例代码。这是一个简单的MFC对话框,只给出主要的一些代码,其他的自然可以自己补充。
头文件&lib
#include <windows.h>
#include <iphlpapi.h>
#pragma comment(lib, "Iphlpapi.lib")
成员函数:
MSDN的示例代码有内存泄漏的问题,做了优化。另外,关心的是MAC地址,所以只对这一块的数据做了处理。
头文件声明部分:
private:
void GetMacAddress();
void OutputMacAddress(PIP_ADAPTER_ADDRESSES pAddresses);
实现代码:
void CGetMacAddressDlg::OnBnClickedOk()
{
// TODO: 在此添加控件通知处理程序代码
GetMacAddress();
CDialogEx::OnOK();
}
void CGetMacAddressDlg::OutputMacAddress(PIP_ADAPTER_ADDRESSES pAddresses)
{
DWORD physicalAddressLength = pAddresses->PhysicalAddressLength;
if (0 == physicalAddressLength) return;
CString macAddress;
CString sByte;
for (DWORD i = 0; i < physicalAddressLength; i++) {
sByte.Format(_T("%02X"), pAddresses->PhysicalAddress[i]);
if (i > 0) macAddress += _T("-");
macAddress += sByte;
}
MessageBox((LPCTSTR)macAddress, _T("Test"), MB_OK);
}
void CGetMacAddressDlg::GetMacAddress()
{
PIP_ADAPTER_ADDRESSES pAddresses;
PIP_ADAPTER_ADDRESSES orgAddresses;
pAddresses = (IP_ADAPTER_ADDRESSES*) malloc(sizeof(IP_ADAPTER_ADDRESSES));
orgAddresses = pAddresses;
ULONG outBufLen = 0;
DWORD dwRetVal = 0;
// Make an initial call to GetAdaptersAddresses to get the
// size needed into the outBufLen variable
if (GetAdaptersAddresses(AF_INET,
0,
NULL,
pAddresses,
&outBufLen) == ERROR_BUFFER_OVERFLOW) {
free(pAddresses);
pAddresses = (IP_ADAPTER_ADDRESSES*) malloc(outBufLen);
orgAddresses = pAddresses;
}
// Make a second call to GetAdapters Addresses to get the
// actual data we want
if ((dwRetVal = GetAdaptersAddresses(AF_INET,
0,
NULL,
pAddresses,
&outBufLen)) == NO_ERROR) {
// If successful, output some information from the data we received
while (pAddresses) {
OutputMacAddress(pAddresses);
pAddresses = pAddresses->Next;
}
} else {
LPVOID lpMsgBuf;
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dwRetVal,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL )) {
TRACE("\tError: %s", lpMsgBuf);
}
free( lpMsgBuf );
}
free(orgAddresses);
}
VC6
如前面所示,上面的api在vc6上是不支持的。所以vc6需要另外的方式,比如网上有提到安装对应sdk库的方法。另外简单描述后一种方法。
对于DOS命令的方法,包括如下两点:
- 执行DOS命令,获取命令执行结果。参考:VC下面执行DOS命令,这篇文章附了vc6示例代码工程。
- 从命令执行结果中获取目标数据。可以用正则表达式来匹配,即便多网卡多MAC地址也是可以的。参考:Windows上面编译pcre的步骤、C语言的一个正则表达式pcre,另外在Python入门教程-12 正则表达式中给出了正则表达式的语法参考书籍。
DOS命令的执行结果示例:
C:\>ipconfig /all
Windows IP 配置
主机名 . . . . . . . . . . . . . : the_computer_name
主 DNS 后缀 . . . . . . . . . . . :
节点类型 . . . . . . . . . . . . : 混合
IP 路由已启用 . . . . . . . . . . : 否
WINS 代理已启用 . . . . . . . . . : 否
以太网适配器 本地连接:
连接特定的 DNS 后缀 . . . . . . . :
描述. . . . . . . . . . . . . . . : ......
物理地址. . . . . . . . . . . . . : 78-AF-35-C6-34-43
DHCP 已启用 . . . . . . . . . . . : 是
自动配置已启用. . . . . . . . . . : 是
本地链接 IPv6 地址. . . . . . . . : ......
IPv4 地址 . . . . . . . . . . . . : ......
子网掩码 . . . . . . . . . . . . : 255.255.255.0
获得租约的时间 . . . . . . . . . : ......
租约过期的时间 . . . . . . . . . : ......
默认网关. . . . . . . . . . . . . : ......
DHCP 服务器 . . . . . . . . . . . : ......
DHCPv6 IAID . . . . . . . . . . . : ......
DHCPv6 客户端 DUID . . . . . . . : ......
DNS 服务器 . . . . . . . . . . . : ......
TCPIP 上的 NetBIOS . . . . . . . : 已启用
隧道适配器 . . .
C:\>
可以只关心下面这个:
物理地址. . . . . . . . . . . . . : 78-AF-35-C6-34-43
对应的正则表达式语法:
"\\s([\\da-fA-F]{2}-[\\da-fA-F]{2}-[\\da-fA-F]{2}-[\\da-fA-F]{2}-[\\da-fA-F]{2}-[\\da-fA-F]{2})[\\s\\r]"
这里直接给出了C中的语法,即两个’\’。
在C语言的一个正则表达式pcre中提到了多次匹配的概念,因此可以获取ipconfig /all中的所有MAC地址。在得到字符串的MAC地址之后,就可以再转换成字节流或其他需要的形式。
注:如果DOS命令执行完之后不能马上读到结果,可以Sleep()一会,然后再ReadFile()。