pingテスト及びmacアドレス・ホスト名・ベンダー名の取得2 GUI版(32/64bit)

icon 項目のみ表示/展開表示の切り替え

概要

 指定されたIPアドレス範囲に対してpingを実行し、応答があればホスト名及びmacアドレス等をリストビューに表示します。(IPv4専用)
 表示内容は、応答があったIPアドレスとtimeはパケット送信から受信までの時間(ミリ秒)、TTLはIPパケットの生存時間、macアドレス、ホスト名、ベンダー名である。
 ベンダー名の一覧は、IEEE-SAのホームページのhttp://standards-oui.ieee.org/oui.txtより本プログラムが自動的に取得し、macアドレスと一致するベンダーを表示します。
 1回取得できればファイルoui.txtに保存されます。次回は、インターネットから取得せずにファイルの一覧表を基にベンダー名を表示します。
 IPアドレスは自身のPCのIPアドレスとサブネットマスクによって範囲の初期値が設定されます。
 pingは16スレッドを同時に処理しますので高速に動作します。
 本プログラムはVistaからサポートしている高DPIに対応するため複雑ことを行っています。
 マニフェストでの高DPIの宣言は不要です。
高DPI対応

高DPI対応無し

IPアドレスの指定

 IPアドレスの検索方法には次の2種類の方法があります。

IPアドレス範囲で指定


 ラジオボタンで範囲で検索が選択されている場合、この方法が選択されます。
 このPCのIPアドレスを選択するとこのPCのIPアドレスとサブネットマスクによってIPアドレス範囲が自動的にIPアドレス範囲に反映されます。IPアドレスの検索範囲は第4オクテットのみです。ユーザー自身が範囲を変更することができます。
 上図の場合、IPアドレス192.168.0.0~192.168.0.10が検索対象となります。

サブネットマスクで検索


 ラジオボタンでサブネットマスクで検索が選択されている場合、この方法が選択されます。
 このPCのIPアドレスを選択するとこのPCのIPアドレスとサブネットマスクによってIPアドレス範囲が自動的に変更されます。IPアドレスの検索範囲は、このIPアドレスとサブネットマスクにより自動的に算出されます。IPアドレスは第1~第4オクテットにわたって有効です。ダイアログボックスのIPアドレスの範囲とサブネットマスクに指定された値は無視されます。
 上図の場合、ブロードキャストアドレス192.168.0.255を除いた、IPアドレス192.168.0.0~192.168.0.254が検索対象となります。

 上図の場合、ブロードキャストアドレス192.168.255.255を除いた、IPアドレス192.168.0.0~192.168.255.255が検索対象となります。

バッファサイズ

 ディフォルトではWindowsのpingコマンドと同じ32byteに設定しています。

タイムアウト

ディフォルト値は2000msとしています。255個のアドレス範囲で自分自身以外に機器がない場合、254個が応答なしとなります。16スレッドなので、 最低、254個/16スレッド*2000ms=32秒実行時間がかかることとなります。LAN内で2000msの応答時間がかかることはないので、総当たりで検索する場合は、タイムアウト値は小さな値を指定すべきです。

ping実行ボタン

 設定されたIPアドレスの範囲にたいして16スレッドでpingを実行します。キャンセルボタンによりpingを中止することができます。

キャンセル

実行中のpingを中止します。

保存

 リストビューに表示されている内容をテキストファイル(TAB区切り形式)で保存します。

終了

 本プログラムを終了します。

テスト環境

コンパイラ

Visual C++ 2013 Express 32/64bit

動作確認

Windows 7
Windows 8.1
Windows 10 Version 1803

プログラムソースの概要

ping2.cpp

WinMain

 高DPIに対応するために以下の処理をしています。
SetThreadDpiAwarenessContext
 Windows 10 Version 1703より古い場合、プログラムの初期時にSetThreadDpiAwarenessContext APIが存在するかチェックし存在する場合、Windows10のバージョンをチェックします。
 バージョンが1607の場合は、非クライアント領域の大きさを変更させるためWM_NCCREATEメッセージ発生時にEnableNonClientDpiScalingを呼び出します。
 バージョンが1703以降の場合は、SetThreadDpiAwarenessContext APIでDPI_AWARENESS_PER_MONITOR_AWARE_V2を指定することにより Windowsに非クライアント領域の大きさの変更を自動にさせます。
SetProcessDPIAware
 SetThreadDpiAwarenessContext APIが使えない場合、SetProcessDPIAware API(Windows Vista以降)が使えるか確認します。
 事前にSetProcessDPIAware API(Windows Vista以降)を実行するとプログラム自身がスケーリング処理をすることになり正常なDPI値が返されます。実行しない場合、常に96DPIが返されます。Windows XPではこのAPIは存在しないので96DPIと仮定します。なおAPIが存在するかどうかは動的にライブラリをロードし関数のエントリポイントが存在するかどうかで判断しています。
 DPIはGetDpiForMonitor API(Windows 8.1以降)が使える場合はこの関数でウィンドウが属するモニターのDPIを、使えない場合GetDeviceCaps(hdc,LOGPIXELSX)によりディスクトップのDPIを取得します。
SetProcessDPIAware宣言無し(Windows 7)

SetProcessDPIAware宣言有り(Windows 7)

 WINSOCKの初期化が必要であるため、WSAStartup APIを呼び出します。
 ダイアログボックスを開きます。
 WSACleanup APIによりWINSOCKの開放を行います。

DlgProc

WM_INITDIALOG
 ダイアログボックスの初期化時に呼び出されます。
 以下の処理はWindows 10 Version 1703以降では実行しません。
  1. ダイアログボックスの座標単位とスクリーン座標の変換に必要な定数をGetActualDialogBaseUnitsで取得
  2. scaleChange関数でダイアログボックス内の各コントロールの大きさ等を変更
 get_my_ip関数により自身のPCのIPアドレスとサブネットマスクを取得します。複数のLANカードにも対応しています。
 リストビューのヘッダ及びIPアドレスコントロール等の初期値を設定します。
WM_DPICHANGED
 Windows 10 Version 1703以降では以下の処理は行いません。
 引数から現DPIを取得しscaleChange関数でダイアログボックス内の各コントロールの大きさ等を変更します。
 ダイアログボックスの各コントロールの移動・大きさ変更は、EnumChildWindows APIから呼び出されるEnumWndProc関数で実行しています。
 リストビューはヘッダーを子ウィンドウとしていますが、リストビューからヘッダーへとヘッダー自身合わせて2回EnumWndProc関数が呼び出されることによりヘッダーの位置がずれる時が発生します。
 リストビューのヘッダーの場合は、移動等の処理を行わないようにしています。
WM_NCCREATE
 Wndows10(Version1607)の時だけ非クライアント領域の処理するためEnableNonClientDpiScaling関数を呼び出します
WM_NOTIFY
 メッセージの中からIPアドレスコントロールで値が変化した場合を抽出し、グローバル変数にその値を保存します。
WM_COMMAND
 各ボタンがクリックされた場合およびコンボボックスやエディットボックスの内容が変更された場合に呼び出されます。
 wParamの下位ワード(16bit)にコントロールのID、wParamの上位ワードにメッセージが格納されています。
 コントロールのID別に処理をswitchステートメントで記述しています。
