在程序中修改IP win7 winXP(參考1)

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

相關文章
相關標籤/搜索