概要

ingテスト及びmacアドレス・ホスト名・ベンダー名の取得(32/64bit)のGUI版です。
IIPアドレス範囲はダイアログボックスで指定できます。(ディフォルトは192.168.0.0~192.168.0.254)ping実行ボタンをクリックするとpingを実行し、応答があればホスト名及びmacアドレス等をリストビューに表示します。(IPv4専用)
表示内容は、応答があったIPアドレスとtimeはパケット送信から受信までの時間(ミリ秒)、TTLはIPパケットの生存時間、macアドレス、ホスト名、ベンダー名である。
ベンダー名の一覧は、IEEE-SAのホームページの下のほうのDownload a copyより本プログラムが自動的に取得し、macアドレスと一致するベンダーを表示します。

テスト環境

コンパイラ

Visual C++ 2008/2013 Express 32/64bit マルチバイト/UNICODE

実行環境

Windows XP Professional Service Pack 3 32bit(Virtual Box上の仮想マシーン)
Windows 7 Enterprise Service Pack 1 64bit

プログラムソースの概要

ping1.cpp

WinMain

ダイアログボックスを開きます。

DlgProc

WM_INITDIALOG
ダイアログボックスの初期化時に呼び出されます。
リストビューのヘッダ及びIPアドレスコントロール等の初期値を設定します。
WM_NOTIFY
メッセージの中からIPアドレスコントロールで値が変化した場合を抽出し、グローバル変数にその値を保存します。
WM_COMMAND
各ボタンがクリックされた場合に呼び出されます。
IDOK
ping実行ボタンがクリックされた場合に呼び出されます。
コントロールからIPアドレスを取得し、スレッドとしてping_thread関数を呼び出します。直接関数を呼び出すとメッセージが回らなくなりキャンセルボタンが有効になりません。
IDCANCEL
キャンセルボタンがクリックされた場合に呼び出されます。
クリティカルセッションを使い、ping_run_fをfalseにセットします。
ping関数がこの値をたまにチェックしており、falseの場合、関数を終了します。
IDC_QUIT
終了ボタンをクリックすると呼び出され、EndDialog APIにより本プログラムを終了させます。

ListViewHeader

リストビューのヘッダーを初期化します。

ping_thread

スレッド作成APIであるCreateThreadより呼び出され、ping関数を呼び出します。

ping

指定された範囲のIPについてpingを実行し、結果をリストビューに表示します。
ひとつのIPごとにクリティカルセッションを使い、ping_run_fをチェックしflaseがセットされていれば、関数を終了します。
カレントフォルダーにIEEE-SAサイトからダウンロードしたoui.txtファイルがあればこれを開きます。
ファイルがなければインターネットからダウンロードします。
get_mac_vendor関数によりファイルを読み取り、macアドレスとベンダー名の一覧(vendor_vec)を作成します。
IcmpCreateFile APIによりハンドルを作成します。
IPアドレスは、for文の中の配列ipで定義しています。(ビックエンディアンで設定)
IcmpSendEcho APIでpingを実行します。引数で送信するデータサイズやタイムアウトなどを指定できます。ここではマクロBUF_SIZEで32byteのデータサイズ及び2000ミリ秒をタイムアウト値としています。
APIが正常値を返した場合、SendARP APIによりIPアドレスからmacアドレスを取得します。
macアドレスよりmac2vendor_name関数によりベンダー名を取得します。
gethostbyaddr APIによりIPアドレスからホスト名を取得します。このAPI実行にはWINSOCKの初期化が必要であるため、WSAStartup APIをあらかじめ実行しておきます。
なぜかこのAPIはUNICODE版がないので、MultiByteToWideChar APIによりUNICODEに変換します。
for文のループから抜けたら、IcmpCloseHandle APIによりハンドルの開放、WSACleanup APIによりWINSOCKの開放を行います。

get_mac_vendor

ファイルよりmacアドレスとベンダー名の対応を一覧を作成します。
一覧はvendor_vecに保存されます。

mac2vendor_name

macアドレスよりベンダー名を取得します。該当するmacアドレスがない場合は文字列errorを返します。

ソースコード

guiping.cpp

//	指定した範囲のIPアドレスにPINGを実行し、レスポンスがあればマックアドレスとベンダー名およびホスト名を表示
//	2014/07/21

#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <icmpapi.h>
#include <wininet.h>
#include <locale.h>
#include <tchar.h>
#include <vector>
#include <commctrl.h>
#include "resource.h"