IDC_END_ADR_EDIT
 IPアドレス範囲のうち終わりを設定するエディットボックスからのメッセージがある場合に呼び出されます。
 エディットボックスの内容に変化があった場合EN_UPDATEメッセージが送られるので、エディットボックスの値を取得し、検索するIPアドレスの個数を変更します。
IDC_COMBOBOX
 このPCのIPアドレスコンボボックスのメッセージがある場合に呼び出されます。
 コンボボックスの選択されているアイテムに変化があった場合CBN_SELCHANGEメッセージが送られるので、選択されているIPアドレスとサブネットマスク値を取得し、検索するIPアドレスの個数を変更します。
 また、選択されているIPアドレスとサブネットマスクを基にIPアドレス範囲とサブネットマスクの表示する値を変更します。
IDOK
 ping実行ボタンがクリックされた場合に呼び出されます。
 直接関数を呼び出すとメッセージが回らなくなりキャンセルボタンが有効にならずしかも応答なし状態となるのでスレッドとしてping_thread関数を呼び出します。
IDC_SAVE
 保存ボタンが押された時呼び出されます。
 GetFileName関数を呼び出し保存するファイル名を取得します。
 ListViewPut関数を呼び出しリストビューの内容を呼び出しテキスト形式(TAB区切り)で保存します。
IDC_ERASE
 クリアボタンが押された時呼び出されます。
 リストビューにLVM_DELETEALLITEMSメッセージを送信しリストビューのアイテムを全部クリアします。
IDCANCEL
 キャンセルボタンがクリックされた場合に呼び出されます。
 クリティカルセッションを使い、ping_run_fをfalseにセットします。
 ping関数がこの値をたまにチェックしており、falseの場合、関数を終了します。
IDC_QUIT
 終了ボタンをクリックすると呼び出され、EndDialog APIにより本プログラムを終了させます。

mask_max

 指定されたIPアドレスとサブネットマスク値よりとりうるIPアドレスの最大個数(ブロードキャストアドレスを除く)を計算し返します。

ip_range_set

 IPアドレスの開始値と終了値よりとりうるIPアドレスの最大個数を計算し返します。

ip_mask_set

 指定されたIPアドレスとサブネットマスク値を基にアドレス範囲の初期値を設定します。

ListViewHeader

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

get_mac_url

 カレントフォルダーにmacアドレスとベンダー名の一覧を示すファイルoui.txtがあれば、ファイルを読み込みmacアドレスとベンダー名の一覧を作成します。
 ファイルがない場合、メッセージボックスを表示、インターネットからの読み込みが選択された場合、インターネットからの読み取りを実行しファイルに保存します。
インターネットからの読み取り
ファイルからの読み取り
 get_mac_vendor関数を呼び出しファイルを読み取り、macアドレスとベンダー名の一覧(vendor_vec)を作成します。

ping_thread

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

ping

 指定された範囲のIPについてpingを実行し、結果をリストビューに表示します。
 スレッドを管理するTHREAD_MANクラスを16スレッド、スレッド関数ping_rootを指定して初期化します。
 macアドレスとベンダー名の一覧(vendor_vec)が作成されていない場合は、get_mac_url関数を呼び出して作成します。
 スレッド分のpingで使用するバッファをnew演算子で作成します。
 IcmpCreateFile APIによりスレッド数分のハンドルを作成します。
 スレッド分の送信するデータを初期化します。
 IP範囲の指定方法をラジオボタンのチェック状態から取得します。
 範囲の検索が指定されている場合は、第4オクテットをfor分で変化させます。
 サブネットマスクで検索が指定されている場合は、各オクテットのipの組み合わせが保存されている配列ip1,ip2,ip3,ip4を検索してIPアドレスを変化させます。
 各IPアドレスに対して以下の処理を行います。
 SetWindowText APIにより現在のIPアドレスを表示します。
 スレッドを管理するTHREAD_MANクラスのdispatchメソッドにより待機中のスレッドを検索し、スレッドにIPアドレスを指定して、pingを実行します。
 ping_run_fをチェックしflaseがセットされていれば、検索を終了します。
 検索が終了したら、スレッドを管理するTHREAD_MANクラスのall_killメソッドにより全スレッド関数ping_rootを終了させます。
 IcmpCloseHandle APIによりスレッド数分のハンドルを解放します。
 new演算子で作成したpingで使用したバッファをdelete []演算子で解放します。

mac2vendor_name

 指定されたmacアドレスをvendor_vec配列から検索し一致したものがあればベンダー名を返します。

vendor_fput

 macアドレスとベンダー名の対応の配列をファイルに保存します。

get_mac_vendor

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

ListViewPut

 リストビューをTAB形式でファイルへ保存する

GetFileName

 ファイル名を指定するコモンダイアログを表示

error_msg

 エラーコードに対応するメッセージを出力する

set_ip

 マスク値から取りうるIPアドレスの一覧を作成する(1byte)

get_my_ip

 自身のIPアドレスを取得

run_thread

 THREAD_MANから起動されるスレッドです。
 ping_run_fがflaseの場合は、スレッドの終了させます。
 スレッドに割り付けられているイベントrun_evenメンバーをチェックしシグナル状態になるまで待機しシグナルになった時、IPアドレスがセットされているので、ユーザー定義関数(ping_root)を実行します。
 イベントのチェックがタイムアウトした場合は、50ms待機し再度イベントのチェックを行います。
 ユーザー定義関数(ping_root)が終了した場合は、非シグナル状態に設定します。

THREAD_MANクラス

init

 スレッドごとに割り付けるイベント(run_eventメンバー)を非シグナル状態で作成します。このイベントは各スレッドがping実行中の場合シグナル、待機中の場合は非シグナルとなります。
 指定されたスレッド数のrun_threadスレッドを作成します。

clear

 スレッド用の変数を解放します。

all_kill

 スレッドの終了を待ちます。

dispatch

 イベントをチェックし待機スレッドを検索しそのスレッドにIPアドレスを設定しイベントをシグナルに設定し、スレッドを待機状態から実行状態に移行させます。

scaleChange

 現在のフォントを取得しスケールに応じたフォントを新規に作成し設定します。
 スケールからダイアログボックスの大きさを計算し、AdjustWindowRectによりクライアントサイズから必要なウィンドウサイズを計算しSetWindowPosによりダイアログボックスの大きさを変更します。
 各コントロールウィンドウをEnumChildWindowsで列挙します。

EnumWndProc

 EnumChildWindowsから呼び出されるEnumWndProc関数によりスケールに応じた位置、サイズ、文字サイズを設定します。
 スケールからコントロールの大きさを計算し、AdjustWindowRectによりクライアントサイズから必要なウィンドウサイズを計算しSetWindowPosによりダイアログボックスの大きさを変更します。
 コントロールのフォントをWM_SETTEXTメッセージにより変更します。

GetActualDialogBaseUnits

 ダイアログボックス座標の計算に必要な単位を取得します。
 YogaPro3では横が13、縦が31ピクセルが取得されました。

