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

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"的使用
————————————————
版权声明:本文为CSDN博主「笨笨D幸福」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/bbdxf/article/details/7548443

在程序中修改IP win7 winXP(参考1)的更多相关文章

  1. windows中修改IP映射的位置

    windows中修改IP映射的位置 置顶 2018年08月05日 14:42:44 wangxiaolong0 阅读数:1473   在安装linux之后,发现windows不能通过映射来访问linu ...

  2. 如何在Ubuntu server中修改IP

    详细请移步至博客https://blog.csdn.net/shenzhen_zsw/article/details/74025066 方法一. sudo  ifconfig eth0 100.100 ...

  3. 在Linux系统中修改IP地址

    在Linux系统中,通过编辑网络配置文件,设置系统IP地址,当然要在root权限下执行,具体步骤如下: 1.切换路径到/etc/sysconfig/network-scripts [root@Comp ...

  4. 在小程序中修改上一个页面里data中的数据调用上一个页面的方法

    //获取已经打开的页面的数组 var pages = getCurrentPages(); //获取上一个页面的所有的方法和data中的数据  var lastpage = pages[pages.l ...

  5. C程序之修改Windows的控制台颜色(转载)

    Windows的CMD可以和Linux下的终端一样可以有五颜六色,目前我在网上找到2种方法可以修改Windows的CMD,当然都是在代码中修改的.在“CMD”->“属性”->“颜色”,这种 ...

  6. Linux命令行修改IP、网关、DNS的方法

    Linux中在命令行中修改IP地址.网关.DNS的方法. 网卡eth0    IP修改为 102.168.0.1 复制代码代码如下: ifconfig eth0 102.168.0.1 netmask ...

  7. Ubuntu16.04修改IP及时生效

    1.Network Connetions 窗口管理器中修改IP 2.ifconfig查看网卡名字 3.刷新IP sudo ip addr flush enp2s0 4.sudo service net ...

  8. 如何限制修改IP地址;如何禁止显示的本地连接属性

    现在很多单位都配置了局域网,为了便于进行网络管理,同时为了提高的登录网络的速度,网管人员一般都为局域网中的每台电脑都指定了IP地址.但是在windows环境下其他用户很容易修改IP地址配置,这样就很容 ...

  9. 开发H5程序或者小程序的时候,后端Web API项目在IISExpress调试中使用IP地址,便于开发调试

    在我们开发开发H5程序或者小程序的时候,有时候需要基于内置浏览器或者微信开发者工具进行测试,这个时候可以采用默认的localhost进行访问后端接口,一般来说没什么问题,如果我们需要通过USB基座方式 ...

随机推荐

  1. etcd+https部署

    关闭防火墙 关闭selinux 下载所需的包(cfssl,生成证书工具) mkdir /usr/local/src/etcd/ cd /usr/local/src/etcd/ wget https:/ ...

  2. spring boot 开启https

    1.生成证书 keytool -genkey -alias tomcat -keyalg RSA -keystore E:/https.keystore 将生成好的证书放在项目根目录即可 2 修改配置 ...

  3. 【VS开发】COM组件技术概述

    这篇文章对COM做出来比较完整的解释,非常好. COM是微软公司为了计算机工业的软件生产更加符合人类的行为方式开发的一种新的软件开发技术.在COM构架下,人们可以开发出各种各样功能专一的组件,然后将它 ...

  4. 【前端知识体系-JS相关】ES6专题系列总结

    1.如何搭建ES6的webpack开发环境? 安装Node环境 node -v // 10.14.1 安装NPM环境 npm -v // 6.4.1 安装babel npm install @babe ...

  5. skeleton在心意web上的实践

    通过手动编写skeleton,在fetch数据时显示skeleton loading,数据拉取成功隐藏skeleton 先看下效果图 在component下创建页面对应的skeleton,然后通过在i ...

  6. gitea/gogs在push操作时报RPC failed的问题

    原文发布于:https://www.chenxublog.com/2019/05/26/gitea-gogs-push-rpc-failed.html 最近川普在搞出口管制,GitHub也更新了相应的 ...

  7. python 提取整个 HTML 节点

    有的时候,需要把整个 HTML 节点原封不动地取下来,也就是包括节点标签.节点内容,甚至也包括内容中的空格.各种特殊符号等等. 假设已获取到页面源码,并将其保存在变量 src 中.则可有代码如下: f ...

  8. Java学习——反射

    Java学习——反射 摘要:本文主要讲述了什么是反射,使用反射有什么好处,以及如何使用反射. 部分内容来自以下博客: https://www.cnblogs.com/tech-bird/p/35253 ...

  9. java的数据类型相关知识点

    总结就是八个字: 数据2型,四类八种 (个人理解,仅供参考) 解析图如下: 基本数据类型: 1.逻辑类:boolean 布尔类型,它比较特殊,布尔类型只允许存储true(真)或者false(假),不可 ...

  10. libtool编译

    1.充分利用共享库的能力.libtool 是一个通用库支持脚本 2.我们可以认为libtool是gcc的一个抽象,也就是说,它包装了gcc或者其他的任何编译器,用户无需知道细节,只要告诉libtool ...