Windows下不重啟機器程式修改IP的三種方法

來源:互聯網
上載者:User

Windows下程式修改IP的三種方法

    以下討論的平台依據是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" 

[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提供一套裝置安裝函數, 用於控制系統裝置, 包括控制裝置的狀態改變. 

/**************************************************************************************** 
 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"的使用.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.