win10ver2

 Windows 10のバージョンを取得する関数です。
 詳細は、Windows 10のバージョン番号をRtlGetVersionのビルド番号から取得するを参照してください。

ソースコード

guiping2.cpp

//	指定した範囲のIPアドレスに同時に最大16個同時にPINGを実行し、レスポンスがあればマックアドレスとベンダー名およびホスト名を表示
//	2018/10/09

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

#pragma comment(lib,"iphlpapi.lib")
#pragma comment(lib,"Ws2_32.lib")
#pragma comment(lib,"wininet.lib")
#pragma comment(lib,"comctl32.lib")

#ifndef _DPI_AWARENESS_CONTEXTS_
DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
#endif

#define WM_DPICHANGED                   0x02E0

typedef enum MONITOR_DPI_TYPE {
	MDT_EFFECTIVE_DPI = 0,
	MDT_ANGULAR_DPI = 1,
	MDT_RAW_DPI = 2,
	MDT_DEFAULT = MDT_EFFECTIVE_DPI
} MONITOR_DPI_TYPE;


typedef HRESULT(_stdcall *GetDpiForMonitorFunc)(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *);
typedef HRESULT(_stdcall* EnableNonClientDpiScalingFunc)(HWND);
typedef HRESULT(_stdcall* SetThreadDpiAwarenessContextFunc)(DPI_AWARENESS_CONTEXT);
typedef BOOL(_stdcall *SetProcessDPIAwareFunc)(VOID);

HMODULE hModUser32 = 0;
SetThreadDpiAwarenessContextFunc ThreadAwareFunc = 0;
EnableNonClientDpiScalingFunc NCCfunc = 0;

#ifndef _DPI_AWARENESS_CONTEXTS_

typedef enum _DPI_AWARENESS {
	DPI_AWARENESS_INVALID = -1,
	DPI_AWARENESS_UNAWARE = 0,
	DPI_AWARENESS_SYSTEM_AWARE = 1,
	DPI_AWARENESS_PER_MONITOR_AWARE = 2
} DPI_AWARENESS;

#define DPI_AWARENESS_CONTEXT_UNAWARE              ((DPI_AWARENESS_CONTEXT)-1)
#define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE         ((DPI_AWARENESS_CONTEXT)-2)
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE    ((DPI_AWARENESS_CONTEXT)-3)
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4)
#define DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED    ((DPI_AWARENESS_CONTEXT)-5)

#endif

#define OUT_TXT_URL _TEXT("http://standards-oui.ieee.org/oui.txt")

int BUF_SIZE = 32;	//	pingのバッファサイズ
int TIMEOUT = 2000;	//	タイムアウト値(ms)

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;
	}
};

//	IPアドレスとサブネットマスク値

struct IP_ADS_MASK{
	DWORD32 ip;
	DWORD32 mask;
};

using namespace std;

vector<VENDOR_ID> vendor_vec;	//	macアドレスとベンダー名の一覧
vector<IP_ADS_MASK> my_ip_ads_mask;	//	このPCのアドレスとサブネットマスク
int my_ip_sel = 0;	//	コンボボックスで選択されている my_ip_ads_maskの要素
HWND hDlg = 0;
HWND hList = 0;
HWND hLabel1 = 0;
unsigned char ip[4];
unsigned char ends;
CRITICAL_SECTION cs;	//	ping_run_fの読書用のクリティカルセッション
CRITICAL_SECTION listview_cs;	//	リストビューの行数の操作用のクリティカルセッション

bool ping_run_f = false;	//	ping実行中にfalseをセットするとpingを終了する

int listview_max = 0;	//	リストビューの行数

//	マスク値でpingを実行する場合の各バイトの組み合わせ表
BYTE ip1[256];
BYTE ip2[256];
BYTE ip3[256];
BYTE ip4[256];
int	ip1_max;
int ip2_max;
int ip3_max;
int ip4_max;


//	自身のIPアドレスを取得
BOOL get_my_ip(void);

//	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);

//	リストビューをTAB形式でファイルへ保存する
void ListViewPut(HWND hList, FILE* fp);

// ファイル名を指定するコモンダイアログを表示
BOOL GetFileName(HWND hWnd, TCHAR* fname, int sz);

//	マスク値から取りうるIPアドレスの一覧を作成する(1byte)
int set_ip(DWORD x, BYTE* ip);

//	マスク値から取りうるIPアドレス(IPv4)の個数を返す

DWORD mask_max(DWORD32 ipads, DWORD32 maskv);

//	IPアドレスのマスク値より必要な情報をセット

void ip_mask_set(void);

//	IPアドレスと終了アドレスより必要な情報をセット

void ip_range_set(void);

//	エラーコードに対応するメッセージを出力する
void error_msg(HWND hWnd);

//	スレッドの管理

//	ユーザー定義関数
typedef DWORD (* THREAD_MAN_FUNC)(LPVOID lParam,int num);

struct THREAD_MAN_CREATE{
	LPVOID thread_man;
	int num;
};

//	登録した関数をマルチスレッドで実行するクラス

struct THREAD_MAN{
	HANDLE* run_event;	//	イベントオブジェクト シグナル時はスレッドが実行中
	HANDLE* thread;		//	スレッドハンドル
	LPVOID* data;	//	ユーザースレッドに渡すデータ
	THREAD_MAN_FUNC func;	//	ユーザー定義関数
	int thread_max;	//	スレッド数
	THREAD_MAN_CREATE* tmcp;	//	各スレッドの初期値
	friend DWORD WINAPI run_thread(LPVOID lParam);	//	起動されるスレッド

	THREAD_MAN(){
		thread_max = 0;
	}
		
	void init(int max, THREAD_MAN_FUNC def_func){	//	スレッドの起動
		func = def_func;
		thread_max = max;
		run_event = new HANDLE[thread_max];
		thread = new HANDLE[thread_max];
		data = new LPVOID[thread_max];
		tmcp = new THREAD_MAN_CREATE[thread_max];

		for (int n = 0; n < thread_max; n++){
			run_event[n] = CreateEvent(NULL, TRUE, FALSE, NULL);//	非シグナル状態で初期化
			tmcp[n].thread_man = (LPVOID)this;
			tmcp[n].num = n;
			thread[n] = CreateThread(NULL, 0, run_thread, &tmcp[n], 0, NULL);	//	run_threadをスレッドとして起動
		}
	}
	void clear(void){	//	スレッド用の変数の解放
		delete[] tmcp;
		delete[] data;
		delete[] thread;
		delete[] run_event;
	}
	void all_kill(void){	//	スレッドの終了を待つ
		WaitForMultipleObjects(thread_max, thread, TRUE, INFINITE); //全スレッドの終了を待つ
		for (int n = 0; n < thread_max; n++){
			thread[n] = NULL;
		}
	}
	void dispatch(LPVOID lParam){	//	待機スレッドに処理を発行
		DWORD ret;
		int n;
		do{
			Sleep(100);
			ret = WaitForMultipleObjects(thread_max, run_event, TRUE, 0);	// 全イベントがシグナル状態であるか取得
		} while (WAIT_OBJECT_0 <= ret && ret < WAIT_OBJECT_0 + thread_max);	//	全イベントがシグナル状態なので待機
		for (n = 0; n < thread_max; n++){
			ret = WaitForSingleObject(run_event[n], 0);	// 全イベントがシグナル状態であるか取得
			if (ret != WAIT_OBJECT_0)	//	非シグナル
				break;
		}
		data[n] = lParam;
		SetEvent(run_event[n]);	//	シグナル状態に設定
	}
};

