zoukankan      html  css  js  c++  java
  • 在程序中修改IP win7 winXP(参考1)

    https://blog.csdn.net/bbdxf/article/details/7548443

    Windows下程序修改IP的三种方法

        以下讨论的平台依据是Window XP + SP1, 不考虑Windows其它版本的兼容性问题, 但对NT系列的系统, 理论上是通用的. 

    方法一: 网卡重启 
        更改Windows网卡属性选项中IP地址, 通过对比前后注册表, 可以发现以下几处发生变化 
    [HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesTcpipParametersInterfaces{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}] 
    "IPAddress" 
    "SubnetMask" 
    "DefaultGateway" 
    "NameServer" 

    [HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServices{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}ParametersTcpip] 
    "IPAddress" 
    "SubnetMask" 
    "DefaultGateway" 

    [HKEY_LOCAL_MACHINESYSTEMControlSet001ServicesTcpipParametersInterfaces{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}] 
    "IPAddress" 
    "SubnetMask" 
    "DefaultGateway" 
    "NameServer" 

    [HKEY_LOCAL_MACHINESYSTEMControlSet001Services{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}ParametersTcpip] 
    "IPAddress" 
    "SubnetMask" 
    "DefaultGateway" 

        其中{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}是网卡名称(AdapterName), 不同的网卡, 不同的接入位置, 不同的接入的时间, 对应的值都不一样, 它的值是第一次接入系统时, 由系统生成的GUID值. 
        此处CurrentControlSet实际是ControlSet001的别名.     
    [HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesTcpipParametersInterfaces{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}] 
    "IPAddress" 
    "SubnetMask" 
    "DefaultGateway" 
    "NameServer" 
        是主要的设置处. 

    [HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServices{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}ParametersTcpip] 
    "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}011  
        实际完整的键名为System\CurrentControlSet\Control\Class{4D36E972-E325-11CE-BFC1-08002BE10318}011  
        该键包含的内容与SetupDiGetDeviceRegistryProperty得到的设备属性基本相同 
        如NetCfgInstanceId即为内部设备名 DriverDesc为设备描述    */ 
        int index; 
    }adapter_info_t; 
         


    /***************************************************************************** 
     用何名称区分不同的网卡 
     有如下名称可供选择 
        本地连接名, 这是系统使用的方法, 调用的是netman.dll中的未公开函数HrLanConnectionNameFromGuidOrPath(其原型笔者正在调试之中, 成功之后会另行撰文); 其实也可从注册表中获得HKLMSystemCurrentControlSetControlNetwork{4D36E972-E325-11CE-BFC1-08002BE10318}{97EFDAD8-EB2D-4F40-9B07-0FCD706FCB6D}ConnectionName 
        网卡类型描述 
        设备友好名        它与网卡类型描述基本相同, 当存在同种类型的网卡, 它会加#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

  • 相关阅读:
    HDU 1800 Flying to the Mars 字典树,STL中的map ,哈希树
    字典树 HDU 1075 What Are You Talking About
    字典树 HDU 1251 统计难题
    最小生成树prim算法 POJ2031
    POJ 1287 Networking 最小生成树
    次小生成树 POJ 2728
    最短路N题Tram SPFA
    poj2236 并查集
    POJ 1611并查集
    Number Sequence
  • 原文地址:https://www.cnblogs.com/liuzhaoyzz/p/11703173.html
Copyright © 2011-2022 走看看