#pragma comment(lib,"iphlpapi.lib")
#pragma comment(lib,"Ws2_32.lib")	//	gethostbyaddr関数に必要
#pragma comment(lib,"wininet.lib")
#pragma comment(lib,"comctl32.lib")

#define BUF_SIZE 32

HWND hLabel1=0;


struct VENDOR_ID{
	unsigned char mac[3];	//	macアドレス 上位3byte
	TCHAR* name;			//	ベンダー名
	void put(FILE* fp){
		_ftprintf(fp,_TEXT("%02x:%02x:%02x\t%s\n"),mac[0],mac[1],mac[2],name);
	}
	bool cmp(unsigned char* src_mac){
		if(mac[0]==src_mac[0] && mac[1]==src_mac[1] && mac[2]==src_mac[2])
			return true;
		else
			return false;
	}
};

using namespace std;

vector<VENDOR_ID> vendor_vec;	//	macアドレスとベンダー名の一覧

//	pingをスレッドで実行
DWORD WINAPI  ping_thread(LPVOID lParam);

//	pingを実行後、macアドレス・ホスト名を取得しリストビューに表示
bool ping(HWND hList,unsigned char* ipa,unsigned char end);

//	macアドレスよりベンダー名を取得する
TCHAR* mac2vendor_name(unsigned char* src_mac);

//	macアドレスとベンダー名の対応を一覧する
void vendor_fput(FILE* fp);

//	macアドレスとベンダー名の対応を一覧する
void vendor_fput(FILE* fp);

//	ファイルよりmacアドレスとベンダー名の対応を一覧を作成する
void get_mac_vendor(FILE* fp);

//	リストビューのヘッダーを表示
void ListViewHeader(HWND hList);

//	ダイアログボックスプロシージャー
LRESULT CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);


int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,LPSTR lpsCmdLine, int nCmdShow){
	InitCommonControls();

	DialogBox(hCurInst, TEXT("DLG1"), 0, (DLGPROC)DlgProc);

	return (int)0;
}


HWND hDlg=0;
HWND hList=0;
unsigned char ip[4];
unsigned char end;
CRITICAL_SECTION cs;
bool ping_run_f=false;	//	ping実行中にfalseをセットするとpingを終了する

//	pingをスレッドで実行

DWORD WINAPI  ping_thread(LPVOID lParam){
	ping(hList,ip,end);
	if(0<ListView_GetItemCount(hList)){
		for(int n=0;n<=5;n++){	//	リストビューの列幅を自動設定する
			SendMessage(hList,LVM_SETCOLUMNWIDTH,n,LVSCW_AUTOSIZE_USEHEADER);
		}
	}
	EnableWindow(GetDlgItem(::hDlg,IDOK),TRUE);
	EnableWindow(GetDlgItem(::hDlg,IDCANCEL),FALSE);
	return 0;
}

//	ダイアログボックスプロシージャー

LRESULT CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){
	static HWND hIPA;
	static HWND hEdit;
	static DWORD tid;
	switch (msg) {
		case WM_INITDIALOG:{
			::hDlg=hDlg;
			hList=GetDlgItem(hDlg,IDC_LISTVIEW1);
			hIPA=GetDlgItem(hDlg,IDC_IPADDRE);
			hEdit=GetDlgItem(hDlg,IDC_EDIT1);
			hLabel1=GetDlgItem(hDlg,IDC_LABEL1);

			ListViewHeader(hList);
			ip[0]=192;
			ip[1]=168;
			ip[2]=0;
			ip[3]=0;
			LPARAM ipadd=MAKEIPADDRESS(ip[0],ip[1],ip[2],ip[3]);
			SendMessage(hIPA, IPM_SETADDRESS, 0, ipadd);
			SetWindowText(hEdit,_TEXT("254"));
			EnableWindow(GetDlgItem(hDlg,IDCANCEL),FALSE);
			InitializeCriticalSection(&cs);
			return TRUE;
		}
		case WM_NOTIFY:
			if( ((LPNMHDR)lParam)->hwndFrom == hIPA){	//	IPコントロールの変更を検出する
				LPNMIPADDRESS lpnmipa=(LPNMIPADDRESS)lParam;
				switch(lpnmipa->hdr.code){
					case IPN_FIELDCHANGED:
						ip[lpnmipa->iField]=lpnmipa->iValue;
						return TRUE;
					default:
						return FALSE;
				}
			}
			break;
        case WM_COMMAND:
            switch (LOWORD(wParam)) {
				case IDOK:{	//	ping実行
					EnableWindow(GetDlgItem(hDlg,IDOK),FALSE);
					EnableWindow(GetDlgItem(hDlg,IDCANCEL),TRUE);
					TCHAR buf[8];
					GetWindowText(hEdit,buf,sizeof(buf)/sizeof(TCHAR));
					end=_ttoi(buf);
					int minv=min(ip[3],end);
					int maxv=max(ip[3],end);
					ip[3]=minv;
					end=maxv;
					ping_run_f=true;
					CreateThread(NULL,0,ping_thread,0,0,&tid);	//	pingをスレッドとして実行
					return FALSE;
				}
				case IDCANCEL:	//	pingキャンセル
					EnterCriticalSection(&cs);
					ping_run_f=false;
					LeaveCriticalSection(&cs);
					return FALSE;
				case IDC_QUIT:	//	ダイアログボックス終了
					EndDialog(hDlg,TRUE);
					return TRUE;
				default:
					return FALSE;

			}
			default:
				return FALSE;
	}
	return TRUE;
}