THREAD_MAN thread_man;
int win10ver2(void);
int WIN10VER;

int WINAPI _tWinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,TCHAR* lpsCmdLine, int nCmdShow){
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);	//	WINSOCKを初期化
	WIN10VER=win10ver2();
	hModUser32 = LoadLibrary(L"User32.dll");
	if (hModUser32){
		NCCfunc = (EnableNonClientDpiScalingFunc)GetProcAddress(hModUser32, "EnableNonClientDpiScaling");
		if (WIN10VER == 1607)	//	AnniversaryUpdate
			NCCfunc = (EnableNonClientDpiScalingFunc)GetProcAddress(hModUser32, "EnableNonClientDpiScaling");
		else
			NCCfunc = 0;
		ThreadAwareFunc = (SetThreadDpiAwarenessContextFunc)GetProcAddress(hModUser32, "SetThreadDpiAwarenessContext");
		if (ThreadAwareFunc){
			if (WIN10VER<1703)
				(*ThreadAwareFunc)(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
			if (1703 <= WIN10VER && WIN10VER<1809)
				(*ThreadAwareFunc)(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
			if (1809 <= WIN10VER)
				(*ThreadAwareFunc)(DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED);
		}
		else{	//	Windows Vista以降
			SetProcessDPIAwareFunc ProcessAwareFunc = (SetProcessDPIAwareFunc)GetProcAddress(hModUser32, "SetProcessDPIAware");
			if (ProcessAwareFunc)
				(*ProcessAwareFunc)();
		}
	}

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

	WSACleanup();
	return (int)0;
}

BOOL GetActualDialogBaseUnits(HWND hDialog, SIZE *baseUnit){
	RECT rect;
	BOOL result;

	rect.left = 4;
	rect.top = 8;
	if (result = MapDialogRect(hDialog, &rect)) {
		baseUnit->cx = rect.left;
		baseUnit->cy = rect.top;
	}
	return result;
}

struct DLG_CHILD_SCALE{
	HWND hDlg;
	SIZE unit;
	UINT oldScale;
	UINT newScale;
	HFONT hFont;
};
//	各ウィンドウ(コントロールの移動及び大きさの変更
BOOL CALLBACK EnumWndProc(HWND hWnd, LPARAM lParam){
	POINT pt;
	TCHAR name[16];
	GetClassName(hWnd,name,sizeof(name)/sizeof(name[0]));//リストビューのヘッダは孫ウィンドウ
	if(_tcscmp(name,_TEXT("SysHeader32"))==0)
		return TRUE;
	DLG_CHILD_SCALE* d = (DLG_CHILD_SCALE*)lParam;
	pt.x = 0;
	pt.y = 0;
	ClientToScreen(hWnd, &pt);
	ScreenToClient(d->hDlg, &pt);
	RECT rc;
	GetClientRect(hWnd, &rc);
	int cx, cy, w, h;
	cx = MulDiv(pt.x, 4, d->unit.cx);
	cy = MulDiv(pt.y, 8, d->unit.cy);

	cx = MulDiv(cx, d->newScale, d->oldScale);
	cy = MulDiv(cy, d->newScale, d->oldScale);

	cx = MulDiv(cx, d->unit.cx, 4);
	cy = MulDiv(cy, d->unit.cy, 8);

	w = MulDiv(rc.right, d->newScale, d->oldScale);
	h = MulDiv(rc.bottom, d->newScale, d->oldScale);

	rc.left = cx;
	rc.top = cy;
	rc.right = cx + w;
	rc.bottom = cy + h;
	LONG style = GetWindowLong(hWnd, GWL_STYLE);
	LONG exStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
	AdjustWindowRectEx(&rc, style, FALSE, exStyle);
	SetWindowPos(hWnd, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER);
	SendMessage(hWnd, WM_SETFONT, (WPARAM)d->hFont, MAKELPARAM(TRUE, 0));
	UpdateWindow(hWnd);

	return TRUE;
}
//	DPIスケールの変更
HFONT scaleChange(HWND hDlg, int nowScale, int oldScale, SIZE& baseUnit, HFONT hFont){
	LOGFONT lf;
	HWND hWnd;
//	hWnd = GetWindow(hDlg, GW_CHILD);
	hWnd=GetDlgItem(hDlg,IDC_END_ADR_EDIT);
	HDC hdc = GetDC(hWnd);
	HFONT hfont9 = (HFONT)SendMessage(hWnd, WM_GETFONT, 0, 0);
	GetObject(hfont9, sizeof(LOGFONT), &lf);
	int height;
	height = lf.lfHeight * 100 / oldScale;
	lf.lfHeight = (height)*nowScale;
	lf.lfHeight /= 100;
	HFONT hFont2 = CreateFontIndirect(&lf);
	LOGFONT lfont;
	HFONT hFontOld = (HFONT)SelectObject(hdc, hFont2);

	TEXTMETRIC metrics;
	GetTextMetrics(hdc, &metrics);
	SelectObject(hdc, hFontOld);

	HFONT htemp = (HFONT)SendMessage(hDlg, WM_GETFONT, 0, 0);
	GetObject(htemp, sizeof(LOGFONT), &lfont);
	ReleaseDC(hWnd, hdc);

	RECT rc;
	GetClientRect(hDlg, &rc);
	int w = rc.right - rc.left;
	int h = rc.bottom - rc.top;
	w = MulDiv(w, nowScale, oldScale);
	h = MulDiv(h, nowScale, oldScale);

	rc.left = 0;
	rc.top = 0;
	rc.right = w;
	rc.bottom = h;

	LONG style = GetWindowLong(hDlg, GWL_STYLE);
	AdjustWindowRectEx(&rc, style, FALSE, 0);

	SetWindowPos(hDlg, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER);

	DLG_CHILD_SCALE d;
	d.hDlg = hDlg;
	d.hFont = hFont2;
	d.newScale = nowScale;
	d.oldScale = oldScale;
	d.unit = baseUnit;

	EnumChildWindows(hDlg, EnumWndProc, (LPARAM)&d);


	InvalidateRect(hDlg, NULL, FALSE);
	UpdateWindow(hDlg);
	if (hFont)
		DeleteObject(hFont);
	return hFont = hFont2;
}

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

LRESULT CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){
	static HWND hIPA;
	static HWND hEdit;
	static DWORD tid;
	TCHAR buf[64];
	static HFONT hFont = 0;
	static int nowDPIx = 96;
	static int nowDPIy = 96;
	static UINT nowScale = 100;
	static UINT initScale = 100;
	static int height;
	static HMODULE hMod;
	static GetDpiForMonitorFunc func = 0;
	static SIZE baseUnit;
	switch (msg) {
		case WM_INITDIALOG:{
			::hDlg=hDlg;
			if (WIN10VER < 1703){
				HMONITOR hMonitor = MonitorFromWindow(hDlg, MONITOR_DEFAULTTONULL);
				hMod = LoadLibrary(L"Shcore.dll");

				func = (GetDpiForMonitorFunc)GetProcAddress(hMod, "GetDpiForMonitor");
				if (func == 0)
					FreeLibrary(hMod);

				UINT hDPI, vDPI;
				if (func)
					(*func)(hMonitor, MDT_EFFECTIVE_DPI, &hDPI, &vDPI);
				else{
					HDC hdc = GetDC(NULL);	//	ディスクトップ
					hDPI = GetDeviceCaps(hdc, LOGPIXELSX);
					vDPI = GetDeviceCaps(hdc, LOGPIXELSY);
				}
				nowDPIx = hDPI;
				nowDPIy = vDPI;
				nowScale = MulDiv(nowDPIx, 100, 96);
				GetActualDialogBaseUnits(hDlg, &baseUnit);
				UINT oldScale = nowScale;
				hFont = scaleChange(hDlg, nowScale, oldScale, baseUnit, hFont);
			}
			hList=GetDlgItem(hDlg,IDC_LISTVIEW1);
			hIPA=GetDlgItem(hDlg,IDC_IPADDRE);
			hEdit=GetDlgItem(hDlg,IDC_END_ADR_EDIT);
			hLabel1=GetDlgItem(hDlg,IDC_LABEL1);

			ip4_max = 0;
			ListViewHeader(hList);
			SetWindowText(hEdit,_TEXT("254"));
			if(get_my_ip()==TRUE){	//	このPCのIPアドレスおよびサブネットマスク値を得る
				for (DWORD n = 0; n < my_ip_ads_mask.size(); n++){
					char ansi[16];
					inet_ntop(AF_INET, (void*)&::my_ip_ads_mask[n].ip, ansi, sizeof(ansi));
					SendMessageA(GetDlgItem(hDlg, IDC_COMBOBOX), CB_INSERTSTRING, n,(LPARAM) ansi);	//コンボボックスにアイテムの設定
				}
				SendMessage(GetDlgItem(hDlg, IDC_COMBOBOX), CB_SETCURSEL, ::my_ip_sel, (LPARAM)0);	//	初期時に選択されるアイテムを選択
				*(DWORD*)ip = ::my_ip_ads_mask[::my_ip_sel].ip & ::my_ip_ads_mask[::my_ip_sel].mask;
				ip_mask_set();
				ip_range_set();
			}else{
				MessageBox(hDlg, _TEXT("このPCのIPアドレスが検出できませんでした"), _TEXT("エラー"), MB_OK);
				EndDialog(hDlg, TRUE);
				return TRUE;
			}
			LPARAM ipadd = MAKEIPADDRESS(ip[0], ip[1], ip[2], ip[3]);
			SendMessage(hIPA, IPM_SETADDRESS, 0, ipadd);
			EnableWindow(GetDlgItem(hDlg, IDCANCEL), FALSE);
			SendMessage(GetDlgItem(hDlg, IDC_RADIOBOX101), BM_SETCHECK, BST_CHECKED, 0);
			_stprintf_s(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("%d"), BUF_SIZE);
			SetWindowText(GetDlgItem(hDlg,IDC_BUFSIZE), buf);
			_stprintf_s(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("%d"),TIMEOUT);
			SetWindowText(GetDlgItem(hDlg, IDC_TIMEOUT), buf);

			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;
					ip_range_set();
					return TRUE;
				}
				default:
					return FALSE;
				}
			}
			break;
		case WM_DPICHANGED:{
			if (WIN10VER < 1703){
				UINT oldScale = nowScale;
				nowDPIx = LOWORD(wParam);
				nowDPIy = HIWORD(wParam);
				nowScale = MulDiv(nowDPIx, 100, 96);
				hFont = scaleChange(hDlg, nowScale, oldScale, baseUnit, hFont);
			}
			break;
		}
		case WM_NCCREATE:{
			if (NCCfunc)
				(*NCCfunc)(hDlg);
			return FALSE;
		}
		case WM_COMMAND:
            switch (LOWORD(wParam)) {
				case IDC_END_ADR_EDIT:{
					if (HIWORD(wParam) == EN_UPDATE){
						GetWindowText(hEdit, buf, sizeof(buf) / sizeof(TCHAR));
						::ends = _ttoi(buf);
						ip_range_set();
					}
					break;
				}
				case IDC_COMBOBOX:{
					if (HIWORD(wParam) == CBN_SELCHANGE){	//	コンボボックスのアイテムが選択されたときに呼び出される
						::my_ip_sel = (int)SendMessage(GetDlgItem(hDlg, IDC_COMBOBOX), CB_GETCURSEL, 0, 0);	//	カーソル位置のアイテムを取得
						ip_mask_set();
						*(DWORD*)ip = ::my_ip_ads_mask[::my_ip_sel].ip & ::my_ip_ads_mask[::my_ip_sel].mask;
						LPARAM ipadd = MAKEIPADDRESS(ip[0], ip[1], ip[2], ip[3]);
						SendMessage(hIPA, IPM_SETADDRESS, 0, ipadd);
						ip_range_set();
					}
					break;
				} 
				case IDOK:{	//	ping実行
					EnableWindow(GetDlgItem(hDlg,IDOK),FALSE);
					EnableWindow(GetDlgItem(hDlg, IDC_SAVE), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDC_ERASE), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDCANCEL), TRUE);
					TCHAR buf[8];
					GetWindowText(hEdit,buf,sizeof(buf)/sizeof(TCHAR));
					ends=_ttoi(buf);
					int minv=min(ip[3],ends);
					int maxv=max(ip[3],ends);
					ip[3]=minv;
					ends=maxv;

					GetWindowText(GetDlgItem(hDlg,IDC_BUFSIZE), buf, sizeof(buf) / sizeof(TCHAR));
					BUF_SIZE = _ttoi(buf);
					if (BUF_SIZE < 32 || 65536 < BUF_SIZE){
						MessageBox(hDlg, _TEXT("バッファイサイズは32~65536の間でなければなりません。"), _TEXT("エラー"), MB_OK);
						EnableWindow(GetDlgItem(hDlg, IDOK), TRUE);
						EnableWindow(GetDlgItem(hDlg, IDC_SAVE), TRUE);
						EnableWindow(GetDlgItem(hDlg, IDC_ERASE), TRUE);
						EnableWindow(GetDlgItem(hDlg, IDCANCEL), FALSE);
						return FALSE;
					}

					GetWindowText(GetDlgItem(hDlg, IDC_TIMEOUT), buf, sizeof(buf) / sizeof(TCHAR));
					TIMEOUT = _ttoi(buf);

					ping_run_f = true;
					CreateThread(NULL,0,ping_thread,0,0,&tid);	//	pingをスレッドとして実行
					return FALSE;
				}
				case IDC_SAVE:{	//	リストビューの内容をテキストファイルに保存
					FILE* fp;
					TCHAR buf[MAX_PATH];
					if (GetFileName(hDlg, buf, sizeof(buf) / sizeof(TCHAR)) == TRUE){
						if (_tfopen_s(&fp, buf, _TEXT("w")) == 0){
							ListViewPut(hList, fp);
							fclose(fp);
						}
					}
					return TRUE;
				}
				case IDC_ERASE:{	//	リストビュー全消去
					SendMessage(hList,LVM_DELETEALLITEMS,0,0);
					return TRUE;
				}
				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;
}

