https://blog.csdn.net/bbdxf/article/details/7548443windows
Windows下程序修改IP的三種方法api
如下討論的平臺依據是Window XP + SP1, 不考慮Windows其它版本的兼容性問題, 但對NT系列的系統, 理論上是通用的. 網絡
方法一: 網卡重啓
更改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" ui
[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}\Parameters\Tcpip]
"IPAddress"
"SubnetMask"
"DefaultGateway" this
其中{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"
是主要的設置處. spa
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}\Parameters\Tcpip]
"IPAddress"
"SubnetMask"
"DefaultGateway"
對一些服務有影響, 如不設置, 用netstat能夠看到原來的IP地址仍處於監聽狀態(?). .net
但爲了使設置生效, 還有很重要的一步, 即重啓網卡. 命令行
更改網卡的配置, 通常而言須要重啓網卡, 如
Linux系統, 只需運行
#ifconfig eth0 down
#ifconfig eht0 up
就能夠實現網卡的重啓.
Windows環境下的步驟與之相似: 先禁用本地鏈接(網卡), 再啓用本地鏈接(網卡). 但沒有相應的命令或者直接的API. 所幸的是DDK提供一套設備安裝函數, 用於控制系統設備, 包括控制設備的狀態改變.
/****************************************************************************************
Purpose: change state of the selected device
Input : hDevInfo device info set
pDeviceInfoData selected device info
NewState one of enable/disable
Output : TRUE for success, FALSE for failed
****************************************************************************************/
BOOL ChangeDeviceState(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDeviceInfoData, DWORD NewState)
{
SP_PROPCHANGE_PARAMS PropChangeParams = {sizeof(SP_CLASSINSTALL_HEADER)};
SP_DEVINSTALL_PARAMS devParams;
if (!pDeviceInfoData) {
return FALSE;
}
PropChangeParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
PropChangeParams.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
PropChangeParams.Scope = DICS_FLAG_CONFIGSPECIFIC;
PropChangeParams.StateChange = NewState;
PropChangeParams.HwProfile = 0;
if (!SetupDiSetClassInstallParams(hDevInfo,pDeviceInfoData,
(SP_CLASSINSTALL_HEADER *)&PropChangeParams,sizeof(PropChangeParams))
|| !SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,hDevInfo,pDeviceInfoData)) {
return FALSE;
}
reutrn TRUE;
}
/* hDevInfo如何獲得***********************************************************/
m_hDevInfo = SetupDiGetClassDevs(
(LPGUID) &GUID_DEVCLASS_NET, /* GUID_DEVCLASS_NET表示僅列出網絡設備 */
NULL,
this->m_hWnd,
DIGCF_PRESENT);
if (INVALID_HANDLE_VALUE == m_hDevInfo) {
return FALSE;
}
/* pDeviceInfoData如何獲得**************************************************/
k = 0;
while (SetupDiEnumDeviceInfo(m_hDevInfo, k ,&DeviceInfoData)) {
k++;
if (CR_SUCCESS != CM_Get_DevNode_Status(&Status, &Problem,
DeviceInfoData.DevInst,0)) {
continue;
}
if ((Status & DN_NO_SHOW_IN_DM)) {
continue;
}
if (GetRegistryProperty(m_hDevInfo,
&DeviceInfoData,
SPDRP_FRIENDLYNAME,
&pBuffer,
&Length)) {
m_Adapter[adapter_num].index = k - 1; /* 當前網卡在設備信息集中的索引 */
_tcscpy(m_Adapter[adapter_num].desc, pBuffer); /* 當前網卡 */
GetRegistryProperty(m_hDevInfo,
&DeviceInfoData,
SPDRP_DRIVER,
&pBuffer,
&Length);
_tcscpy(m_Adapter[adapter_num].driver, pBuffer);
adapter_num++;
}
}
/* GetRegistryProperty是對SetupDiGetDeviceRegistryProperty封裝***************/
BOOL GetRegistryProperty(HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
ULONG Property,
LPTSTR* Buffer,
PULONG Length)
{
while (!SetupDiGetDeviceRegistryProperty(
DeviceInfoSet,
DeviceInfoData,
Property,
NULL,
(PBYTE)(*Buffer),
*Length,
Length
))
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
if (*(LPTSTR *)Buffer)
LocalFree(*(LPTSTR *)Buffer);
*Buffer = (LPTSTR)LocalAlloc(LPTR, *Length);
}else {
return FALSE;
}
}
return TRUE;
}
/* m_Adapter的數據結構 */
typedef struct adapter_info_s {
char name[NAME_LEN]; /* 內部設備名, UUID的字符串形式 */
char desc[NAME_LEN]; /* 網卡描述 */
char driver[NAME_LEN]; /* 網卡在註冊表中的位置, 如{4D36E972-E325-11CE-BFC1-08002BE10318}\0011
實際完整的鍵名爲System\\CurrentControlSet\\Control\\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0011
該鍵包含的內容與SetupDiGetDeviceRegistryProperty獲得的設備屬性基本相同
如NetCfgInstanceId即爲內部設備名 DriverDesc爲設備描述 */
int index;
}adapter_info_t;
/*****************************************************************************
用何名稱區分不一樣的網卡
有以下名稱可供選擇
本地鏈接名, 這是系統使用的方法, 調用的是netman.dll中的未公開函數HrLanConnectionNameFromGuidOrPath(其原型筆者正在調試之中, 成功以後會另行撰文); 其實也可從註冊表中得到HKLM\System\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}\Connection\Name
網卡類型描述
設備友好名 它與網卡類型描述基本相同, 當存在同種類型的網卡, 它會加#n(n = 2, 3, 4...)以示區分
如本程序中筆者即以設備友好名區分網卡
*****************************************************************************/
/* 重啓網卡的過程************************************************************/
k = pAdapter->GetCurSel(); /* m_Adapter[k]即當前網卡 */
if (SetupDiEnumDeviceInfo(m_hDevInfo, m_Adapter[k].index ,&DeviceInfoData))
{
hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
ChangeDeviceState(m_hDevInfo, &DeviceInfoData, DICS_DISABLE); /* 禁用網卡 */
ChangeDeviceState(m_hDevInfo, &DeviceInfoData, DICS_ENABLE); /* 啓用網卡 */
/* 重啓網卡, 通常狀況下, 如下命令至關於前兩命令的組合. 但我仍建議使用前者 */
// ChangeDeviceState(m_hDevInfo, &DeviceInfoData, DICS_PROPCHANGE);
SetCursor(hCursor);
}
/* 掃尾工做 */
SetupDiDestroyDeviceInfoList(m_hDevInfo);
總結: 經過網卡重啓更改IP的方法有兩個步驟: 修改註冊表, 重啓網卡. 重啓網卡的全過程上面已做描述. 註冊表修改的內容爲文中列出四個主要項, 如{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}的網卡名稱便是內部設備名, 在adapter結構中已給出. 整個註冊表修改的過程比較簡單, 本文不加敘述.
方法二:未公開函數
Windows系統中, 更改Windows網卡屬性選項中IP地址, 能夠即時使更改生效, 而且沒有重啓網卡的過程. 系統自帶的netsh也能經過命令行或腳本文件的形式, 完成更改IP的功能時, 也不須要重啓網卡
同時也有不少共享軟件, 能夠實現一樣的功能, 常見IP地址更改軟件有IPFreeSet, IPChanger, IPProfile, IPHelp, IPSet, SNet等.
筆者經過分析netsh發現一個未公開函數, 即用netcfgx.dll封裝的dhcpcsvc.dll中DhcpNotifyConfigChange函數
具體的方法參見VCKB 25期 王駿先生的 "不重起Windows直接更改IP地址", 他獲得的函數原型比我準確, 思路也很清晰.
分析上述共享軟件時, 發現其技術要點不外乎三種: 使用未公開函數, 調用netsh命令, 重啓網卡硬件. 調用netsh命令的實質仍是使用未公開函數
使用未公開函數的有: IPFreeSet, IPChanger
調用netsh命令的有 : IPHelp, IPSet. 二者都是用Delphi開發的.
重啓網卡硬件: IPSwitcher
速度比較: 由於netsh自己的實現是調用netcfgx.dll, netcfgx.dll封裝了對未公開函數的使用, 故效率相對較低. 在一臺CPU:PIII500/RAM:256/XP的系統中, IPHelp須要6~7秒才能完成, 而IPFreeSet只須要1~2秒.
方法三:一卡多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地址的更改.
具體內容參見我下篇文章"iphlpapi"的使用
————————————————
版權聲明:本文爲CSDN博主「笨笨D幸福」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/bbdxf/article/details/7548443