非同期名前解決ダイアログ

ここで作るプログラムは次のようなダイアログプログラムです。


今回作るプログラム - 非同期名前解決プログラムのダウンロード

これはどのようなものかと言うと、使い方は次の通りです。

  1. Host name に名前解決したいホスト名を入力する。(例えば www.microsoft.com)
  2. Resolve ボタンをクリックする
  3. その結果が表示される

というものです。

名前解決というと、ホスト名から IP アドレスを引いてくるだけのものかと思ってしまいますが、 (もちろん、ポイントはそこですが) API を呼び出した結果ではエイリアスなども引いてきていることが、上の結果からわかると思います。

ここで使っているのは、WSAAsyncGetHostByName 関数です。

HANDLE WSAAsyncGetHostByName(
  __in   HWND hWnd,
  __in   unsigned int wMsg,
  __in   const char *name,
  __out  char *buf,
  __in   int buflen
);

この第4引数の buf に結果が返りますが、この時のデータが次のような HOSTENT 型として返ります。

typedef struct hostent {
  char FAR *     h_name;
  char FAR  FAR **h_aliases;
  short         h_addrtype;
  short         h_length;
  char FAR  FAR **h_addr_list;
} HOSTENT, *PHOSTENT, FAR *LPHOSTENT;

パッと見、ちょっとわかりにくいように感じると思いますので、実際にどんなデータが入っているか見てみよう、というのがこのプログラムの狙いです。

尚、お気づきと思いますが名前解決をするのに使用している WSAAsyncGetHostByName 関数は、 Async と付いているように非同期 (Asynchronous) 関数です。

そのため、Resolve ボタンを押した後に待たされることなく、なんとなく軽快に動くような感じがすると思います。

非同期名前解決のサンプルコード

それでは実際に上記のコードを示します。

いきなり、最初にイイワケさせていただくと(苦笑)、かなり昔に書いたコードで、 今見ると直したいと思う箇所がちょこちょこ見受けられます。(ちなみに 2001年11月14日 に書いてます)

ご丁寧に TCHAR 使ってますが、でも Unicode ビルドできなかったりします (笑) WSAAsyncGetHostByName 関数が char* を受け取ったりするんですが、その辺の変換までは入れ込んでないってことです。 直したい方は自分で直してください。

と、言い訳もしたところでコードはこちらです。

まずは以下のコードを HostentTest.h として保存してください。

#pragma once

#include <windows.h>
#include <windowsx.h>


///////////////////////////////////////////////////////////////////////////////