//	マスク値から取りうるIPアドレス(IPv4)の個数を返す

DWORD mask_max(DWORD32 ipads, DWORD32 maskv){
	BYTE* my_ip = (BYTE*)&ipads;
	BYTE* mask = (BYTE*)&maskv;

	ip1_max = set_ip(mask[0], ip1);
	ip2_max = set_ip(mask[1], ip2);
	ip3_max = set_ip(mask[2], ip3);
	ip4_max = set_ip(mask[3], ip4); 
	if (ip1_max == 0){
		ip1[0] = my_ip[0];
		ip1_max = 1;
	}
	if (ip2_max == 0){
		ip2[0] = my_ip[1];
		ip2_max = 1;
	}
	if (ip3_max == 0){
		ip3[0] = my_ip[2];
		ip3_max = 1;
	}
	if (ip4_max == 0){
		ip4[0] = 0;
		ip4_max = 1;
	}
	return ip1_max*ip2_max*ip3_max*ip4_max - 1;
}

//	IPアドレスと終了アドレスより必要な情報をセット

void ip_range_set(void){
	TCHAR buf[16];
	GetWindowText(GetDlgItem(::hDlg, IDC_END_ADR_EDIT), buf, sizeof(buf) / sizeof(TCHAR));
	::ends = _ttoi(buf);
	int minv = min(::ip[3], ::ends);
	int maxv = max(::ip[3], ::ends);

	_stprintf_s(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("%d個"), maxv - minv + 1);
	SetWindowText(GetDlgItem(::hDlg, IDC_RENGE_LABEL), buf);
}

