实验室IP和MAC绑定,而且经常来回于各个实验室和宿舍,频繁的地址切换,带来了相当的烦恼。想做这样一个工具是很久以前的想法,可到现在都没有做;没有行动的想法都是空谈,抱着锻炼自己行动力的决心,完成了这个小工具。
一.工具介绍
工具界面展示:
本工具主要功能:
(1)能够找出所有的网卡适配器,并显示适配器的IP、MAC等信息
(2)能够修改IP、DNS、MAC等地址信息
(3)能够通过配置文件增加/删除网卡信息配置方案,并且能够根据方案进行地址切换,配置文件如下图所示:
二.IP、DNS、MAC的显示功能
2.1如何获取IP、DNS、MAC信息
主要利用Iphlpapi库获取网卡信息,Iphlpapi中IP_ADAPTER_INFO包含了IP、MAC信息但并没有包含DNS:
//获取IP地址信息
void CIpChangeDlg::GetIpAddrsInfo()
{
/*******************************************
*通过Iphlpapi库获取网卡信息和个数
********************************************/
PIP_ADAPTER_INFO pIpAdapterInfo = new IP_ADAPTER_INFO();
ULONG stSize = sizeof(IP_ADAPTER_INFO);
int nRel = GetAdaptersInfo(pIpAdapterInfo, &stSize); //获得其大小
if (ERROR_BUFFER_OVERFLOW == nRel) //重新申请所需要的空间
{
delete pIpAdapterInfo;
pIpAdapterInfo = (PIP_ADAPTER_INFO) new BYTE[stSize];
nRel=GetAdaptersInfo(pIpAdapterInfo, &stSize);
}
if (ERROR_SUCCESS == nRel) //获取信息成功
{
m_AdapterInfo.pIpAdapterInfo = pIpAdapterInfo;
m_AdapterInfo.iCount = 0;
while (pIpAdapterInfo) //获取网卡个数
{
m_AdapterInfo.iCount++;
pIpAdapterInfo = pIpAdapterInfo->Next;
}
}
}
根据IP_ADAPTER_INFO中的Index,获取相应网卡所使用的DNS信息:
//DNS,pIpAdapterInfo为网卡适配器信息结构PIP_ADAPTER_INFO
IP_PER_ADAPTER_INFO* pPerAdapt = NULL;
ULONG ulLen = 0;
int err = GetPerAdapterInfo( pIpAdapterInfo->Index, pPerAdapt, &ulLen);
if( err == ERROR_BUFFER_OVERFLOW ) {
pPerAdapt = ( IP_PER_ADAPTER_INFO* ) HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, ulLen);
err = GetPerAdapterInfo( pIpAdapterInfo->Index, pPerAdapt, &ulLen );
if( err == ERROR_SUCCESS ) {
IP_ADDR_STRING* pNext = &( pPerAdapt->DnsServerList );
if (pNext && strcmp(pNext->IpAddress.String, "") != 0)
{
m_DNSDHCPBUTTON.SetCheck(FALSE);
m_PrimaryDnsCtl.EnableWindow(TRUE);
m_BackupDnsCtl.EnableWindow(TRUE);
m_PrimaryDnsCtl.SetWindowText(pNext->IpAddress.String);
if (pNext = pNext->Next)
m_BackupDnsCtl.SetWindowText(pNext->IpAddress.String);
}
else
{
m_DNSDHCPBUTTON.SetCheck(TRUE);
m_PrimaryDnsCtl.EnableWindow(FALSE);
m_BackupDnsCtl.EnableWindow(FALSE);
m_PrimaryDnsCtl.SetWindowText("");
m_BackupDnsCtl.SetWindowText("");
}
}
HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, pPerAdapt);
}
2.2如何获得网络连接名称?
对一个网络适配器的名称有三种描述:
(1)适配器对应的注册表中的一个注册表项值,称其为AdapterName;这个信息将在修改MAC的时使用;
(2)适配器描述名称,称其为AdapterDestription,比如本人主机的某一个网卡描述为:“Microsoft Virtual WiFi Miniport Adapter”;
(3)连接名称,就是在操作系统的网络连接中看到的连接名,如"本地连接"等;这个信息将在修改IP/DNS的时使用;
本工具采用netsh命令行方式进行IP/DNS的修改,命令行中进行修改的时候要指定连接名称,如"本地连接",但利用Iphlpapi库并不能获取连接名称,通过Mprapi库中的API和IP_ADAPTER_INFO中的Index 得到网卡所对应的连接名称。
void CIpChangeDlg::GetConnectNames()
{
/*******************************************
*通过mprapi库获取连接名称
*并通过index将网卡信息和连接名称相关联
********************************************/
HANDLE hMprConfig; //连接信息的句柄
DWORD dwRet=0; //返回值
PIP_INTERFACE_INFO plfTable = NULL; //接口信息表
DWORD dwBufferSize=0; //接口信息表空间大小
m_AdapterInfo.csConnectName = new char [m_AdapterInfo.iCount] [256]; //申请空间存储连接名
dwRet = MprConfigServerConnect(NULL, &hMprConfig); //获得句柄
dwRet = GetInterfaceInfo(NULL, &dwBufferSize); //获得接口信息表大小
if(dwRet == ERROR_INSUFFICIENT_BUFFER) //获得接口信息
{
plfTable = (PIP_INTERFACE_INFO)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, dwBufferSize);
GetInterfaceInfo(plfTable, &dwBufferSize);
}
TCHAR szFriendName[256]; //接口名称
DWORD tchSize = sizeof(TCHAR) * 256;
ZeroMemory(&szFriendName, tchSize);
for (UINT i = 0; i < plfTable-> NumAdapters; i++)
{
IP_ADAPTER_INDEX_MAP AdaptMap; //接口信息
AdaptMap = plfTable->Adapter[i];
dwRet = MprConfigGetFriendlyName(hMprConfig, AdaptMap.Name,
(PWCHAR)szFriendName, tchSize); //获得连接名称unicode
USES_CONVERSION;
char* pName = W2A((PWCHAR)szFriendName); //转换为ansi
InsertConnectName(AdaptMap.Index, pName); //根据Index存储名字信息
}
HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, plfTable);
}
三.IP、DNS修改
第二章节中以及提到,工具采用命令行方式进行修改;
修改IP命令:
netsh interface ip set address "连接名称" dhcp //dhcp方式
netsh interface ip set address "连接名称" static IP地址 子网掩码 网关 //静态IP方式
修改DNS命令:
netsh interface ip set dnsservers "连接名称" dhcp //自动获取方式
netsh interface ip set dnsservers "连接名称" static IP地址 //设置主 DNS
netsh interface ip add dnsservers "连接名称" IP地址 //设置从属 DNS
四.MAC修改
(1)第一步寻找适配器MAC所在的注册表位置
在注册表"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002bE10318}"目录下遍历寻找 与 AdapterName(2.2节中所提AdatpterName)相同的注册表项NetCfgInstanceId,则相应的MAC地址存储在相同目录项中的NetworkAddress中。
(2)修改MAC
修改NetworkAddress中的值
(3)重启网卡
重启网卡也采用命令行方式:
netsh interface set interface "连接名称" DISABLE //使网卡失效
netsh interface set interface "连接名称" ENABLE //启动网卡
五.细节问题处理
5.1 命令行方式修改产生的console窗口问题
//启动cmd执行 netsh命令,并等待命令结束
void CIpChangeDlg::ExcuteCommand(char* pCommandParam)
{
//初始化shellexe信息
SHELLEXECUTEINFO ExeInfo;
ZeroMemory(&ExeInfo, sizeof(SHELLEXECUTEINFO));
ExeInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ExeInfo.lpFile = "cmd.exe";
ExeInfo.lpParameters = pCommandParam;
ExeInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
ExeInfo.nShow = SW_HIDE;
ExeInfo.hwnd = NULL;
ExeInfo.lpVerb = NULL;
ExeInfo.lpDirectory = NULL;
ExeInfo.hInstApp = NULL;
//执行命令
ShellExecuteEx(&ExeInfo);
//等待进程结束
WaitForSingleObject(ExeInfo.hProcess, INFINITE);
}
5.2 窗口假死问题
如果直接在按钮按下响应消息中执行命令,命令执行期间窗口将假死(因为消息循环因命令执行而阻塞);通过创建新的线程去进行修改操作,并在线程执行结束后,发送一个自定义消息WM_MSG_MODIFY_COMPLETE(表示命令执行结束)。