//	リストビューのヘッダーを表示

void ListViewHeader(HWND hList){
	LV_COLUMN lvcol;
	lvcol.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
	lvcol.fmt = LVCFMT_LEFT;
	lvcol.cx = 100;
	lvcol.pszText = _TEXT("IP");
	lvcol.iSubItem = 0;
	ListView_InsertColumn(hList, 0, &lvcol);

	lvcol.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM ;
	lvcol.fmt = LVCFMT_RIGHT;
	lvcol.cx = 48;
	lvcol.pszText = _TEXT("Time");
	lvcol.iSubItem = 1;
	ListView_InsertColumn(hList, 1, &lvcol);

	lvcol.cx = 48;
	lvcol.fmt = LVCFMT_RIGHT;
	lvcol.pszText = _TEXT("TTL");
	lvcol.iSubItem = 2;
	ListView_InsertColumn(hList, 2, &lvcol);

	lvcol.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM ;
	lvcol.cx = 100;
	lvcol.pszText = _TEXT("MAC");
	lvcol.iSubItem = 3;
	ListView_InsertColumn(hList, 3, &lvcol);

	lvcol.cx = 150;
	lvcol.pszText = _TEXT("Vendor");
	lvcol.iSubItem = 4;
	ListView_InsertColumn(hList, 4, &lvcol);

	lvcol.cx = 150;
	lvcol.pszText = _TEXT("Name");
	lvcol.iSubItem = 5;
	ListView_InsertColumn(hList, 5, &lvcol);
}

//	pingを実行後、macアドレス・ホスト名を取得しリストビューに表示