//	IPアドレスのマスク値より必要な情報をセット

void ip_mask_set(void){
	TCHAR buf[16];
	char ansi[16];
	DWORD ipmax = mask_max(::my_ip_ads_mask[::my_ip_sel].ip, ::my_ip_ads_mask[::my_ip_sel].mask);
	inet_ntop(AF_INET, (void*)&::my_ip_ads_mask[::my_ip_sel].mask, ansi, sizeof(ansi));
	SetWindowTextA(GetDlgItem(::hDlg, IDC_MY_MASK_EDIT), ansi);
	_stprintf_s(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("%d個"), ipmax);
	SetWindowText(GetDlgItem(::hDlg, IDC_MY_MASK_LABEL), buf);

	if (0 < ip4_max){
		_stprintf_s(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("%d"), ip4[ip4_max - 1] - 1);
		SetWindowText(GetDlgItem(::hDlg, IDC_END_ADR_EDIT), buf);
	}
}

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

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);
}

//	ベンダーの一覧をファイル又はwebから取得しファイルに保存する

bool get_mac_url(void){
	TCHAR buf[128];
	int num = 0;
	bool f = true;
	FILE* fp;
	if (_tfopen_s(&fp, _TEXT("oui.txt"), _TEXT("r"))){
		if (MessageBox(hDlg, _TEXT("ベンダー一覧をインターネットから取得しますか"), _TEXT("選択"), MB_YESNO) == IDNO){
			return false;
		}else{
			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, OUT_TXT_URL, 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);
				fclose(fp);
				return false;
			}
			LeaveCriticalSection(&cs);
		}
	}
	get_mac_vendor(fp);
	fclose(fp);
	return true;
}

//	指定アドレスにpingを実行し応答があればリストビューに表示

BOOL ping_sub(HANDLE hIcmp, IPAddr ipaddr, char* cbRequest,  char*  pReply,DWORD cbReply,DWORD timeout){
	TCHAR buf[128];
	LV_ITEM item;
	int num;
	unsigned char* ip = (unsigned char*)&ipaddr;

	PICMP_ECHO_REPLY p = (PICMP_ECHO_REPLY)pReply;

	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アドレスを取得

#pragma warning(push)

	/* Visual C++ 2013 コンパイル時のエラー抑制
	  error C4996 : 'gethostbyaddr' : Use getnameinfo() or GetNameInfoW() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API warnings
	*/
	#pragma warning(disable:4996)
			hostp = gethostbyaddr((const char*)ip, 4, AF_INET);	//	IPアドレスからホスト名を取得

#pragma warning(pop)

			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]);

			EnterCriticalSection(&listview_cs);

			num = listview_max++;

			LeaveCriticalSection(&listview_cs);

			item.mask = LVIF_TEXT | LVIF_PARAM;
			item.pszText = buf;
			item.iItem = num;
			item.iSubItem = 0;
			item.lParam = ipaddr;
			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);

			return TRUE;
		}else
			return FALSE;
	}else{	//	pingに失敗した場合
		return FALSE;
	}
}


HANDLE* hIcmp;
char** cbRequest;
char** pReply;
DWORD cbReply;

//	THREAD_MANに登録する関数  IPアドレスを受け取り、ping_subを呼び出す

DWORD ping_root(LPVOID lParam, int num){
	IPAddr ipaddr=(IPAddr)lParam;
	char* ip = (char*)&lParam;
	ping_sub(hIcmp[num], ipaddr, cbRequest[num], pReply[num], cbReply, TIMEOUT);
	return 0;
}


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

