如何使用Windows API獲取IP、MAC

這裏用的兩個函數:gethostbyname/gethostbyaddr,GetAdaptersInfo,這裏主要通過獲取IP看鏈表的操作

獲取所有IP地址的函數,如下:

int CIPInfo::GetIPInfo(const char * szHostName)
{
#define HOST_NAME_MAX 256
	char szHost[HOST_NAME_MAX];
	HOSTENT* pHost;
	//LPSTR szIPAddr = NULL;
	char* szIPAddr = NULL;
	in_addr addr;
	memset(szHost, 0, ARRSIZE(szHost));

	if (szHostName)
	{
		if (isalpha(szHostName[0]))  /* host address is a name */
		{     
#ifdef _DEBUG
			TRACE("Calling gethostbyname with %s\n", szHostName);
#endif // _DEBUG
			pHost = gethostbyname(szHostName);
		} 
		else if (isdigit(szHostName[0])) /* host address is a digit */
		{
#ifdef _DEBUG
			TRACE("Calling gethostbyaddr with %s\n", szHostName);
#endif // _DEBUG
			
			addr.s_addr = inet_addr(szHostName);
			if (addr.s_addr == INADDR_NONE) 
			{
#ifdef _DEBUG
				TRACE("The IPv4 address entered must be a legal address\n");
#endif // _DEBUG			
				return -1;
			} 
			else
			{
				pHost = gethostbyaddr((char *) &addr, 4, AF_INET);
			}
		}
		else
		{
			TRACE("The IPv4 address entered Error!\n");
			return -1;
		}
	}
	else
	{
		::gethostname(szHost, HOST_NAME_MAX);
		pHost = gethostbyname(szHost);
	}

	if (pHost->h_addrtype == AF_INET) 
	{
		int i = 0;
		//char *p = NULL;
		while ( (szIPAddr = pHost->h_addr_list[i]) != 0) //Addresses are in network byte order
		{
			memcpy(&addr.s_addr, szIPAddr, pHost->h_length);
			szIPAddr = ::inet_ntoa(addr);
#ifdef _DEBUG
			TRACE("\tIPv4 Address #%d: %s\n", i, szIPAddr);
#endif // _DEBUG			
			
			if (szIPAddr)
			{
				PIPInfo ipInfo = GetIPObj(szIPAddr);
				if (ipInfo)
				{
					InsertIPObj(*ipInfo);
				}				
			}
			i++;
		}
	} 
	else if (pHost->h_addrtype == AF_INET6)
	{
#ifdef _DEBUG
		TRACE("\tRemotehost is an IPv6 address\n");
#endif // _DEBUG
		return -1;
	}
	return 0;
}
注意:

1)這裏我將IP6排除在外了,只使用了IP4

 2)還有一個就是這裏面其實牽涉到Unicode和ANSI字符集I編碼的問題,這裏並沒有嚴格遵守此規則

3)這裏使用了ip地址和域名的方式,這裏並沒有涉及,因爲此函數有個默認形參

  在使用HOSTENT獲得的結構中,包含本機ip地址的列表,就是h_addr_list,這裏我將其保存在瞭如下的一個結構鏈表中

typedef struct IPInfo
{	
	char	ip[IP_ADDR_SIZE];
	IPInfo*	next;
}IPInfo, *PIPInfo;
這裏就需要牽涉堆內存空間的開闢和釋放,鏈表的插入和刪除,這裏我是在類中寫入的函數,所以我將釋放放置於析構函數中,

如下內存空間的分配

PIPInfo CIPInfo:: GetIPObj(char* szIP)
{
	PIPInfo pIP = (PIPInfo)malloc(sizeof(IPInfo));
	if (pIP)
	{
		memset(pIP->ip, 0, sizeof(pIP->ip));
		strcpy_s( pIP->ip, szIP);
		pIP->next = NULL;
	}
	return pIP;
}
先開闢空間,再填充數據,這裏爲了防止臨時變量空間的釋放,使用的是內容的複製

內存空間的釋放:如下

int CIPInfo::FreeIPObj(PIPInfo pIPObj)
{
	if (pIPObj)
	{
		free(pIPObj);
		pIPObj = NULL;
	}
	return 0;
}
釋放空間後,一定要將指針置爲NULL,至於爲什麼請參考:void * 與 空指針NULL 

鏈表的插入:如下

int CIPInfo::InsertIPObj(IPInfo& pIPObj)
{
	PIPInfo pTest = m_ipAddrList;
	if (m_ipAddrList == NULL)
	{
		m_ipAddrList = &pIPObj;
	}
	else
	{
		while ( pTest->next != NULL)
		{
			pTest = pTest->next;
		}

		pTest->next = &pIPObj;			
	}
	return 0;
}
這裏m_ipAddrList是類的成員變量,這裏鏈表沒有頭節點,也沒有頭指針或者尾指針,只有一個單向鏈表。鏈表爲空,直接插入;鏈表不爲空,插入尾部

鏈表的刪除:如下:

int CIPInfo::RemoveIPObj(PIPInfo pIPObj)
{
	PIPInfo pTest = m_ipAddrList;
	if (pIPObj == NULL)
	{
		return -1;
	}
	else if (pIPObj == pTest)
	{
		m_ipAddrList = (pTest->next) ? pTest->next : NULL;
	}
	else
	{
		while ( (pTest->next != NULL) &&
			    (pTest->next != pIPObj) )
		{
			pTest = pTest->next;
		}
		if (pTest != NULL)
		{
			pTest->next = (pIPObj->next) ? pIPObj->next : NULL;
		}
	}
	return 0;
}
這裏要考慮鏈表爲空的情況,通常移除的時候就會判斷,這裏只是保險起見,如果非空,要考慮刪除的是否是頭結點,是否是僅有一個結點,當時其他元素時,要考慮刪除的是否是尾結點,以及在中間的結點。
MAC地址的獲取不在闡述,如下:
int CIPInfo::GetMac(void)
{
	//PIP_ADAPTER_INFO pAdapterInfo = NULL;
	ULONG uLen = 0;

	/* 獲得內存空間大小,然後申請內存空間*/
	if (GetAdaptersInfo(pAdapterInfo, &uLen) == ERROR_BUFFER_OVERFLOW) 
	{
		pAdapterInfo = (PIP_ADAPTER_INFO)::GlobalAlloc(GPTR, uLen); //分配一塊內容,並初始化爲0
		if (pAdapterInfo == NULL) 
		{
			TRACE("Error allocating memory needed to call GetAdaptersinfo\n");
			return -1;
		}
	}

	/* 取得適配器信息*/
	if ( ::GetAdaptersInfo(pAdapterInfo, &uLen) == ERROR_SUCCESS )
	{
		if (pAdapterInfo)
		{
			memcpy(m_szLocalMac, pAdapterInfo->Address, MAC_ADDR_SIZE);
			strcpy_s(m_szLocalIP, pAdapterInfo->IpAddressList.IpAddress.String);
			strcpy_s(m_szMask, pAdapterInfo->IpAddressList.IpMask.String);
			strcpy_s(m_szGatewayIP, pAdapterInfo->GatewayList.IpAddress.String);
		}
	}
	return 0;
}
另外千萬不要忘了內存空間的釋放,因爲不釋放分配的空間,會導致內存泄露,如下析構函數
CIPInfo::~CIPInfo(void)
{
	PIPInfo pTest = m_ipAddrList;
	while (pTest)
	{
		RemoveIPObj(pTest);
		FreeIPObj(pTest);
		pTest = m_ipAddrList->next;
	}
	m_ipAddrList = NULL;

	if (pAdapterInfo)
		GlobalFree(pAdapterInfo);	
}

測試用例:這裏我使用了MFC的主窗口類,在一個編輯框中進行顯示,如下測試代碼:

void CSocketDlg::OnBnClickedOk()
{
	// TODO: 在此添加控件通知處理程序代碼
	CString strText = _T("");
	CString strTemp = _T("");	
	CIPInfo ipInfo;
	int nIndex = -1;

	nIndex = m_comboBox.GetCurSel();	
	m_comboBox.GetLBText(nIndex, strText);	
		
	if (strText == _T("主機IP地址"))
	{	
		strText += _T("\r\n");
		PIPInfo IPInfoList;
		int nResult = -1;

		nResult = ipInfo.GetIPInfo();
		if( !nResult )
		{
			IPInfoList = ipInfo.GetIPList();
			while ( IPInfoList && 
				    (strTemp = (CString)IPInfoList->ip))
			{
				strTemp += _T("\r\n");
				strText += strTemp;
				IPInfoList = IPInfoList->next;
			}
		}
	}
	if (strText == _T("主機MAC地址"))
	{
		strText += _T("\r\n");
		ipInfo.GetMac();
		strTemp.Format(_T("IP\t: %s\r\n"), (CString)ipInfo.GetLocalIP());
		strText += strTemp;
		strTemp.Format(_T("Mask\t: %s\r\n"), (CString)ipInfo.GetMask());
		strText += strTemp;
		strTemp.Format(_T("Gateway\t: %s\r\n"),(CString)ipInfo.GetGatewayIP());
		strText += strTemp;
		u_char* p = ipInfo.GetLocalMac();
		strTemp.Format(_T("MAC\t: %.2X-%.2X-%.2X-%.2X-%.2X-%.2X"),
			p[0],p[1],p[2],p[3],p[4],p[5]);
		strText += strTemp;	
	}
	SetDlgItemText(IDC_SHOW_TEXT, strText);

	//CDialogEx::OnOK();
}
測試IP的結果如下:

主機IP地址
192.168.1.3
192.168.50.1
192.168.159.1

測試MAC配置信息的結果如下:

主機MAC地址
IP                : 192.168.1.3
Mask          : 255.255.255.0
Gateway    : 192.168.1.1
MAC            : 9C-4E-36-17-AA-A4

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