bool ping(HWND hList,unsigned char* ipa,unsigned char end){
	TCHAR buf[128];
	LV_ITEM item;
	int num=0;
	bool f=true;
	FILE* fp;

	if(vendor_vec.size()==0){	//	ベンダー情報が読み込まれていないのでファイルまたはインターネットから取得
		//	ファイルが開けない場合、インターネットから取得しファイルに保存後、再度ファイルを開く
		if(_tfopen_s(&fp,_TEXT("oui.txt"),_TEXT("r"))){
			SetWindowText(hLabel1,_TEXT("インターネットからベンダー一覧表を取得します"));
			if(_tfopen_s(&fp,_TEXT("oui.txt"),_TEXT("wb"))){
				return false;
			}
			HINTERNET hInet, hUrl;
			//インターネット(WinInet)開始
			hInet = InternetOpen(_TEXT("xyz-123"),
				INTERNET_OPEN_TYPE_PRECONFIG,	
				NULL, NULL, 0);
			if (hInet == NULL) {
				return false;
			}

			//HTTPセッションの開始, 指定のURLオープン
			hUrl = InternetOpenUrl(hInet, _TEXT("http://standards.ieee.org/develop/regauth/oui/oui.txt"), NULL, 0, 0, 0);
			if (hUrl == NULL) {
				InternetCloseHandle(hInet);
				return false;
			}
			char szBuf[1024];
			DWORD dwRead = 0;
			unsigned read_sz=0;
			//読み出す物がなくなるまで読み出す
			while (1) {
				InternetReadFile(hUrl, szBuf, (DWORD)sizeof(szBuf), &dwRead); 

				//読み出す物がなくなったのでループ脱出
				if (dwRead == 0)
					break;
				EnterCriticalSection(&cs);
				if(ping_run_f==false){
					LeaveCriticalSection(&cs);
					SetWindowText(hLabel1,_TEXT("インターネットから読み込みがキャンセルされました。"));
					break;
				}
				LeaveCriticalSection(&cs);
				read_sz+=dwRead;
				_stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%dbyte読み込みました"),read_sz);
				SetWindowText(hLabel1,buf);
				fwrite(szBuf,1,dwRead,fp);
			}
			fclose(fp);
			if(ping_run_f==true){
				if(_tfopen_s(&fp,_TEXT("oui.txt"),_TEXT("r"))){
					return false;
				}
			}
			//インターネットハンドルの解放
			InternetCloseHandle(hUrl);
			InternetCloseHandle(hInet);
			EnterCriticalSection(&cs);
			if(ping_run_f==false){
				LeaveCriticalSection(&cs);
				_stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("pingがキャンセルされました"));
				SetWindowText(hLabel1,buf);
				return false;
			}
			LeaveCriticalSection(&cs);
		}
		get_mac_vendor(fp);
		fclose(fp);

#ifdef _DEBUG
		if(_tfopen_s(&fp,_TEXT("vendor.txt"),_TEXT("w"))){
			return false;
		}
		vendor_fput(fp);
		fclose(fp);
	
#endif
	}
	HANDLE hIcmp;
	IPAddr ipaddr;


	hIcmp=IcmpCreateFile();
	char cbRequest[BUF_SIZE];	//	送信するデータ
	memset(cbRequest,'a',BUF_SIZE);	//	送信するデータを設定
	DWORD cbReply=sizeof(ICMP_ECHO_REPLY)+BUF_SIZE;
	char* pReply=new char[cbReply];	//	受信するヘッダー + 受信するデータ
	PICMP_ECHO_REPLY p=(PICMP_ECHO_REPLY)pReply;

	WSADATA wsaData;
	int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);	//	WINSOCKを初期化

	unsigned char* ip=(unsigned char*)&ipaddr;
	ip[0]=ipa[0];
	ip[1]=ipa[1];
	ip[2]=ipa[2];
	_tprintf(_TEXT("IPアドレス      time  TTL マックアドレス    ベンダー名\tホスト名\n"));

	for(unsigned n=ipa[3];n<=end;n++){	//	IPアドレスの検索範囲を設定している
		ip[3]=n;
		_stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%3i.%3i.%3i.%3i PINGを実行しています。"),ip[0],ip[1],ip[2],ip[3]);
		SetWindowText(hLabel1,buf);
		// pingを実行
		//                           IPアドレス 送信データ データーサイズ                     タイムアウトms
		DWORD ret=IcmpSendEcho( hIcmp ,ipaddr , cbRequest ,BUF_SIZE , NULL , pReply , cbReply , 2000);
		if(ret){	//	pingが成功した
			unsigned char mac[6];
			struct hostent* hostp;
			ULONG len=sizeof(mac)/sizeof(unsigned char);
			if(SendARP(ipaddr,NULL,mac,&len)==NO_ERROR){	//	IPアドレスからmacアドレスを取得
				hostp=gethostbyaddr((const char*)ip,4,AF_INET);	//	IPアドレスからホスト名を取得
				TCHAR t[64];
				if(hostp)
#ifdef UNICODE
					//	UNICODEに変換
					MultiByteToWideChar(932,0,hostp->h_name,-1,t,sizeof(t)/sizeof(TCHAR));
#else
					_tcscpy_s(t,sizeof(t)/sizeof(TCHAR),hostp->h_name);
					
#endif
				else
					t[0]=_T('\0');

				_stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%3i.%3i.%3i.%3i"),ip[0],ip[1],ip[2],ip[3]);

				item.mask = LVIF_TEXT  | LVIF_PARAM;
				item.pszText =buf;
				item.iItem = num;
				item.iSubItem = 0;
				item.lParam = num;
				ListView_InsertItem(hList, &item);

				_stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%i"),p->RoundTripTime);	
				item.mask = LVIF_TEXT;
				item.pszText =buf;
				item.iItem = num;
				item.iSubItem = 1;
				ListView_SetItem(hList, &item);

				_stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%i"),p->Options.Ttl);	
				item.mask = LVIF_TEXT;
				item.pszText =buf;
				item.iItem = num;
				item.iSubItem = 2;
				ListView_SetItem(hList, &item);

				_stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%02x:%02x:%02x:%02x:%02x:%02x"),mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);	
				item.mask = LVIF_TEXT;
				item.pszText =buf;
				item.iItem = num;
				item.iSubItem = 3;
				ListView_SetItem(hList, &item);

				item.mask = LVIF_TEXT;
				item.pszText =mac2vendor_name(mac);
				item.iItem = num;
				item.iSubItem = 4;
				ListView_SetItem(hList, &item);

				item.mask = LVIF_TEXT;
				item.pszText =t;
				item.iItem = num;
				item.iSubItem = 5;
				ListView_SetItem(hList, &item);

				++num;
			}

		}else{	//	pingに失敗した場合
		}
		EnterCriticalSection(&cs);
		if(ping_run_f==false){
			LeaveCriticalSection(&cs);
			_stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%3i.%3i.%3i.%3iまでpingを実行しました"),ip[0],ip[1],ip[2],ip[3]);
			SetWindowText(hLabel1,buf);
			f=false;
			break;
		}
		LeaveCriticalSection(&cs);
	}
	if(f==true)
		SetWindowText(hLabel1,_TEXT(""));
	delete [] pReply;
	IcmpCloseHandle(hIcmp);
	WSACleanup();
	return f;
}