bool ping(HWND hList,unsigned char* ipa,unsigned char end){
	int thread_max = 16;
	thread_man.init( thread_max , ping_root);
	TCHAR buf[128];
	int n;
	bool f=true;

	if(vendor_vec.size()==0){	//	ベンダー情報が読み込まれていないのでファイルまたはインターネットから取得
		//	ファイルが開けない場合、インターネットから取得しファイルに保存後、再度ファイルを開く
		get_mac_url();	//	MACアドレスの一覧をwebから取得しファイルに保存する
	}
	cbReply = sizeof(ICMP_ECHO_REPLY) + BUF_SIZE;
	IPAddr ipaddr;

	listview_max = (int)SendMessage(hList, LVM_GETITEMCOUNT, 0, 0);

	hIcmp = new HANDLE[thread_max];
	cbRequest=new LPSTR[thread_max];
	pReply = new LPSTR[thread_max];

	for (n = 0; n < thread_man.thread_max; n++){
		hIcmp[n] = IcmpCreateFile();
		cbRequest[n]=new char[BUF_SIZE];	//	送信するデータ
		memset(cbRequest[n], 'a', BUF_SIZE);	//	送信するデータを設定
		pReply[n] = new char[cbReply];	//	受信するヘッダー + 受信するデータ
	}
	unsigned char* ip=(unsigned char*)&ipaddr;
	ip[0]=ipa[0];
	ip[1]=ipa[1];
	ip[2]=ipa[2];

	InitializeCriticalSection(&listview_cs);

	if (SendMessage(GetDlgItem(hDlg, IDC_RADIOBOX101), BM_GETCHECK, 0, 0) == BST_CHECKED){
		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);
			thread_man.dispatch((LPVOID)ipaddr);
			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);
		}
	}else{
		::my_ip_ads_mask[::my_ip_sel].ip;
		BYTE* mask = (BYTE*)&::my_ip_ads_mask[::my_ip_sel].mask;
		BYTE* my_ip = (BYTE*)&::my_ip_ads_mask[::my_ip_sel].ip;

		int ip1_v = 0;
		int ip2_v;
		int ip3_v;
		int ip4_v;
		while (ip1_v < ip1_max){
			ip2_v = 0;
			while (ip2_v < ip2_max){
				ip3_v = 0;
				while (ip3_v < ip3_max){
					ip4_v = 0;
					while (ip4_v < ip4_max){
						if (ip1_v == ip1_max - 1 && ip2_v == ip2_max - 1 && ip3_v == ip3_max - 1 && ip4_v == ip4_max - 1){
							//	ブロードキャストアドレスなので終了させる
							break;
						}
						else{
							ip[0] = ip1[ip1_v];
							ip[1] = ip2[ip2_v];
							ip[2] = ip3[ip3_v];
							ip[3] = ip4[ip4_v];

							_stprintf_s(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("%3i.%3i.%3i.%3i PINGを実行しています。"), ip[0], ip[1], ip[2], ip[3]);
							SetWindowText(hLabel1, buf);
							thread_man.dispatch((LPVOID)ipaddr);
							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);
							ip4_v++;

						}

					}
					ip3_v++;
				}
				ip2_v++;
			}
			ip1_v++;
		}

	}
	if(f==true)
		SetWindowText(hLabel1,_TEXT(""));

	ping_run_f = false;	//	スレッドの終了を要求
	thread_man.all_kill();
	thread_man.clear();
	DeleteCriticalSection(&listview_cs);

	for (n = 0; n < thread_man.thread_max; n++){
		IcmpCloseHandle(hIcmp[n]);
		delete[] cbRequest[n];	//	送信するデータ
		delete[] pReply[n];	//	受信するヘッダー + 受信するデータ
	}
	delete[] cbRequest;
	delete[] pReply;
	delete[] hIcmp;

	cbRequest=0;
	pReply=0;
	hIcmp=0;
	ping_run_f = true;
	return f;
}

//	pingをスレッドで実行

DWORD WINAPI  ping_thread(LPVOID lParam){
	ping(hList, ip, ends);
	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);
	EnableWindow(GetDlgItem(hDlg, IDC_SAVE), TRUE);
	EnableWindow(GetDlgItem(hDlg, IDC_ERASE), TRUE);
	return 0;
}


//	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;
	}
	if (vendor_vec.size())
		return _TEXT("error");
	else
		return _TEXT("");
}

//	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;
			}
		}
	}
}

//	リストビューをTAB形式でファイルへ保存する

void ListViewPut(HWND hList, FILE* fp){
	HWND hHeader;
	TCHAR buf[256];
	int rmax = (int)SendMessage(hList, LVM_GETITEMCOUNT, 0, 0);
	hHeader = ListView_GetHeader(hList);
	int cmax = (int)SendMessage(hHeader, HDM_GETITEMCOUNT, 0, 0);
	HDITEM hdi;
	ZeroMemory(&hdi, sizeof(hdi));
	hdi.mask = HDI_TEXT;
	hdi.pszText = buf;
	hdi.cchTextMax = sizeof(buf) / sizeof(TCHAR);
	int c, r;
	for (c = 0; c < cmax; c++){
		SendMessage(hHeader, HDM_GETITEM, c, (LPARAM)&hdi);
		_ftprintf(fp, _TEXT("%s"), buf);
		if (c != cmax - 1)
			_fputtc(_T('¥t'), fp);
	}
	_fputtc(_T('¥n'), fp);
	for (r = 0; r < rmax; r++){
		for (c = 0; c < cmax; c++){
			ListView_GetItemText(hList, r, c, buf, sizeof(buf) / sizeof(TCHAR));
			_ftprintf(fp, _TEXT("%s"), buf);
			if (c != cmax - 1)
				_fputtc(_T('¥t'), fp);
		}
		_fputtc(_T('¥n'), fp);
	}
}

// ファイル名を指定するコモンダイアログを表示

BOOL GetFileName(HWND hWnd, TCHAR* fname, int sz){
	OPENFILENAME o;
	fname[0] = _T('¥0');
	ZeroMemory(&o, sizeof(o));
	o.lStructSize = sizeof(o);
	o.hwndOwner = hWnd;
	o.Flags = 0;
	o.lpstrFile = fname;
	o.nMaxFile = sz;
	o.lpstrFilter = _TEXT("TXT files(*.TXT)¥0¥0");
	o.lpstrDefExt = _TEXT("TXT");
	o.lpstrTitle = _TEXT("テキストファイルを指定");
	o.nFilterIndex = 1;
	return GetSaveFileName(&o);
}

//	エラーコードに対応するメッセージを出力する

void error_msg(HWND hWnd){
	LPVOID lpMsgBuf;

	FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		GetLastError(),
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 既定の言語
		(TCHAR*)&lpMsgBuf,
		0,
		NULL
		);

	MessageBox(hWnd, (TCHAR*)lpMsgBuf, _TEXT("エラー"), MB_OK);

	// バッファを解放する。
	LocalFree(lpMsgBuf);
}

//	マスク値から取りうるIPアドレスの一覧を作成する(1byte)

int set_ip(DWORD x, BYTE* ip){
	DWORD base = 1;
	DWORD bit[8];
	DWORD i = 0, u, n;
	for (n = 0; n<8; n++){
		if ((x & base) == 0){	//	マスクされていないビット
			bit[i++] = base;
		}
		base = base * 2;
	}	//	iに有効なビット数が取得される
	if (i == 0)
		return 0;
	base = 1;
	for (u = 0; u<i; u++)
		base = base * 2;
	int y;
	for (u = 0; u<base; u++){
		y = 0;
		for (n = 0; n<i; n++){
			y += (u & (1 << n)) ? bit[n] : 0;
		}
		ip[u] = y;
	}
	return base;
}

//	自身のIPアドレスを取得

