Windowsプログラミング
アルゴリズム Vitual C++ 2008/2013によるWin32/Win64 APIレベルのプログラム 基礎 Vitual C++ 2008/2013によるAPIレベルのプログラム(32/64bit) Wix3でインストーラーを作る Visual C++ 2008 Standard Editonによるフォームアプリケーションのプログラム(32/64bit) Vitual C++ 2008 Standard EditonによるAPIレベルのプログラム(32/64bit) Windows 7対応 Visual C++ 2008 ExpressによるAPIレベルのプログラム Visual C++ 2005 ExpressによるAPIレベルのプログラム Visual C++ Versiosn 5 BORLAND C++ Windowsプログラム全般 Excel VBA その他
概要
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
}
Copyright (C) 2012 山本ワールド All Rights Reserved.