#define HANDLE_DLG_MSG(hwnd, msg, fn) \
     case (msg): \
         return SetDlgMsgResult(hwnd, msg, HANDLE_##msg(hwnd, wParam, lParam, fn)) 



#define HANDLE_SM_GETHOSTBYNAME(hwnd, wParam, lParam, fn) \
     (LRESULT)(DWORD)(UINT)(BOOL)(fn)((hwnd), (HANDLE)(wParam), lParam)



#define  GET_ERROR_RESOURCEID(msg, x) \
     case (msg): (x) = IDS_##msg; break


///////////////////////////////////////////////////////////////////////////////


#define SM_GETHOSTBYNAME (WM_USER+5001)


///////////////////////////////////////////////////////////////////////////////


BOOL InitializeGlobalVariables(HWND hWnd);
void ClearControls(HWND hWnd);

次に次のコードを HostentTest.cpp として保存します。

#include <winsock2.h>
#include "HostentTest.h"
#include <tchar.h>
#include <stdio.h>
#include "resource.h"


///////////////////////////////////////////////////////////////////////////////
//
// global variables.
//


WSADATA g_wsaData;
HANDLE g_hTask = NULL;
TCHAR g_HostEntry[MAXGETHOSTSTRUCT];


///////////////////////////////////////////////////////////////////////////////


BOOL CallGetHostByName(HWND hWnd) {

     TCHAR szName[50];
     Edit_GetText(GetDlgItem(hWnd, IDC_HOSTNAME), szName, 50);

     ::ZeroMemory(&g_HostEntry, MAXGETHOSTSTRUCT);

     g_hTask = WSAAsyncGetHostByName(
          hWnd,
          SM_GETHOSTBYNAME, 
          szName, 
          g_HostEntry, 
          MAXGETHOSTSTRUCT );

     if(!g_hTask) {
          return FALSE;
     }

     return TRUE;

}


///////////////////////////////////////////////////////////////////////////////


BOOL OnGetHostEntry(HWND hWnd, HANDLE hTaskHandle, LPARAM lParam) {

     INT nError = WSAGETASYNCERROR(lParam); // Get an error code from lParam.

     if(nError == 0) { //success
     
          Static_SetText(GetDlgItem(hWnd, IDC_STATUS), TEXT("Found!"));

          INT i;
          TCHAR szBuff[100];
          HOSTENT* pHostEntry = (HOSTENT*) g_HostEntry;
          
          Static_SetText(GetDlgItem(hWnd, IDC_HNAME), pHostEntry->h_name);
          for(i=0; pHostEntry->h_aliases[i] != NULL; i++) {
               ListBox_AddString(GetDlgItem(hWnd, IDC_HALIASES), pHostEntry->h_aliases[i]);
          }

          _stprintf(szBuff, "%d", pHostEntry->h_addrtype);
          Static_SetText(GetDlgItem(hWnd, IDC_HADDRTYPE), szBuff);
          _stprintf(szBuff, "%d", pHostEntry->h_length);
          Static_SetText(GetDlgItem(hWnd, IDC_HLENGTH), szBuff);

          for(i=0; pHostEntry->h_addr_list[i] != NULL; i++) {
               IN_ADDR* pInAddr = NULL;
               pInAddr = (IN_ADDR*) pHostEntry->h_addr_list[i]; //network byte order
               LPCTSTR lpszDotAddr = inet_ntoa(*pInAddr);
               
               ListBox_AddString(
		GetDlgItem(
			hWnd, 
			IDC_HADDRLIST), 
			lpszDotAddr ? lpszDotAddr : TEXT(""));
          }

     }
     else {

          TCHAR szBuff[255];
          INT nStringID = 0;
          switch(nError) {
          GET_ERROR_RESOURCEID(WSAENETDOWN, nStringID);
          GET_ERROR_RESOURCEID(WSAENOBUFS, nStringID);
          GET_ERROR_RESOURCEID(WSAEFAULT, nStringID);
          GET_ERROR_RESOURCEID(WSAHOST_NOT_FOUND, nStringID);
          GET_ERROR_RESOURCEID(WSATRY_AGAIN, nStringID);
          GET_ERROR_RESOURCEID(WSANO_RECOVERY, nStringID);
          GET_ERROR_RESOURCEID(WSANO_DATA, nStringID);
          }
          if(nStringID) {
               LoadString((HINSTANCE) GetWindowLongPtr(hWnd, GWL_HINSTANCE),
                    nStringID, szBuff, 255);
          }
          else {
               _stprintf(szBuff, TEXT("Unexpected error has occured."));
          }
          Static_SetText(GetDlgItem(hWnd, IDC_STATUS), szBuff);

     }

     InitializeGlobalVariables(hWnd);
     
     return TRUE;
}


///////////////////////////////////////////////////////////////////////////////


inline void SetButtonTextInSearch(HWND hWnd, BOOL bInSearch) {

     HWND hButton = GetDlgItem(hWnd, IDC_RESOLVE);
     if(bInSearch) {
          Button_SetText(hButton, TEXT("C&ancel"));
     }
     else {
          Button_SetText(hButton, TEXT("&Resolve"));
     }
}


///////////////////////////////////////////////////////////////////////////////
//
// Initialize global variables except g_wsaData;
//


BOOL InitializeGlobalVariables(HWND hWnd) {

     ::ZeroMemory(g_HostEntry, MAXGETHOSTSTRUCT);
     g_hTask = NULL;
     SetButtonTextInSearch(hWnd, FALSE);
     return TRUE;
}


///////////////////////////////////////////////////////////////////////////////
//
// Initialize a main dialog box
//


BOOL OnInitDialog(HWND hWnd, HWND hWndFocus, LPARAM lParam) {

     //Set text
     Edit_SetText(GetDlgItem(hWnd, IDC_HOSTNAME), TEXT("www.microsoft.com"));

     //Set icon
     SendMessage(hWnd, WM_SETICON, TRUE,  (LPARAM) 
          LoadIcon((HINSTANCE) GetWindowLongPtr(hWnd, GWLP_HINSTANCE), 
          MAKEINTRESOURCE(IDI_HOSTENTTEST)));
    
     return TRUE;
}


///////////////////////////////////////////////////////////////////////////////


void CancelSearching(HWND hWnd) {

     WSACancelAsyncRequest(g_hTask);

}


///////////////////////////////////////////////////////////////////////////////
//
// Handling WM_COMMAND Message
//


void OnCommand(HWND hWnd, int nID, HWND hWndCtl, UINT codeNotify) {

     switch(nID) {
     case IDC_RESOLVE:
          
          if(g_hTask) {
               CancelSearching(hWnd);
               InitializeGlobalVariables(hWnd);
               SetButtonTextInSearch(hWnd, FALSE);
               Static_SetText(GetDlgItem(hWnd, IDC_STATUS), 
                    TEXT("The request has been successfully canceled."));
          }
          else {
               ClearControls(hWnd);
               CallGetHostByName(hWnd);
               SetButtonTextInSearch(hWnd, TRUE);
               Static_SetText(GetDlgItem(hWnd, IDC_STATUS), 
                    TEXT("Searching..."));
          }
          
          break;

     case IDCANCEL:

          EndDialog(hWnd, 0);
          
          break;

     }
}


///////////////////////////////////////////////////////////////////////////////
//
// Main Window Procedure
//


BOOL CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {

     switch(uMsg) {
          HANDLE_DLG_MSG(hWnd, WM_INITDIALOG, OnInitDialog);
          HANDLE_DLG_MSG(hWnd, WM_COMMAND, OnCommand);
          HANDLE_DLG_MSG(hWnd, SM_GETHOSTBYNAME, OnGetHostEntry);
     }

     return FALSE;
}


///////////////////////////////////////////////////////////////////////////////
//
// WinSock ��������
//


BOOL InitializeWinSock() {

     ::ZeroMemory(&g_wsaData, sizeof(WSADATA));
     WORD wVersionRequested = WINSOCK_VERSION;

     WORD nRet = WSAStartup(
          wVersionRequested,
          &g_wsaData);

     return nRet == 0 ? TRUE: FALSE;
}


///////////////////////////////////////////////////////////////////////////////
//
// Clear all controls
//


void ClearControls(HWND hWnd) {

     Static_SetText(GetDlgItem(hWnd, IDC_HNAME), TEXT(""));
     ListBox_ResetContent(GetDlgItem(hWnd, IDC_HALIASES));
     Static_SetText(GetDlgItem(hWnd, IDC_HADDRTYPE), TEXT(""));
     Static_SetText(GetDlgItem(hWnd, IDC_HLENGTH), TEXT(""));
     ListBox_ResetContent(GetDlgItem(hWnd, IDC_HADDRLIST));

}


///////////////////////////////////////////////////////////////////////////////
//
// WinMain
//


int APIENTRY WinMain(HINSTANCE hInstance,
                      HINSTANCE hPrevInstance,
                      LPSTR     lpCmdLine,
                      int       nCmdShow ) {

     if(!InitializeWinSock()) {
          ::MessageBox(NULL, TEXT("WinSock is not available."),
               TEXT("Error"), MB_OK|MB_ICONERROR);
          return 0;
     }
     
     ::DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAINWND), NULL, MainWndProc);


     WSACleanup();

     return 0;
}

