有很多网友都遇到过更改IP地址但是要重启机器的问题,在这里,为大家介绍几种不重启Windows直接更改IP地址的方法。首先是调用DhcpNotifyConfigChange的方法,后面还有修改注册表跟使用iphlpapi的方法。
一、未公开函数:DhcpNotifyConfigChange
运行效果图如下:
设置IP地址只需要更改注册表中关于适配器的相应设置,但更改后需要重新启动系统才能生效,而AddIPAddress函数只能添加IP而不是更改当前的IP,我们在Windows NT/2000界面上操作不需要重新启动就可以生效,那系统到底做了什么额外的工作才使IP设置直接生效呢?笔者通过跟踪explorer.exe中API的调用发现在netcfgx.dll中调用了dhcpcsvc.dll中一个未公开的API:DhcpNotifyConfigChange,现将不重新启动WINDOWS直接更改IP地址的详细方法介绍如下:
首先需要包括如下文件及代码:
#include “Iptypes.h”
#include “Iphlpapi.h”
#include “Winsock2.h”
#pragma comment(lib, “Iphlpapi.lib”)
typedef int (CALLBACK* DHCPNOTIFYPROC)(LPWSTR, LPWSTR, BOOL, DWORD, DWORD, DWORD, int);
这里指的适配器名称要区别于适配器描述,比如我的一块网卡,适配器描述是:Realtek RTL8139(A) PCI Fast Ethernet
Adapter,适配器名称为:{66156DC3-44A4-434C-B8A9-0E5DB4B3EEAD}。获取适配器名称的方法有多种:
1.1 调用IP helper API取得适配器名称
ULONG ulAdapterInfoSize = sizeof(IP_ADAPTER_INFO);
IP_ADAPTER_INFO *pAdapterInfoBkp, *pAdapterInfo = (IP_ADAPTER_INFO*)new
char[ulAdapterInfoSize];
if( GetAdaptersInfo(pAdapterInfo, &ulAdapterInfoSize) == ERROR_BUFFER_OVERFLOW
) // 缓冲区不够大
{
delete pAdapterInfo;
pAdapterInfo = (IP_ADAPTER_INFO*)new char[ulAdapterInfoSize];
pAdapterInfoBkp = pAdapterInfo;
}
if( GetAdaptersInfo(pAdapterInfo, &ulAdapterInfoSize) == ERROR_SUCCESS
)
{
do{ // 遍历所有适配器
if(pAdapterInfo->Type == MIB_IF_TYPE_ETHERNET) // 判断是否为以太网接口
{
// pAdapterInfo->Description 是适配器描述
// pAdapterInfo->AdapterName 是适配器名称
}
pAdapterInfo = pAdapterInfo->Next;
}while(pAdapterInfo);
}
delete pAdapterInfoBkp;
/
在Vs2005下的实现如下
ULONG ulAdapterInfoSize = sizeof(IP_ADAPTER_INFO);
IP_ADAPTER_INFO *pAdapterInfoBkp, *pAdapterInfo = (IP_ADAPTER_INFO*)new char[ulAdapterInfoSize];
if( GetAdaptersInfo(pAdapterInfo, &ulAdapterInfoSize) == ERROR_BUFFER_OVERFLOW ) // 缓冲区不够大
{
delete pAdapterInfo;
pAdapterInfo = (IP_ADAPTER_INFO*)new char[ulAdapterInfoSize];
pAdapterInfoBkp = pAdapterInfo;
}
if( GetAdaptersInfo(pAdapterInfo, &ulAdapterInfoSize) == ERROR_SUCCESS )
{
do{ // 遍历所有适配器
if(pAdapterInfo->Type == MIB_IF_TYPE_ETHERNET) // 判断是否为以太网接口
{
CString tmp(pAdapterInfo->Description) ;// 是适配器描述
if(tmp.Find(L”JLHW”)!=-1 )
{
::AfxMessageBox(tmp);
CString AdapterName(pAdapterInfo->AdapterName);
RegSetIP(AdapterName,L”192.168.0.101″,L”255.255.255.0″,L”192.168.0.1″);
NotifyIPChange(AdapterName,0,L”192.168.0.101″,L”255.255.255.0″);
}
// pAdapterInfo->AdapterName 是适配器名称
}
pAdapterInfo = pAdapterInfo->Next;
}while(pAdapterInfo);
}
delete pAdapterInfoBkp;
//
1.2 读取注册表取得适配器名称
在Windows2000中可以通过遍历 HKEY_LOCAL_MACHINE/System/CurrentControlSet/Control/Class/{4d36e972-e325-11ce-bfc1-08002be10318}/000n/
(n是从0开始编号的数字)所有接口, 在Windows NT中可以读取HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows
NT/CurrentVersion/NetworkCards中的信息,下面以Windows2000为例:
HKEY hKey, hSubKey, hNdiIntKey;
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
“System//CurrentControlSet//Control//Class//{4d36e972-e325-11ce-bfc1-08002be10318}”,
0,
KEY_READ,
&hKey) != ERROR_SUCCESS)
return FALSE;
DWORD dwIndex = 0;
DWORD dwBufSize = 256;
DWORD dwDataType;
char szSubKey[256];
unsigned char szData[256];
while(RegEnumKeyEx(hKey, dwIndex++, szSubKey, &dwBufSize, NULL,
NULL, NULL, NULL) == ERROR_SUCCESS)
{
if(RegOpenKeyEx(hKey, szSubKey, 0, KEY_READ, &hSubKey) == ERROR_SUCCESS)
{
if(RegOpenKeyEx(hSubKey, “Ndi//Interfaces”, 0, KEY_READ, &hNdiIntKey)
== ERROR_SUCCESS)
{
dwBufSize = 256;
if(RegQueryValueEx(hNdiIntKey, “LowerRange”, 0, &dwDataType, szData,
&dwBufSize) == ERROR_SUCCESS)
{
if(strcmp((char*)szData, “ethernet”) == 0) // 判断是不是以太网卡
{
dwBufSize = 256;
if(RegQueryValueEx(hSubKey, “DriverDesc”, 0, &dwDataType, szData,
&dwBufSize) == ERROR_SUCCESS)
{
// szData 中便是适配器详细描述
dwBufSize = 256;
if(RegQueryValueEx(hSubKey, “NetCfgInstanceID”, 0, &dwDataType,
szData, &dwBufSize) == ERROR_SUCCESS)
{
// szData 中便是适配器名称
}
}
}
}
RegCloseKey(hNdiIntKey);
}
RegCloseKey(hSubKey);
}
dwBufSize = 256;
} /* end of while */
RegCloseKey(hKey);
2、将IP信息写入注册表
代码如下:
BOOL RegSetIP(LPCTSTR lpszAdapterName, LPCTSTR pIPAddress,
LPCTSTR pNetMask, LPCTSTR pNetGate)
{
HKEY hKey;
string strKeyName = “SYSTEM//CurrentControlSet//Services//Tcpip//Parameters//Interfaces//”;
strKeyName += lpszAdapterName;
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
strKeyName.c_str(),
0,
KEY_WRITE,
&hKey) != ERROR_SUCCESS)
return FALSE;
char mszIPAddress[100];
char mszNetMask[100];
char mszNetGate[100];
strncpy(mszIPAddress, pIPAddress, 98);
strncpy(mszNetMask, pNetMask, 98);
strncpy(mszNetGate, pNetGate, 98);
int nIP, nMask, nGate;
nIP = strlen(mszIPAddress);
nMask = strlen(mszNetMask);
nGate = strlen(mszNetGate);
*(mszIPAddress + nIP + 1) = 0x00; // REG_MULTI_SZ数据需要在后面再加个0
nIP += 2;
*(mszNetMask + nMask + 1) = 0x00;
nMask += 2;
*(mszNetGate + nGate + 1) = 0x00;
nGate += 2;
RegSetValueEx(hKey, “IPAddress”, 0, REG_MULTI_SZ, (unsigned char*)mszIPAddress,
nIP);
RegSetValueEx(hKey, “SubnetMask”, 0, REG_MULTI_SZ, (unsigned char*)mszNetMask,
nMask);
RegSetValueEx(hKey, “DefaultGateway”, 0, REG_MULTI_SZ, (unsigned char*)mszNetGate,
nGate);
RegCloseKey(hKey);
return TRUE;
}
//
Vs005下实现
BOOL CTestIPDlg::RegSetIP(LPCTSTR lpszAdapterName, LPCTSTR pIPAddress, LPCTSTR pNetMask, LPCTSTR pNetGate)
{
HKEY hKey;
CString strKeyName;
//= L”SYSTEM//CurrentControlSet//Services”;
// ”
//Tcpip//Parameters//Tcpip//
“;
strKeyName = CString(L”SYSTEM//CurrentControlSet//Services//”)+ lpszAdapterName;
strKeyName+=L”
//Parameters//Tcpip//
“;
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
strKeyName,
0,
KEY_WRITE,
&hKey) != ERROR_SUCCESS)
return FALSE;
CString mszIPAddress;
CString mszNetMask;
CString mszNetGate;
mszIPAddress.Format(L”%s”,pIPAddress);
mszNetMask.Format(L”%s”,pNetMask);
mszNetGate.Format(L”%s”,pNetGate);
int nIP, nMask, nGate;
nIP = mszIPAddress.GetLength();
nMask = mszNetMask.GetLength();
nGate = mszNetGate.GetLength();
wchar_t *tmp=mszIPAddress.LockBuffer();
RegSetValueEx(hKey, L”IPAddress”, 0, REG_MULTI_SZ, (const BYTE *)tmp, nIP*2+2);
mszIPAddress.UnlockBuffer();
tmp=mszNetMask.LockBuffer();
RegSetValueEx(hKey, L”SubnetMask”, 0, REG_MULTI_SZ, (const BYTE *)tmp, nMask*2+2);
mszNetMask.UnlockBuffer();
tmp=mszNetGate.LockBuffer();
RegSetValueEx(hKey, L”DefaultGateway”, 0, REG_MULTI_SZ, (const BYTE *)tmp, nGate*2+2);
mszNetGate.UnlockBuffer();
RegCloseKey(hKey);
return TRUE;
}
///
3、调用DhcpNotifyConfigChange通知配置的改变
未公开函数DhcpNotifyConfigChange位于 dhcpcsvc.dll中,原型如下:
BOOL
DhcpNotifyConfigChange(
LPWSTR lpwszServerName, // 本地机器为NULL
LPWSTR lpwszAdapterName, // 适配器名称
BOOL bNewIpAddress, // TRUE表示更改IP
DWORD dwIpIndex, // 指明第几个IP地址,如果只有该接口只有一个IP地址则为0
DWORD dwIpAddress, // IP地址
DWORD dwSubNetMask, // 子网掩码
int nDhcpAction ); // 对DHCP的操作 0:不修改, 1:启用 DHCP,2:禁用 DHCP
具体调用代码如下:
BOOL NotifyIPChange(LPCTSTR lpszAdapterName, int nIndex,
LPCTSTR pIPAddress, LPCTSTR pNetMask)
{
BOOL bResult = FALSE;
HINSTANCE hDhcpDll;
DHCPNOTIFYPROC pDhcpNotifyProc;
WCHAR wcAdapterName[256];
MultiByteToWideChar(CP_ACP, 0, lpszAdapterName, -1, wcAdapterName,256);
if((hDhcpDll = LoadLibrary(“dhcpcsvc”)) == NULL)
return FALSE;
if((pDhcpNotifyProc = (DHCPNOTIFYPROC)GetProcAddress(hDhcpDll, “DhcpNotifyConfigChange”))
!= NULL)
if((pDhcpNotifyProc)(NULL, wcAdapterName, TRUE, nIndex, inet_addr(pIPAddress),
inet_addr(pNetMask), 0) == ERROR_SUCCESS)
bResult = TRUE;
FreeLibrary(hDhcpDll);
return bResult;
}
/
VS2005下实现:
char *w2c(char *pcstr,const wchar_t *pwstr, size_t len)
{
int nlength=wcslen(pwstr);
//获取转换后的长度
int nbytes = WideCharToMultiByte( 0, // specify the code page used to perform the conversion
0, // no special flags to handle unmapped characters
pwstr, // wide character string to convert
nlength, // the number of wide characters in that string
NULL, // no output buffer given, we just want to know how long it needs to be
0,
NULL, // no replacement character given
NULL ); // we don’t want to know if a character didn’t make it through the translation
// make sure the buffer is big enough for this, making it larger if necessary
if(nbytes>len) nbytes=len;
// 通过以上得到的结果,转换unicode 字符为ascii 字符
WideCharToMultiByte( 0, // specify the code page used to perform the conversion
0, // no special flags to handle unmapped characters
pwstr, // wide character string to convert
nlength, // the number of wide characters in that string
pcstr, // put the output ascii characters at the end of the buffer
nbytes, // there is at least this much space there
NULL, // no replacement character given
NULL );
return pcstr ;
}
BOOL CTestIPDlg::NotifyIPChange(LPCTSTR lpszAdapterName, int nIndex, LPCTSTR pIPAddress, LPCTSTR pNetMask)
{
BOOL bResult = FALSE;
HINSTANCE hDhcpDll;
DHCPNOTIFYPROC pDhcpNotifyProc;
WCHAR wcAdapterName[256];
char tmppIPAddress[100];
char tmppNetMask[100];
w2c(tmppNetMask,(LPWSTR)pNetMask,wcslen(pNetMask));
w2c(tmppIPAddress,(LPWSTR)pIPAddress,wcslen(pIPAddress));
tmppNetMask[wcslen(pNetMask)]=0;
tmppIPAddress[wcslen(pIPAddress)]=0;
if((hDhcpDll = LoadLibrary(L”dhcpcsvc”)) == NULL)
return FALSE;
if((pDhcpNotifyProc = (DHCPNOTIFYPROC)GetProcAddress(hDhcpDll, “DhcpNotifyConfigChange”)) != NULL)
if((pDhcpNotifyProc)(NULL, (LPWSTR)lpszAdapterName, TRUE, nIndex, inet_addr(tmppIPAddress), inet_addr(tmppNetMask), 0) == ERROR_SUCCESS)
{
DWORD ttt=GetLastError();
bResult = TRUE;
}
FreeLibrary(hDhcpDll);
return bResult;
}
/
二、修改注册表:网卡重启
更改Windows网卡属性选项中IP地址, 通过对比前后注册表, 可以发现以下几处发生变化
>[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services
/Tcpip/Parameters/Interfaces/{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}]
“IPAddress”
“SubnetMask”
“DefaultGateway”
“NameServer”
[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}/Parameters/Tcpip]
“IPAddress”
“SubnetMask”
“DefaultGateway”
[HKEY_LOCAL_MACHINE/SYSTEM/ControlSet001/Services/Tcpip/Parameters/Interfaces/{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}]
“IPAddress”
“SubnetMask”
“DefaultGateway”
“NameServer”
[HKEY_LOCAL_MACHINE/SYSTEM/ControlSet001/Services/{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}/Parameters/Tcpip]
“IPAddress”
“SubnetMask”
“DefaultGateway”
其中{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}是网卡名称(AdapterName), 不同的网卡, 不同的接入位置,
不同的接入的时间, 对应的值都不一样, 它的值是第一次接入系统时, 由系统生成的GUID值.
此处CurrentControlSet实际是ControlSet001的别名.
>[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/
Tcpip/Parameters/Interfaces/{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}]
“IPAddress”
“SubnetMask”
“DefaultGateway”
“NameServer”
是主要的设置处.
>[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}/Parameters/Tcpip]
“IPAddress”
“SubnetMask”
“DefaultGateway”
对一些服务有影响, 如不设置, 用netstat可以看到原来的IP地址仍处于监听状态(?).
但为了使设置生效, 还有很重要的一步, 即重启网卡.
更改网卡的配置, 一般而言需要重启网卡, 如Linux系统, 只需运行
#ifconfig eth0 down
#ifconfig eht0 up
就可以实现网卡的重启.
Windows环境下的步骤与之类似: 先禁用本地连接(网卡), 再启用本地连接(网卡). 但没有相应的命令或者直接的API. 所幸的是DDK提供一套设备安装函数,
用于控制系统设备, 包括控制设备的状态改变.(点击查看详细代码 href=”images/4.txt” target=”_blank”>附件)
总结: 通过网卡重启更改IP的方法有两个步骤: 修改注册表, 重启网卡. 重启网卡的全过程上面已作描述. 注册表修改的内容为文中列出四个主要项,
如{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}的网卡名称即是内部设备名, 在adapter结构中已给出.
整个注册表修改的过程比较简单, 本文不加叙述.
三、使用”iphlpapi”一卡多IP
除以上两个方法外, 笔者再介绍一种方法. 无论是在Windows下还是在Linux下, 一块网卡都可同时具有多个IP地址. 根据TCP/IP原理,
在网络层标识通信节点是IP地址, 在链路层上的则是MAC地址. 只要通过ARP, 将多个IP与一个MAC对应起来, 就可实现一网卡多IP(其实是一MAC多IP).
系统本身也有相应的设置选项, 如windows是通过TCP/IP属性的高级选项添加的, Linux下可由ifconfig命令添加.
iphlpapi提供AddIPAddress和DelIPAddress. 如果能先加入新的IP, 再去除原来的IP, 即可实现IP地址的更改.
1、获取适配器名称