BOOL get_my_ip(void){
	PMIB_IPADDRTABLE pTable = 0;
	DWORD dwSize = 0;
	// GetIpAddrTable()で使用するバッファサイズを取得 
	if (GetIpAddrTable(NULL, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
		pTable = (MIB_IPADDRTABLE *) new BYTE[dwSize];
	}
	else{
		error_msg(hDlg);
		return FALSE;
	}
	if (GetIpAddrTable(pTable, &dwSize, 0) == NO_ERROR) {
		if (0 < pTable->dwNumEntries) {
			for (DWORD i = 0; i < pTable->dwNumEntries; i++) {
				IP_ADS_MASK t;
				if (pTable->table[i].dwAddr != 0x0100007f){	//	127.0.0.1
					t.ip = pTable->table[i].dwAddr;
					t.mask = pTable->table[i].dwMask;
					my_ip_ads_mask.push_back(t);
				}
			}
		}
	}
	else{
		error_msg(hDlg);
		delete[](MIB_IPADDRTABLE *)pTable;
		return FALSE;
	}
	delete[](MIB_IPADDRTABLE *)pTable;
	return TRUE;
}

//	THREAD_MANから指定個数起動されるスレッド
//	IPアドレスが発行されシグナル状態となるとユーザー定義関数を呼び出し終了を待ちイベントを非シグナルにする。

DWORD WINAPI run_thread(LPVOID lParam){	//	起動されるスレッド
	THREAD_MAN_CREATE* cp = (THREAD_MAN_CREATE*)lParam;
	THREAD_MAN* tmp = (THREAD_MAN*)cp->thread_man;
	int num = cp->num;

	DWORD ret;
	//	データーが投入されるまで待機
	for (;;){
		if (ping_run_f == false)
			return 0;
		ret = WaitForSingleObject(tmp->run_event[num], 200);//	0.2秒待機
		if (ret == WAIT_OBJECT_0){
			(*tmp->func)(tmp->data[num], num);	//	ユーザー定義関数
		}
		else{
			Sleep(50);
			continue;
		}
		ResetEvent(tmp->run_event[num]);	//	非シグナル状態に設定
	}
	return 0;
}


//	DLL内の関数へのポインタ型を定義
typedef void (WINAPI *RtlGetVersion_FUNC)(OSVERSIONINFOEXW*);

int win10ver2(void){
	int ver = 0;
	HMODULE hMod;
	OSVERSIONINFOEXW osw;
	hMod = LoadLibrary(TEXT("ntdll.dll"));
	RtlGetVersion_FUNC func;
	if (hMod){
		func = (RtlGetVersion_FUNC)GetProcAddress(hMod, "RtlGetVersion");
		if (func == 0){
			FreeLibrary(hMod);
			return FALSE;
		}
		ZeroMemory(&osw, sizeof(osw));
		osw.dwOSVersionInfoSize = sizeof(osw);
		func(&osw);
		FreeLibrary(hMod);
		if (osw.dwMajorVersion == 10){
			switch (osw.dwBuildNumber){
			case 10240: ver = 1507; break;
			case 10586: ver = 1511; break;
			case 14393: ver = 1607; break;
			case 15063: ver = 1703; break;
			case 16299: ver = 1709; break;
			case 17134: ver = 1803; break;
			case 17763: ver = 1809; break;
			default: ver = 1809; break;
			}
			return ver;
		}
	}
	return -1;
}

resource.h


#define APL_NAME "GUIPING"
#define APL_NAME0 "GUIPING"
#define FILE_VERSION 1,1,0,0
#define VERSION_NUMBER  "1, 1, 0,0"
#define VERSION_NUMBER0 "1, 1, 0,0\0"
#define COPYRIGHT "Copyright (C) 2015/08/25"
#define COPYRIGHT0 "Copyright (C) 2015/08/25"
#define DESCRIPTION  "キラー"
#define DESCRIPTION0  "キラー\0"
#define APL_VER "GUIPing Version  1, 1, 0,0"


#define IDC_IPADDRE     100     //      IPアドレス
#define IDC_END_ADR_EDIT 110    //
#define IDC_MY_IP_EDIT  120     //      終了IPアドレス
#define IDC_MY_MASK_EDIT        130     //      終了IPアドレス
#define IDC_LISTVIEW1   140     //      リストビュー
#define IDC_LABEL1      150     //      ラベル
#define IDC_COMBOBOX 160

#define IDC_BUFSIZE 165
#define IDC_TIMEOUT 167

#define IDC_SAVE        170     //      保存
#define IDC_ERASE       180     //      リストビュー消去
#define IDC_QUIT        190     //      終了

#define IDC_RADIOBOX101 200
#define IDC_RADIOBOX102 210

#define IDC_RENGE_LABEL 220
#define IDC_MY_MASK_LABEL 230

resource.rc

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

DLG1 DIALOG DISCARDABLE 0, 0, 494, 230
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP | WS_CAPTION | DS_SETFONT
CAPTION "guiping"
FONT 9, "MS Shell Dlg"
{
 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_END_ADR_EDIT, "EDIT", WS_CHILD | WS_DLGFRAME | WS_VISIBLE | ES_AUTOHSCROLL | ES_NUMBER, 130, 28, 32, 14
 CONTROL "このPCのIPアドレス", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 169, 14, 64, 14
 CONTROL "", IDC_COMBOBOX, "COMBOBOX", WS_CHILD | WS_VISIBLE | WS_VSCROLL | CBS_DROPDOWNLIST, 169, 28, 64, 100
 CONTROL "サブネットマスク", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 240, 14, 64, 14
 CONTROL "", IDC_MY_MASK_EDIT, "EDIT", WS_CHILD | WS_DLGFRAME | WS_VISIBLE | ES_AUTOHSCROLL, 240, 28, 64, 14

 CONTROL "範囲で検索", IDC_RADIOBOX101, "BUTTON", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON | WS_GROUP, 310,14, 96, 14
 CONTROL "", IDC_RENGE_LABEL, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 406, 14, 32, 14
 CONTROL "サブネットマスクで検索", IDC_RADIOBOX102, "BUTTON", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, 310, 28, 96, 14
 CONTROL "", IDC_MY_MASK_LABEL, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 406, 28, 32, 14

 CONTROL "サイズ", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 310,49,24 , 14
 CONTROL "", IDC_BUFSIZE, "EDIT", WS_CHILD | WS_DLGFRAME | WS_VISIBLE | ES_AUTOHSCROLL | ES_NUMBER, 334, 45, 24, 14

 CONTROL "タイムアウト", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 365, 49, 48, 14
 CONTROL "", IDC_TIMEOUT, "EDIT", WS_CHILD | WS_DLGFRAME | WS_VISIBLE | ES_AUTOHSCROLL | ES_NUMBER, 415, 45, 24, 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実行(&P)", IDOK, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 159, 198, 60, 18
 CONTROL "キャンセル(&C)", IDCANCEL, "BUTTON", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 226, 198, 60, 18
 CONTROL "保存(&S)", IDC_SAVE, "BUTTON", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 293, 198, 60, 18
 CONTROL "クリア(&E)", IDC_ERASE, "BUTTON", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 360,198, 60, 18

 CONTROL "終了(&Q)", IDC_QUIT, "BUTTON", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 427, 198, 60, 18
}


//--------------------
//  ヴァージョン情報
//--------------------

VS_VERSION_INFO VERSIONINFO
FILEVERSION FILE_VERSION
PRODUCTVERSION FILE_VERSION
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments", "GUIPING¥0"
VALUE "CompanyName", DESCRIPTION0
VALUE "FileDescription", "PING¥0"
VALUE "FileVersion", VERSION_NUMBER0
VALUE "InternalName", "¥0"
VALUE "LegalCopyright", COPYRIGHT0
VALUE "OriginalFilename", "guiping2.exe¥0"
VALUE "ProductName", APL_NAME0
VALUE "ProductVersion", VERSION_NUMBER0
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0411, 0x03A4
END
END

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

ダウンロード guiping2.zip(79.0kByte)

ZIPファイルに含まれるファイル
guiping2.cpp
resource.h
resource.rc
guiping2.exe