修改IP、DNS、MAC工具VC源码实现

    实验室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窗口问题

采用命令行方式来修改IP,首先想到的是system("命令");但这种方式执行指令的时候会弹出console窗口,显然用户体验降低;那如何屏蔽窗口呢? 采用ShellExcuteEx,可以创建一个新的进程去执行修改命令,并且在程序中适用WaitForSingleObject等待进程结束。
//启动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(表示命令执行结束)。

  • 7
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值