주의 사항 원래 코드 파일인 C로는 컴파일되는 코드지만 CPP 로 하면 안되는 부분이 있습니다. GetProcAddress 와 FreeLibrary 인 곳인데, 첫번째 인수가 struct HISTANCE 형이여야 합니다. C 코드는 그냥 void 포인터로 넣어도 문제가 없는데 CPP 에서는 형검사(타입 체킹)을 하기 때문에 문제가 발생합니다.
cannot convert parameter 1 from 'void *' to 'struct HINSTANCE__ *'
(HISTANCE)를 붙여서 강제 형변환(type-casting)을 하면 해결할수 있습니다. GetProcAddress(hndlIcmp, "IcmpCreateFile"); -> GetProcAddress((HINSTANCE)hndlIcmp, "IcmpCreateFile"); FreeLibrary(hndlIcmp); -> FreeLibrary((HINSTANCE)hndlIcmp);
C 에서는 문제가 없습니다.
실행 화면
코드 내용
/*
저작권
http://www.sockaddr.com/ExampleSourceCode.html
http://cakel.tistory.com
교육용을 전제로 자유롭게 사용할수 있습니다.
PingI.cpp -- 마이크로소프트 ICMP API 를 사용한 간단한 ping 프로그램
사용법 : pingi.exe [호스트 주소]
*/
#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <string.h>
#pragma comment(lib,"wsock32.lib")
typedef struct tagIPINFO
{ // IP 정보 태그 관련 구조체 입니다.
u_char Ttl; // Time To Live
u_char Tos; // Type Of Service
u_char IPFlags; // IP 플래그
u_char OptSize; // 옵션 데이터의 크기
u_char FAR *Options; // 옵션 데이터 버퍼
}IPINFO, *PIPINFO;
typedef struct tagICMPECHO
{ // 에코 요청 자료의 구조체
u_long Source; // 원본 주소
u_long Status; // IP 상태
u_long RTTime; // 순회 시간(Round Trip Time : RTT) ms
u_short DataSize; // 응답 데이터 크기
u_short Reserved; // 알수 없음
void FAR* pData; // 응답 데이터 버퍼
IPINFO ipInfo; // 응답 옵션
}ICMPECHO, *PICMPECHO;
// ICMP.DLL 출력 함수 포인터
HANDLE (WINAPI* pIcmpCreateFile)(VOID);
BOOL (WINAPI* pIcmpCloseHandle)(HANDLE);
DWORD (WINAPI* pIcmpSendEcho)
(HANDLE, DWORD, LPVOID, WORD, PIPINFO, LPVOID, DWORD, DWORD);
//
// main 함수 부분입니다.
int main(int argc, char* *argv)
{
WSADATA wsaData; // WinSock Applicatoin DATA
ICMPECHO icmpEcho; // Internet Control Message Protocol 에코 응답 버퍼
HANDLE hndlIcmp; // ICMP.DLL 를 다룰 LoadLibrary() 함수의 핸들
HANDLE hndlFile; // IcmpCreateFile() 함수의 핸들
LPHOSTENT pHost; // 호스트 객체 구조를 가리키는 포인터
struct in_addr iaDest; // 인터넷 주소 구조체
DWORD *dwAddress; // IP 주소
IPINFO ipInfo; // IP 옵션 구조
int nRet; // 일반적으로 응답 코드로 사용
DWORD dwRet; // DWORD(더블워드형) 응답 코드
int x;
// 인수 확인
if(argc != 2)
{
fprintf(stderr,"\n사용법: pingi [호스트 또는 IP주소]\n");
return -1;
}
// 동적으로 ICMP.DLL 을 코드 수행시 바로 불러옵니다.
hndlIcmp = LoadLibrary("ICMP.DLL");
if(hndlIcmp == NULL)
{
fprintf(stderr,"\nICMP.DLL 파일을 불러오지 못했습니다.\n");
return -1;
}
// ICMP 함수 포인터를 반환 받습니다.
pIcmpCreateFile = ( HANDLE (WINAPI*)(void) )GetProcAddress((HINSTANCE)hndlIcmp, "IcmpCreateFile");
pIcmpCloseHandle = ( BOOL (WINAPI* )(HANDLE) )GetProcAddress((HINSTANCE)hndlIcmp, "IcmpCloseHandle");
pIcmpSendEcho = (DWORD (WINAPI* )(HANDLE, DWORD, LPVOID, WORD, PIPINFO, LPVOID, DWORD, DWORD))
GetProcAddress((HINSTANCE)hndlIcmp,"IcmpSendEcho");
//모든 함수 포인터를 확인합니다.
if(pIcmpCreateFile == NULL || pIcmpCloseHandle == NULL || pIcmpSendEcho == NULL)
{
fprintf(stderr,"\nICMP 프로시져 주소를 찾는데 문제가 생겼습니다.\n");
FreeLibrary((HINSTANCE)hndlIcmp);
return -1;
}
// 윈속을 시작 합니다.
nRet = WSAStartup(0x0101, &wsaData); // 귀찮아서 MAKEWORD(1,1)을 쓰지 않고 바로 0x0101 로 WORD 형 자료를 완성합니다.
if(nRet)
{
fprintf(stderr,"\nWSAStartup() 오류 발생했습니다. 코드 번호 : %d\n", nRet);
WSACleanup();
FreeLibrary((HINSTANCE)hndlIcmp);
return -1;
}
// 윈속 버전을 확인 합니다.
if (0x0101 != wsaData.wVersion)
{
fprintf(stderr,"\n이 컴퓨터의 윈속이 버전 1.1을 지원하지 않습니다.\n");
WSACleanup();
FreeLibrary((HINSTANCE)hndlIcmp);
return -1;
}
// 목적지를 검색합니다.
// inet_addr() 함수로 호스트 명인지 호스트 주소인지 검사 합니다.
iaDest.s_addr = inet_addr(argv[1]);
if(iaDest.s_addr == INADDR_NONE) // IP주소가 아닌 경우
pHost = gethostbyname(argv[1]);
else // IP 주소인 경우
pHost = gethostbyaddr( (const char*)&iaDest, sizeof(struct in_addr), AF_INET);
if (pHost == NULL) // 목적지 호스트에 도달하지 못한 경우
{
fprintf(stderr,"\n%s 호스트를 찾을수 없었습니다.\n", argv[1]);
WSACleanup();
FreeLibrary((HINSTANCE)hndlIcmp);
return -1;
}
// 사용자에게 우리가 무얼 할지 가르쳐 줍니다.
printf("%s [%s] 호스트 로 핑(Ping)합니다.", pHost->h_name, inet_ntoa( (*(LPIN_ADDR)pHost->h_addr_list[0])) );
// IP 주소를 복사 합니다.
dwAddress = (DWORD*) (*pHost->h_addr_list);
// ICMP 에코 요청 핸들을 가집니다.
hndlFile = pIcmpCreateFile();
for(x = 0; x < 4; x++) // 4번 재시도 합니다.
{
// 그럴싸한 기본 값을 설정합니다.
ipInfo.Ttl = 255;
ipInfo.Tos = 0;
ipInfo.OptSize = 0;
ipInfo.Options = NULL;
// icmpEcho.ipInfo.Ttl = 256;
// ICMP 에코 요청을 합니다.
dwRet = pIcmpSendEcho(
hndlFile, // IcmpCreateFile() 함수에서 가저온 핸들
*dwAddress, // 목적지 IP 주소
NULL, // 보낼 버퍼를 가리키는 포인터
0, // 보낼 버퍼의 바이트 길이
&ipInfo, // 요구 옵션
&icmpEcho, // 응답 버퍼
sizeof(struct tagICMPECHO), // 에코 보낼 자료의 주소 길이
5000); // 기타릴 시간 밀리초(천분의 1초 => 5초)
// 결과를 출력합니다.
iaDest.s_addr = icmpEcho.Source;
printf("\n %s 에서 응답이 왔습니다. 시간 = %ldms TTL=%d",
inet_ntoa(iaDest),
icmpEcho.RTTime,
icmpEcho.ipInfo.Ttl);
if(icmpEcho.Status)
{
printf("\n오류 : icmpEcho.Status 상태코드 = %ld", icmpEcho.Status);
break;
}
}
printf("\n");
// 요구 했던 파일 핸들을 닫습니다.
pIcmpCloseHandle(hndlFile);
FreeLibrary((HINSTANCE)hndlIcmp);
WSACleanup();
return 0;
}