IP Helper 是一套用于管理本地网络设置的API(应用程序编程接口)它的功能十分强大,通过使用这一套API,你可以方便的改变计算机的网络设置或者提取有关的信息。而且它还提供了一种消息机制,能够在本地计算机的网络设置发生改变时通知你的应用程序。也就是说以前设置IP,掩码等等另人难以入眠的种种烦琐的工作现在都可以轻松搞定了。而且实际上,它不仅仅能够提取本机的网络设置信息,还能够获得网络上其它计算机的IP使用情况和MAC地址。这正是后面另一篇文章的例子程序中要使用的功能。
Windows 98以上的所有操作系统在系统目录的system32下都带有iphlpapi.dll这个库文件,而且对于NT 4.0在加装了Service Pack 2以后也就有了这个库文件。这只能说明我们的程序可以在这些系统上运行而不需要额外的库文件,但是对于C程序员来说还必须有相应的头文件等等东西。这个东东网上不太多,在华中理工和清华9#的FTP上有它的头文件,文件很小,只有几K(头文件而已,大不到哪儿去)。当然,你也可以问微软要,下载SDK或者是定购SDK光盘(这是我梦想的事情)。最后是对MSDN的一个勘误:MSDN所声称的静态链接库iphlpapi.lib是不存在的(至少我没有找到),还是老老实实的使用动态库吧。
最简单的当然是看看我们计算机上的网络设置是什么而不要动手修改。附表中列出了一个简单的网络设置查看程序,在这个程序中我分类使用了主要的API函数,为了交代问题而不让你陷入到MFC的汪洋大海中去,我把它做成了控制台界面的,而且做了详尽的注释,你可以把其中的代码剪切粘贴下来在任何地方使用。让我来解释一下这些代码:
hInst=LoadLibrary("iphlpapi.dll"); if(!hInst) cout<<"iphlpapi.dll not supported in this platform! ";
这三行代码的作用是加载iphlpapi库文件,在极个别的情况下,你使用了win95以下的操作系统或者不小心删除了iphlpapi.dll,第三行的语句才会被打印出来,这也是程序失败的唯一原因。
希望你还记得如何使用.dll。长久以来我们一直都在幸福的使用着VC的静态链接库,在没有静态链接库的情况下我们还有傻瓜化的控件。下面的几行代码显示了调用DLL中函数的过程:
pGAInfo=(PGAINFO)GetProcAddress(hInst,"GetAdaptersInfo"); ULONG ulSize=0; pGAInfo(pInfo,&ulSize); pInfo=(PIP_ADAPTER_INFO)new(char[ulSize]); pGAInfo(pInfo,&ulSize);
第一行代码是获得函数GetAdaptersInfo的入口地址,以便我们在后面通过指针调用函数。这个函数的功能是提取网卡的信息,并接收两个参数,第一个参数是用来保存网卡信息的内存缓冲的首地址,而第二个参数是这个缓冲的大小。但上面的代码看起来有些奇怪对么?由于我们事先不知道本地机器上有多少张网卡,所以也就没法知道应该分配多大的缓存。好在GetAdaptersInfo函数在缓冲的大小不够时会在第二个参数也就是ulSize中填入应该分配的缓冲的大小。这样,我们就可以调用两次GetAdaptersInfo,第一次是获取缓冲区的大小,然后分配这个缓冲以后再次调用它以获得实际的网卡信息。
令人不解的是,GetAdaptersInfo通过pInfo返回的信息竟然是以静态链表的方式组织的,下面就是访问链表的代码:
while(pInfo) { 。。。。。。。。。。。。。。。。。。。。//访问网卡数据 //将当前指针移向下一个结点 pInfo=pInfo->Next; }
为了说明问题,我再一次省略了其中的代码。下面看看GetAdaptersInfo都返回了些什么样的信息,下面是对pInfo所指向的数据结构的解释:
typedef struct _IP_ADAPTER_INFO { struct _IP_ADAPTER_INFO* Next; //链表指针域,我们通过这个来遍历静态键表 DWORD ComboIndex;//保留未用 char AdapterName[MAX_ADAPTER_NAME_LENGTH + 4]; //网卡名 char Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4]; //对网卡的描述,实际上好象是//驱动程序的名字 UINT AddressLength; //物理地址的长度,通过这个我们才能正确的显示下面数组中的物理地 //址 BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH]; //物理地址,每个字节存放一个十六进制的数 //值,我们配合上一个数据域在printf中用/x格式把每个字节输出。 DWORD Index;//网卡索引号 UINT Type;//网卡类型 UINT DhcpEnabled;//是否启用了DHCP动态IP分配? PIP_ADDR_STRING CurrentIpAddress;//当前使用的IP地址 IP_ADDR_STRING IpAddressList; //绑定到此网卡的IP地址链表,重要项目 IP_ADDR_STRING GatewayList; //网关地址链表,重要项目 IP_ADDR_STRING DhcpServer; //DHCP服务器地址,只有在DhcpEnabled==TRUE的情况下才有 //效 BOOL HaveWins;//是否启用了WINS? IP_ADDR_STRING PrimaryWinsServer; //主WINS地址 IP_ADDR_STRING SecondaryWinsServer; //辅WINS地址 time_t LeaseObtained; //当前DHCP租借获取的时间 time_t LeaseExpires; //当前DHCP租借失效时间。这两个数据结构只有在启用了DHCP时才 //有用。 } IP_ADAPTER_INFO, *PIP_ADAPTER_INFO;
现在应该可以看懂这个程序了吧。还必须告诉你的是这个数据结构中的几个IP地址字符串IpAddressList、GatewayList等等都是以链表的方式组织的(微软偏好链表?)。这就是为什么我的程序里面充满了循环。实际上你看到的所有数据都可以在控制面板|网络|属性|TCP/IP的属性页的高级选项里看见。