リソーススクリプト HostentTest.rc はこちら。ダイアログのサイズ・配置など好きに書き換えてください。 (リソースファイルの書き換えには ResEdit などが良いです。 http://www.resedit.net/ 私は Visual Studio は余計なコードを書き込むので好きじゃありません... 全く... 何でコードにデザイナーの情報を書き入れるんだ...ブツブツ)

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


//
// Dialog resources
//
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_MAINWND DIALOGEX 0, 0, 226, 203
STYLE DS_CENTER | DS_MODALFRAME | WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_POPUP | WS_SYSMENU
CAPTION "Hostent Test"
FONT 10, "MS Sans Serif", 400, 0, 0
BEGIN
    PUSHBUTTON      "&Close", IDCANCEL, 169, 182, 50, 14
    EDITTEXT        IDC_HOSTNAME, 47, 7, 118, 14, ES_AUTOHSCROLL
    LTEXT           "Host name:", IDC_STATIC, 7, 10, 36, 10
    DEFPUSHBUTTON   "&Resolve", IDC_RESOLVE, 171, 7, 48, 14
    GROUPBOX        "Result (The content of Hostent structure)", IDC_STATIC, 7, 39, 212, 135
    LTEXT           "Host name (h_name) :", IDC_STATIC, 13, 53, 64, 10
    LTEXT           "", IDC_HNAME, 78, 51, 133, 12, SS_SUNKEN
    LTEXT           "Alias (h_aliases) :", IDC_STATIC, 13, 66, 55, 10
    LISTBOX         IDC_HALIASES, 78, 65, 134, 23, WS_TABSTOP | WS_VSCROLL | LBS_NOINTEGRALHEIGHT | LBS_SORT
    LTEXT           "Address Type (h_addrtype) : ", IDC_STATIC, 14, 95, 83, 9
    LTEXT           "", IDC_HADDRTYPE, 98, 93, 114, 13, SS_SUNKEN
    LTEXT           "Length (h_length) : ", IDC_STATIC, 14, 109, 70, 10
    LTEXT           "", IDC_HLENGTH, 98, 107, 114, 13, SS_SUNKEN
    LTEXT           "Address List (h_addr_list) :", IDC_STATIC, 14, 123, 83, 10
    LISTBOX         IDC_HADDRLIST, 80, 135, 132, 34, WS_TABSTOP | WS_VSCROLL | LBS_NOINTEGRALHEIGHT | LBS_SORT
    LTEXT           "", IDC_STATUS, 47, 24, 172, 12, SS_SUNKEN
    LTEXT           "Status:", IDC_STATIC, 7, 25, 35, 10
END


//
// Icon resources
//
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDI_HOSTENTTEST    ICON           "icon1.ico"


//
// String Table resources
//
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
STRINGTABLE
BEGIN
    IDS_WSAENETDOWN       "The network subsystem has failed."
    IDS_WSAENOBUFS        "Insufficient buffer space is available."
    IDS_WSAEFAULT         "The name or buf parameter is not in a valid part of the process address space."
    IDS_WSAHOST_NOT_FOUND "Authoritative answer host not found."
    IDS_WSATRY_AGAIN      "A nonauthoritative host not found, or SERVERFAIL."
    IDS_WSANO_RECOVERY    "Nonrecoverable errors, FORMERR, REFUSED, NOTIMP."
    IDS_WSANO_DATA        "Valid name, no data record of requested type."
END

resource.h はこちら

#ifndef IDC_STATIC
#define IDC_STATIC (-1)
#endif

#define IDD_MAINWND                             101
#define IDI_HOSTENTTEST                         102
#define IDC_HOSTNAME                            1001
#define IDC_RESOLVE                             1002
#define IDC_HNAME                               1003
#define IDC_HALIASES                            1004
#define IDC_HADDRTYPE                           1005
#define IDC_HLENGTH                             1006
#define IDC_HADDRLIST                           1007
#define IDC_STATUS                              1008
#define IDS_WSAENETDOWN                         40000
#define IDS_WSAENOBUFS                          40001
#define IDS_WSAEFAULT                           40002
#define IDS_WSAHOST_NOT_FOUND                   40003
#define IDS_WSATRY_AGAIN                        40004
#define IDS_WSANO_RECOVERY                      40005
#define IDS_WSANO_DATA                          40006

プログラムのアイコンはこちら アイコンのダウンロード。 これも適当に書いてますので、適当に差し替えてくださいませ。

makefile は次の通りです。

CPP=cl.exe
MTL=midl.exe
RSC=rc.exe
LINK32=link.exe

TARGETNAME=hostenttest
OUTDIR=.\chk

ALL : "$(OUTDIR)\$(TARGETNAME).exe"

"$(OUTDIR)" :
    @if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"

CPP_PROJ=\
	/MT\
	/W3\
	/Fo"$(OUTDIR)\\"\
	/Fd"$(OUTDIR)\\"\
	/c\
	/Zi

RSC_PROJ=\
	/l\
	0x411\
	/fo"$(OUTDIR)\$(TARGETNAME).res"\
	/d "_DEBUG" 

LINK32_FLAGS=\
	user32.lib\
	ws2_32.lib\
	/subsystem:windows\
	/pdb:"$(OUTDIR)\$(TARGETNAME).pdb"\
	/machine:I386\
	/out:"$(OUTDIR)\$(TARGETNAME).exe"\
	/DEBUG
	
LINK32_OBJS= \
	"$(OUTDIR)\$(TARGETNAME).obj"\
	"$(OUTDIR)\$(TARGETNAME).res"


"$(OUTDIR)\$(TARGETNAME).exe" : "$(OUTDIR)" $(LINK32_OBJS)
    $(LINK32) $(LINK32_FLAGS) $(LINK32_OBJS)


.cpp{$(OUTDIR)}.obj:
   $(CPP) $(CPP_PROJ) $<


.rc{$(OUTDIR)}.res::
	$(RSC) $(RSC_PROJ) $<

以上、これで nmake すればビルドできると思います。

尚、コード内で用いている HANDLE_DLG_MSG マクロについては当サイト内メッセージクラッカの記事を参照してください。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Web/DB プログラミング徹底解説