//	macアドレスよりベンダー名を取得する

TCHAR* mac2vendor_name(unsigned char* src_mac){
	for(unsigned n=0;n<vendor_vec.size();n++){
		if(vendor_vec[n].cmp(src_mac)==true)
			return vendor_vec[n].name;
	}
	return _TEXT("error");
}

//	macアドレスとベンダー名の対応を一覧する

void vendor_fput(FILE* fp){
	for(unsigned n=0;n<vendor_vec.size();n++){
		vendor_vec[n].put(fp);
	}
}

//	ファイルよりmacアドレスとベンダー名の対応を一覧を作成する

void get_mac_vendor(FILE* fp){
	TCHAR buf[256];
	int f=1;
	int len;
	TCHAR* top;
	TCHAR* p;

	VENDOR_ID id;
	while(_fgetts(buf,sizeof(buf)/sizeof(TCHAR),fp)){
		top=buf;
		while(*top){
			if(*top!=_T(' '))
				break;
			++top;
		}
		if(f==1){
			len=(int)_tcslen(top);
			if(10<=len){
				if(_istxdigit(top[0]) && top[2]==_T('-')){
					f=2;
				}
			}
		}
		switch(f){
			case 2:{
				TCHAR* name=top;
				id.mac[0]=(unsigned char)_tcstol(top,NULL,16);
				id.mac[1]=(unsigned char)_tcstol(top+3,NULL,16);
				id.mac[2]=(unsigned char)_tcstol(top+6,NULL,16);

				while(*name){
					if(*name==_T(' '))
						break;
					++name;
				}
				while(*name){
					if(*name!=_T(' '))
						break;
					++name;
				}
				while(*name){
					if(*name==_T(')'))
						break;
					++name;
				}
				while(*name){
					if(*name!=_T('\t'))
						break;
					++name;
				}
				name+=3;
				p=name;
				while(*p){
					if(*p==_T('\n') || *p==_T('\r') || *p==_T('\t'))
						*p=_T('\0');
					++p;
				}
				id.name=_tcsdup(name);
				vendor_vec.push_back(id);
				f=1;
				break;
			}
		}
	}
}

resource.h

#define IDC_IPADDRE	100	//	IPアドレス	
#define IDC_EDIT1	110	//	終了IPアドレス	
#define IDC_LISTVIEW1	120	//	リストビュー
#define IDC_LABEL1	130	//	ラベル	
#define	IDC_QUIT	140	//	終了

resource.rc

#include <windows.h>
#include "resource.h"

DLG1 DIALOG DISCARDABLE 0, 0, 494, 230
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_SETFONT
CAPTION "guiping"
FONT 9, "MS 明朝"
{
 CONTROL "IPアドレス範囲", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 7, 14 , 80 , 14
 CONTROL "IPAddress1", IDC_IPADDRE, "SysIPAddress32", WS_CHILD | WS_VISIBLE | SS_SUNKEN | SS_NOTIFY, 7, 28, 96, 14
 CONTROL "~", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 110, 32 , 8 , 14
 CONTROL "", IDC_EDIT1, "EDIT", WS_CHILD | WS_DLGFRAME | WS_VISIBLE | ES_AUTOHSCROLL, 130, 28, 32, 14

 CONTROL "応答のあったIPアドレス", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 7, 49 , 160 , 14
 CONTROL "ListView100", IDC_LISTVIEW1, "SYSLISTVIEW32", WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT, 7, 63, 480, 100

 CONTROL "", IDC_LABEL1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 7, 170 , 450 , 14

 CONTROL "ping実行", IDOK, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 143, 198, 60, 18
 CONTROL "キャンセル", IDCANCEL, "BUTTON", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 217, 198, 60, 18
 CONTROL "終了", IDC_QUIT, "BUTTON", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 291, 198, 60, 18
}

実行ファイルとソースファイルのダウンロード