IP Helper API 簡介 (zz)

 IP Helper API 簡介 (zz)

IP Helper 是一套用於管理本地網絡設置的API(應用程序編程接口)它的功能十分強大,通過使用這一套API,你可以方便的改變計算機的網絡設置或者提取有關的信息。而且它還提供了一種消息機制,能夠在本地計算機的網絡設置發生改變時通知你的應用程序。也就是說以前設置IP,掩碼等等另人難以入眠的種種煩瑣的工作現在都可以輕鬆搞定了。而且實際上,它不僅僅能夠提取本機的網絡設置信息,還能夠獲得網絡上其它計算機的IP使用情況和MAC地址。這正是後面另一篇文章的例子程序中要使用的功能。

哪兒可以搞到IP Helper API?
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!/n";


這三行代碼的作用是加載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的屬性頁的高級選項裏看見


提取網絡接口信息
 
 
其中最主要的兩個函數是GetNumberOfInterfaces和GetInterfaceInfo,前者指出網絡接口的個數,後者提取網絡接口的信息。對於第一個函數要說明的一點是它好象並沒有返回正確的值,因爲據MSDN描述:一個網絡接口是網卡的邏輯抽象,它們是一對一的關係。而實際情況是我的機器上只有一張網卡,這個函數卻返回了2。實際上,因爲每個系統都附加有一個調試用的網絡接口,這個接口的IP地址是127.0.0.1子網掩碼是255.0.0.0。這個結果可以從程序的輸出看出來。由GetInterfaceInfo返回的IP_INTERFACE_INFO結構中也有一個NumAdapters整型的數據域記錄了正確的網卡。然後對於GetInterfaceInfo要注意的是它也必須被調用兩次,第一次獲取緩衝大小,第二次纔是取值。然後再次讓我感到其怪的是GetInterfaceInfo返回的IP_INTERFACE_INFO不象上面的結構是用鏈表,而是用的動態數組的方法(到現在什麼線性結構都用上了),所以遍歷其中每一個元素的代碼變成:


for(int i=0;iNumAdapters;i++)
{
  cout<<"Adapter index:"<Adapter[i].Index<Adapter[i].Name<
IP_INTERFACE_INFO結構的解釋如下:


typedef struct _IP_INTERFACE_INFO {
  LONG NumAdapters;                
// 動態數組中網絡接口元素的個數,通過它來遍歷數
//組
  IP_ADAPTER_INDEX_MAP Adapter[1]; 
// 網絡接口數據數組
} IP_INTERFACE_INFO,*PIP_INTERFACE_INFO;

其中的IP_ADAPTER_INDEX_MAP結構如下:


typedef struct _IP_ADAPTER_INDEX_MAP {
  ULONG Index;                    
// 網卡索引
  WCHAR Name[MAX_ADAPTER_NAME];   
// 網卡名
} IP_ADAPTER_INDEX_MAP, * PIP_ADAPTER_INDEX_MAP;

提取IP信息
這部分顯然和提取網絡接口信息部分是相同的。

設置本地網絡
設置的過程與提取過程其實是換湯不換藥。我不想在這裏演示每一個函數的用法,所以只使用了一個(我認爲)最常用的函數AddIPAddress。這個函數能夠設置本地網絡的IP。但不幸的是,這個IP是臨時的,當系統重新啓動或者發生其它的PNP事件的時候這個IP就不存在了。那麼有人會問這有什麼用呢?實際上,每個網絡接口卡都可以綁定多個IP,所以在網絡環境惡劣的情況下(如校園網)同時預備多個IP以防斷線是有必要的。必須指出的是這種臨時的IP在網絡通訊時可能導致的問題現在還沒有測試過,諸如CODEGURU,CSDN,MSDN,或者TECHREPUBLIC對這套API也沒有詳細的解釋(雖然它很有用)。

其它的API函數
這些函數能夠讓你察看或者設置網絡數據報文方面的信息。比方GetIpStatistics、GetIcmpStatistics函數能夠讓你查看當前IP數據報和ICMP數據報的流量,以及廢棄的數據報數量等等。使用這些函數你可以構建自己的網絡監控程序檢察網絡中的故障。你也可以使用SetIpStatistics 函數來設置相應的IP協議棧屬性,縮短或者延長IP數據報的缺省TTL值。然後你也可以使用GetIpForwardTable、CreateIpForwardEntry 、DeleteIpForwardEntry、SetIpForwardEntry來分別獲取IP路由表的信息,創建路由表項,刪除路由表項和修改路由表項。也可以用GetBestRoute、 GetBestInterface獲得到達指定IP的最好的路由點和網絡接口。

事實上,通過這些函數我們可以得到許多MIB變量(《使用TCP/IP協議實現網際互聯》第二卷),通過這些MIB變量,我們可以非常快速的製作一個網絡管理軟件。

未涉及的部分
我們沒有涉及IP Helper API中SendARP函數的使用。實際上,這個函數是我使用這套API的主要原因,它能夠簡單的發送ARP數據包並返回目標機器的MAC地址。我使用這個函數製作了另一個簡單的IP查看程序,它能夠查看局域網上哪些IP正在使用,並能夠顯示它們的MAC地址。如果把這些信息記錄在文件中,我就可以統計出一天中哪些傢伙的上網時間最長以及其上網的習性並能夠知道哪些人更換了他們的網卡,也可以分析出這個網絡的使用情況(我在學校的高峯時段統計出一個網段的254個IP中竟有236個正在使用,可見增加網段的必要了)。我將在後面的文章中說明這個程序的製作。我們也沒有涉及這套API提供的當網絡設置改變時嚮應用程序發出消息的異步通知功能。因爲它們非常簡單,與WINSOCK中WSAAsyncSelect的使用方法是一樣的,就不再說明了。

後話
討論如何使用API函數也許會被一批"純粹"的程序員所鄙視。但每次看到有人在論壇上急切的詢問如何取得本機網關,IP地址,路由之類的信息的時候,我就覺得非常有必要介紹這一套API函數,它能夠(至少在我看來)滿足我們的大部分要求

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章