[펌: http://blog.naver.com/hsshee?Redirect=Log&logNo=70037637328]


간혹 어떤 프로그램에서 LoadLibrary와 GetProcAddress API를 사용하여,

어떤 dll 내의 함수를 사용하는 경우가 있다.

 

LoadLibrary와 GetProcAddress의 원형을 살펴보자.

 

◈ HMODULE LoadLibrary( LPCTSTR lpFileName );

> 인자로 준 DLL을 현재 프로세스의 주소공간으로 mapping 시켜서 사용할 수 있도록 해주는 API.

 

◈ FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName);

> DLL에서 Export한 함수의 번지를 찾아서 그 함수를 사용할 수 있도록 그 함수의 포인터를 리턴해 주는 API.

 

 

1. DLL 내의 함수를 사용하기 위해서는,

먼저 LoadLibrary를 이용하여 DLL의 모듈 핸들을 리턴 받는다.

( HMODULE or HINSTANCE와 같은 핸들을 통해 리턴받으면 된다. )

 

인자는 DLL의 경로를 문자열로 넘겨주면 되고, 끝에 널문자가 있어야 한다는 걸 잊지 말자.

 

참고로, 반드시 리턴된 모듈 핸들이 NULL이 아닌지를 체크하는 습관을 들이도록 하자.

 

 

2. 유효한 모듈 핸들을 가지고,

GetProcAddress를 이용하여 DLL에서 Export한 함수를 가져오자.

 

이 때의 인자는 LoadLibrary를 통해서 리턴 받은 모듈 핸들과

그 DLL 내에서 사용하기 원하는 함수의 이름을 문자열로 넘겨준다. 이 때에도 끝에 널문자가 있어야 한다.

 

GetProcAddress를 수행하면 프로그램에서 선언한 함수포인터 변수에

DLL에서 Export한 함수의 주소가 리턴되어 이 프로그램에서 선언한 변수를 통하여

DLL 내의 해당 함수를 사용할 수 있게 되는 것이다.

 

만약에 인자가 정확하게 들어가지 않았다면, 역시 NULL이 리턴된다.

 

 

3. LoadLibrary와 같이 모듈 핸들을 받아서 DLL을 사용하였을 경우,

FreeLibrary API를 이용하는 습관을 들이도록 하자.

 

◈ BOOL FreeLibrary(HMODULE hLibModule);

>해당 DLL의 사용 카운트를 1 감소시키고 사용 카운트가 0이 되었을 때에 메모리에서 해당 DLL을 삭제해준다.

   리턴형은 BOOL인데, 이 때에 DLL을 삭제하는 데에 성공했다면 TRUE를, 실패했다면 FALSE를 리턴하는 것이다.

 
4. GetProcAddress에서 NULL이 리턴되는 경우 중,

Name Mangling으로 인한 경우가 있다. 이 때의 해결책은 다음과 같다.

 

DLL 프로그래밍한 프로젝트에서 export하는 함수를 선언하는 헤더에 다음과 같이 추가하여 Rebuild하면 된다.

 

 cpp dll 을 c type으로 바꿔주는 코드

 

#ifdef __cplusplus
extern "C"
{
#endif

 

// export할 함수

 

#ifdef __cplusplus
}
#endif

 

 

어떤 함수를 위의 코드를 이용하지 않고 빌드한 경우와 위의 코드를 추가하여 빌드한 경우에

각각의 dll파일을 Peview를 통하여 어떻게 함수명이 저장되는지 확인해보자.

 

원하는 함수명은 GetOpcodeFirstByte이다.

 

1) 위의 코드를 이용하지 않고 빌드한 경우,

 

2) 위의 코드를 추가하여 빌드한 경우,

 

1)의 경우에 GetProcAddress에서 Export할 함수의 문자열을 "GetOpcodeFisrtByte"로 넘겨주면,

실제 dll의 export된 함수명과 다르기 때문에 NULL이 리턴된다.

 

※ 위의 코드를 추가하는 습관을 들이도록 하자.

'NativeCode > api' 카테고리의 다른 글

printf, scanf 함수 포맷 문자열  (0) 2010.07.07
IP, TCP, UDP, ICMP Checksum 계산  (0) 2010.07.05
IpHlpApi MSDN  (0) 2010.07.04
SendARP  (0) 2010.07.03
[팁] Heap 메모리 검증하기.. | VC++ 일반  (0) 2010.05.09

출처 : http://blog.naver.com/pointer98/150046033124

포맷문자열

사용해야 인자타입

실제 내부 사용 변환 타입

기본진법

정밀도

%c 

int x

(unsigned char)x 

1바이트 문자

%lc 

wint_t x

wchar_t a[2] = {x} 

2바이트 확장 문자

%d

int x

(int)x 

10 

1 

%hd 

int x

(short)x 

10 

1 

%ld 

long x

(long)x 

10 

1 

%lld 

long long int x

(long long int)x

10

1

%i 

int x

(int)x 

10 

1 

%hi 

int x

(short)x 

10 

1 

%li 

long x

(long)x 

10 

1 

%lli 

long long int x

(long long int)x

10

1

%f 

double x

(double)x 

10 

6

%Lf 

long double x

(long double)x 

10 

6 

%F

double x

(double)x 

10 

6 

%LF

long double x

(long double)x 

10 

6 

%e 

double x

(double)x 

10 

6 

%Le 

long double x

(long double)x 

10 

6 

%E 

double x

(double)x 

10 

6 

%LE 

long double x

(long double)x 

10 

6 

%g 

double x

(double)x

10 

6 

%Lg 

long double x

(long double)x 

10 

6 

%G 

double x

(double)x 

10 

6 

%LG 

long double x

(long double)x 

10 

6 

%s 

char x[]

x[0]... 

1바이트 문자열

%ls 

wchar_t x[]

x[0]... 

2바이트 확장 문자열

%p 

void *x

(void *)x 

 

%u 

int x

(unsigned int)x 

10 

1 

%hu 

int x

(unsigned short)x

10 

1 

%lu 

long x

(unsigned long)x 

10 

1 

%llu 

long long int x

(unsigned long long int)x

10

1

%o 

int x

(unsigned int)x 

8 

1 

%ho 

int x

(unsigned short)x 

8 

1 

%lo 

long x

(unsigned long)x 

8 

1 

%llo

long long int x

(unsigned long long int)x

8 

1 

%x

int x

(unsigned int)x 

16 

1 

%hx 

int x

(unsigned short)x 

16 

1 

%lx 

long x

(unsigned long)x 

16 

1 

%llx 

long long int x

(unsigned long long int)x

16 

1 

%X 

int x

(unsigned int)x 

16 

1 

%hX 

int x

(unsigned short)x 

16 

1 

%lX 

long x

(unsigned long)x 

16 

1 

%llX

long long int x

(unsigned long long int)x

16 

1 

%n 

int * x

 

 

 

%hn 

short * x

 

 

 

%ln 

long * x

 

 

 

%% 

None

'%' 

%문자

 

 

 Escape문자

 

실제 문자

Escape 문자

"

\"

"문자

'

\'

'문자

? 

\? 

?문자

\ 

\\ 

\문자

BEL

\a 

Beep

BS

\b 

Backspace: 뒤로

FF

\f 

Form feed

NL

\n 

New line: 바꿈, buffer 비움

CR

\r 

Carriage return: 라인 처음으로

HT

\t 

수평 tab

VT

\v 

수직 tab

null 

\0 

null문자

code values

\d \dd \ddd

8진수 코드값

code values

\xh \xhh

16진수 코드값

 

 

 

scanf()함수의 포맷 플래그

 

포맷문자열

사용 데이터 변수 타입

호출되는 내부 변환함수

기본진법

%c

char x[]

1바이트 문자

%lc

wchar_t x[]

2바이트 확장 문자

%d

int * x

strtol

10

%hd

short * x

strtol

10

%ld 

long * x

strtol

10 

%lld 

long long int * x

strtol

10

%i 

int * x

strtol

10 

%hi 

short * x

strtol

10 

%li 

long * x

strtol

10 

%lli 

long long int * x

strtol

10 

%f 

float * x

strtod

10 

%lf 

double * x

strtod

10 

%Lf

long double * x

strtod

10 

%F

float * x

strtod

10 

%lF

double * x

strtod

10 

%LF

long double * x

strtod

10 

%e

float * x

strtod

10 

%le

double * x

strtod

10 

%Le

long double * x

strtod

10 

%E

float * x

strtod

10 

%lE

double * x

strtod

10 

%LE

long double * x

strtod

10 

%g 

float * x

strtod

10 

%lg 

double * x

strtod

10 

%Lg

long double * x

strtod

10 

%G

float * x

strtod

10 

%lG

double * x

strtod

10 

%LG

long double * x

strtod

10 

%s 

char x[] 

1바이트 문자열

%ls 

wchar_t x[] 

2바이트 확장 문자열

%u 

unsigned int * x

strtoul

10 

%hu 

unsigned short * x

strtoul

10 

%lu 

unsigned long * x

strtoul

10 

%llu 

unsigned long long int * x

strtoul

10 

%o 

unsigned int * x

strtoul

8 

%ho 

unsigned short * x

strtoul

8 

%lo 

unsigned long * x

strtoul

8 

%llo 

unsigned long long int * x

strtoul

8

%x 

unsigned int * x

strtoul

16 

%hx 

unsigned short * x

strtoul

16 

%lx 

unsigned long * x

strtoul

16 

%llx 

unsigned long long int * x

strtoul

16 

%X

unsigned int * x

strtoul

16 

%hX

unsigned short * x

strtoul

16 

%lX

unsigned long * x

strtoul

16 

%llX 

unsigned long long int * x

strtoul

16 

%p 

void ** x

 

 

%n 

int * x

 

 

%hn 

short * x

 

 

%ln 

long * x

 

 

%[...] 

char x[] 

 

 

%l[...] 

wchar_t x[] 

 

 

%% 

None

 

 

'NativeCode > api' 카테고리의 다른 글

LoadLibrary, GetProcAddress, FreeLibrary, Name Mangling 해결  (0) 2010.11.09
IP, TCP, UDP, ICMP Checksum 계산  (0) 2010.07.05
IpHlpApi MSDN  (0) 2010.07.04
SendARP  (0) 2010.07.03
[팁] Heap 메모리 검증하기.. | VC++ 일반  (0) 2010.05.09
패킷들은 checksum 필드를 갖고 있습니다. 이 필드를 이용해 이 패킷이 정상인지 아닌지 판단할 수 있습니다.
체크섬 계산은 간단히 할 수 있습니다.

알고리즘을 먼저 소개하겠습니다.


-- Ip Header checksum 계산--
1. ip header를 2바이트씩 자른다.
2. 체크섬 바이트를 0으로 초기화 합니다.
3. 체크섬 바이트에 짤린 바이트를 그대로 계속 더해갑니다. (sum += 각 2바이트)
4. 더해지는 sum은 4바이트여야 합니다. (올림이 생기기 때문에)
5. 다 더한다음 sum(4바이트) 중 윗 부분의 2바이트를 아랫부분의 2바이트에 다시 더합니다.
6. 다시 sum(4바이트) 중 윗 부분의 2바이트를 아랫부분의 2바이트에 다시 더합니다.

이와 같이 하는 이유는 올림이 다시 생기기 때문에 2번을 한다고 합니다.

7. 이제 2바이트가 된 sum을 1의 보수로 바꾸면 됩니다.



TCP, UDP, ICMP는 한가지 과정이 더 추가됩니다. IP Header의 정보가 필요합니다.
IP 헤더의 체크섬 계산법과 마찬가지로 20바이트에 해당하는 tcp 헤더의 값을 모두 더합니다.
데이터가 존재할경우 데이터 부분까지 더해 줍니다. 데이터가 홀수로 끝나는 부분은 주의해서 더해야 합니다.
다음으로 IP Header의 srcip, dstip를 2바이트로 잘라 더해줍니다.
부가적으로 IP Header의 protocol 필드, tcp 헤더의 길이를 더해줍니다.
여기서 tcp 헤더의 길이는 데이터 부분이 존재할경우 데이터 부분까지길이를 말합니다.
이후 IP의 체크섬과 마찬가지로 위 과정에서 발생한 케리값을 더해주는 과정을 거쳐 체크섬 계산이 완성됩니다.





UDP, ICMP의 경우는 TCP 체크섬 계산과 똑같습니다.

Bloger : moltak.net

'NativeCode > api' 카테고리의 다른 글

LoadLibrary, GetProcAddress, FreeLibrary, Name Mangling 해결  (0) 2010.11.09
printf, scanf 함수 포맷 문자열  (0) 2010.07.07
IpHlpApi MSDN  (0) 2010.07.04
SendARP  (0) 2010.07.03
[팁] Heap 메모리 검증하기.. | VC++ 일반  (0) 2010.05.09
iphlpapi 라이브러리의 msdn 소스 입니다.
굉장히 좋네요. 
NIC 이름과 설명 뿐만 아니라 아이피 NDIS 버젼등등 많은 정보가 나옵니다. 





Bloger: moltak.net

'NativeCode > api' 카테고리의 다른 글

printf, scanf 함수 포맷 문자열  (0) 2010.07.07
IP, TCP, UDP, ICMP Checksum 계산  (0) 2010.07.05
SendARP  (0) 2010.07.03
[팁] Heap 메모리 검증하기.. | VC++ 일반  (0) 2010.05.09
WSAGetLastError()  (0) 2010.04.06
이놈의 SendARP 사용법을 자꾸 까먹네요.ㅠㅠ
그래서 아예 사용법을 올리기로....
이 함수가 변수가 많이 필요해서 자꾸 헷갈립니다. 다른 분들도 그러시나;;
설마 나만?? ㅋㅋ


SendARP는 한 세그먼트에 있는 다른 시스템의 MAC을 얻을 때 사용합니다.
MAC 주소는 48비트이기 때문에 [unsigned char] 가 6개 필요하겠네요.
그리고 아이피 주소를 [unsigned int] 형으로 바꿔 줄 필요가 있습니다.
아래의 소스에서 MAC 주소를 알아낼 대상은 "192.168.0.2" 인 것을 알 수 있네요.

변수만 잘 보신다면 쉽게 사용하실 수 있을 듯 합니다.


Bloger: moltak.net

'NativeCode > api' 카테고리의 다른 글

IP, TCP, UDP, ICMP Checksum 계산  (0) 2010.07.05
IpHlpApi MSDN  (0) 2010.07.04
[팁] Heap 메모리 검증하기.. | VC++ 일반  (0) 2010.05.09
WSAGetLastError()  (0) 2010.04.06
Sock 정보들  (0) 2010.04.04

혹시나 싶어서 여기 게시판에서 검색하니 나오지 않더군요..

모르시는 분들 계실까봐 적어봅니다...

 

 

보통 잘못된 메모리 영역에 쓰기를 하는 경우(memory corruption) 오류가 즉시 발생하지 않습니다..

이런 오류를 잡기가 참으로 난감한데요..

 

그럴 때 _ASSERT(_CrtCheckMemory()); 를 많이 사용하셨을 겁니다.

 

-------------------------------------------------

_CrtCheckMemory()에 대한 사용법은.. 아실거라고 가정하고..

DCRT를 사용하기 위한 준비 작업만 간단하게 적어봅니다.

 

//stdafx.h 제일 처음에 선언

#define _CRTDBG_MAP_ALLOC   

 

//stdafx.h 제일 마지막에 선언

#include <crtdbg.h>    

// main() 제일 처음에 선언

 _CrtSetDbgFlag(
  _CRTDBG_CHECK_ALWAYS_DF
  |_CRTDBG_LEAK_CHECK_DF
  |_CRTDBG_ALLOC_MEM_DF
  |_CRTDBG_DELAY_FREE_MEM_DF
  );

-------------------------------------------------------

 

그런데 _CrtCheckMemory()를 사용해서 커럽션 발생 유무는 확인 할 수 있어도

어느 부분이 문제인지는 알 수 없습니다.

결국  _ASSERT(_CrtCheckMemory()); 를 의심되는 코드에 넣어가면서 범위를 좁히게 되는데요..

그럼에도 불구하고 문제를 해결하기 난감한 상황이 올 수 있습니다.

 

그럴 때 아래와 같은 코드를 사용하면 효험을 볼 수 있습니다.

 

_ASSERT(_CrtIsMemoryBlock(buffer, size, NULL,NULL,NULL));

 

_CrtIsMemoryBlock()는 지정된 메모리 영역이 올바른 힙 메모리인지 검증합니다.

 

스택 메모리나.. 이런 것은 안 됩니다. 힙만 됩니다.

 

그래서.. IOCP와 같은 비동기로 메모리에 쓰기 요청을 하기 전에 위와 같은 코드로 검증을 하면..

커럽션이 발생할 것인지 미리 알 수 있습니다.

 

상큼하게..

 

 void Transfer::PreRequest(BYTE* buffer, int size)
 {
    _ASSERT(_CrtIsMemoryBlock(buffer, size, NULL,NULL,NULL));

 }

 

위와 같이 전달 받은 포인터로 작업을 하는 함수의 제일 상단에 검증하는 코드를 넣어주면...

뭔가 있어 보입니다... ^^

 

------------------------------------------------------------

퇴근 시간이 조금 남아서 예제 하나 만들어 봤습니다.

진작 만들걸.. 하는 후회가 듭니다... T_T

 

#define _CRTDBG_MAP_ALLOC

 

#include <tchar.h>

#include <memory>

 

#include <crtdbg.h>

 

int _tmain(int argc, _TCHAR* argv[])

{

        _CrtSetDbgFlag(

               _CRTDBG_CHECK_ALWAYS_DF

               |_CRTDBG_LEAK_CHECK_DF

               |_CRTDBG_ALLOC_MEM_DF

               |_CRTDBG_DELAY_FREE_MEM_DF

               );

 

 

        int* p=new int[10];

        const int size=sizeof(int)*10;

 

        //p[9]까지유효하기때문에p[10]에무언가를기록하면

        //Memory Corruption이다커럽션이발생한이후에

        //_CrtCheckMemory()를실행하면false를리턴한다.

        _ASSERT(true==_CrtCheckMemory());

        p[10]=0;

        _ASSERT(false==_CrtCheckMemory());

 

        //할당한후_CrtIsMemoryBlock로영역을체크하면true를리턴

        _ASSERT(true==_CrtIsMemoryBlock((const void *)p, size, NULL, NULL, NULL));

 

        //할당된 메모리의 제일 처음이 아닌 임의의 주소를 검사하면 false 리턴

       //찾아보지는 않았지만 메모리 할당 테이블을 유지하고 있는데..

       //이 테이블에서 p+2로 탐색이 안 되서 그럴거라 추측합니다.

       //아시는 분의 제보 기다립니다.

        _ASSERT(false==_CrtIsMemoryBlock((const void *)p+2, size-2, NULL, NULL, NULL)); 

 

        //pdelete 한후에_CrtIsMemoryBlock로영역을체크하면

        //올바르지않은힙영역이기때문에false가리턴된다.

 

        delete[] p;

        _ASSERT(false==_CrtIsMemoryBlock((const void *)p, size, NULL, NULL, NULL));

 

        return 0;

}

'NativeCode > api' 카테고리의 다른 글

IpHlpApi MSDN  (0) 2010.07.04
SendARP  (0) 2010.07.03
WSAGetLastError()  (0) 2010.04.06
Sock 정보들  (0) 2010.04.04
소켓 옵션  (0) 2010.04.01


윈속을 사용할 때 WSAGetLastError()함수를 굉장히 많이 사용하게 됩니다. 함수를 호출해서 그때의 반환 값을 보고 코딩을 다시 하거나 소스를 바꾸거나 하죠. 반환 값은 int형인데 그것을 보고 인터넷에서 검색해서 사용하게 됩니다. 
하지만 사용자가 직접 메시지를 넣어 줄 수도 있겠죠?? 그때 아래와 같은 것이 필요합니다. 좋네요 ^^

10004, "블럭킹 윈속이 WSACancelBlockingCall 함수에서 취소되었습니다 ") );

10009, "잘못된 기술자(소켓 핸들)이다 ") );
10013, "브로드캐스트 어드레스를 위한 데이터그램 소켓의 접속시도가 setsockopt 함수로 SO_BROADCAST가 설정되어있지 않은 상태에서 실패 했습니다. ") );
10014, "name 또는 namelen 매개변수가 올바른 형태가 아닙니다. ") );
10022, "accept 하기 전에 listen 함수가 불려지지 않았습니다. ") );
10024, "새로운 소켓에 할당하기 위한 소켓 기술자가 더 이상 남아있지 않습니다 ") );
10035, "소켓 함수가 비블럭킹 모드로 동작중이다 ") );
10036, "블록화 함수가 호출 되는 동안 부적절한 소켓 함수가 호출되었다 ") );
10037, "이미 완료된 비동기 명령에 대한 취소가 시도됨 ") );
10038, "지정한 기술자가 소켓 기술자가 아닙니다 ") );
10039, "해당 함수에 목적지 어드레스가 필요하지만 제공되지 않았음 ") );
10040, "수신된 메시지가 지정된 버퍼에 저장하기에 너무 커서 손실 되었습니다 ") );
10041, "지정된 프로토콜이 잘못되었거나 이 소켓에 대해서 잘못된 형식입니다 ") );
10042, "알 수 없는 옵션이거나, 지원지지 않는 옵션을 사용했습니다. ") );
10043, "지정된 프로토콜이 지원되지 않는 형식입니다 ") );
10044, "지정된 소켓 타입이 지정한 어드레스 체계에서 지원되지 않는 형식입니다 ") );
10045, "socket이 연결지향형 서비스(SOCK_STREAM)형태가 아닙니다. ex) listen이 UDP socket에서 호출 ") );
10046, "지정된 프로토콜 체계가(PF_*) 지원되지 않습니다 ") );
10047, "지정된 어드레스 체계가(AF_*) 지원되지 않습니다 ") );
10048, "지정한 어드레스(IP)가 이미 사용중이다 ") );
10049, "지정된 어드레스는 로컬 머신에서 사용할 수가 없다 ") );
10050, "네트웍 서브 시스템에 에러가 발생했습니다 ") );
10051, "원격 시스템까지 네트웍이 도달할 수 없습니다 ") );
10052, "연산이 진행되고 있는 도중 접속이 끊겨버렸습니다. ") );
10053, "연결이 out-of-band나 다른 실패 때문에 끊어져 버렸습니다. ") );
10054, "원격 연결지에서 "hard"나 "abortive" 종료를 수행해서 리셋되었습니다. ") );
10055, "윈도우 소켓 시스템의 버퍼 공간이 모자라거나, 애플리케이션에 의해 API에게 제공된 공간이 너무 작아서 요청된 정보를 저장 할 수가 없음 ") );
10056, "지정된 소켓이 이미 연결 되어 있음 ") );
10057, "지정된 소켓이 이미 연결 되어 있지 않음 ") );
10058, "소켓이 셧다운(shutdown()) 되었습니다. ") );
10059, "지정한 함수에 대한 인자가 너무 많음") ); 
10060, "접속 시도가 시간초과 되었습니다. ") );
10061, "접속시도가 강제로 종료되었습니다 ") );
10062, "") );                  
10063, "") );                  
10064, "원격 호스트가 다운 되었음 ") );
10065, "네트웍 시스템 장애 등에 의해서 원격호스트까지도 달 할 수 없습니다.") );
10091, "네트워크 서브 시스템이 아직 통신할 준비가 되어 있지 않음(WSAStartup()이 반환)") ); 
10092, "요청한 윈도우즈 소켓 버전이 현재 윈도우즈 소켓 시스템에서 지원하지 않습니다. ") );
10093, "이 함수를 사용하기 전에 성공적인 WSAStartup 함수의 호출이 없었습니다.") ); 
11001, "호스트를 찾아낼 수 없습니다.") );
11002, "요청된 정보가 발견 되지 않음") );
11003, "회복할 수 없는 에러발생") );
11004, "잘못된 이름(name)으로 아무런 데이터가 기록되지 않았습니다. ") );

'NativeCode > api' 카테고리의 다른 글

SendARP  (0) 2010.07.03
[팁] Heap 메모리 검증하기.. | VC++ 일반  (0) 2010.05.09
Sock 정보들  (0) 2010.04.04
소켓 옵션  (0) 2010.04.01
Broadcasting 예제  (0) 2010.03.31
WSAAsyncSelect() 소켓의 I/O 상태 변화 즉, 연결요청, 데이터 수신, 송신 버퍼의 사용가능 등의 이벤트를 시스템이 메시지를 통하여 알려주도록 요청함 
WSAAsyncGetHostByAddr() 호스트 주소로부터 호스트 정보를 얻음 
WSAAsyncGetHostByName() 호스트 이름으로부터 호스트 정보를 얻음 
WSAAsyncGetProtoByName() 프로토콜 이름으로부터 프로토콜 번호를 얻음 
WSAAsyncGetProtoByNumber() 프로토콜 번호로부터 프로토콜 이름을 얻음 
WSAAsyncGetServByName() 서비스 이름으로부터 서비스 정보를 얻음 
WSAAsyncGetServByPort() 포트번호로부터 서비스 정보를 얻음 


DWORD GetWindowThreadProcessId (
HWND       hWnd;
        LPDWORD lpdwProcessId
);
HWND 값을 이용하여 프로세스 ID를 알려주는 함수이다.
hWnd              - PID를 얻고자 하는 윈도우의 핸들
lpdwProcessId - 반환받을 PID의 포인터, NULL로 설정할 경우 PID는 리턴값으로 반환된다.

--------------------------------------------------------------------------------

WSAAsyncGetServByPort

getservbyport

두 함수는 서비스 이름이 등록된 파일에서 입력된 포트번호에 대한 서비스를 리턴해 준다.

http://blog.naver.com/lseykwang?Redirect=Log&logNo=100071270266

위 사이트에서 검색..

WSAAsyncGetProtoByNumber()

--------------------------------------------------------------------------------

문법 HANDLE WSAAsyncGetServByPort(HWND hwnd, UINT wMsg, int port, const char *proto, char *buf, int buflen) ; 

인자

hwnd 함수 호출이 완료되었을 때 메시지를 수신할 윈도우의 핸들 
wMsg 함수 호출이 완료되었을 때 보낼 메시지 
port 서비스 포트 번호 
proto 프로토콜의 이름을 가리키는 문자열의 포인터 
buf servent구조체를 수신할 버퍼의 포인터 
buflen 버퍼의 크기 
설명

getservbyport()의 비동기형 함수로 포트 번호와 프로토콜 이름에 해당하는 서비스 정보 구조체 servent를 만들고 응용 프로그램 윈도우 hwnd에게 메시지 wMsg를 보낸다. 

리턴값

성공 비동기 태스크 핸들 
실패 0 
WSAGetLastError() :

WSANOTINITALISED, WSAENETDOWN, WSAEINPROGRESS, WSAEINVAL WSAEALREADY 

 

windows socket system call 요약 
-----------------------------------------------------------------------------------------------------

BOOL WINAPI GetFileInformationByHandle(
  __in   HANDLE hFile,
  __out  LPBY_HANDLE_FILE_INFORMATION lpFileInformation
);
핸들을 이용해서 파일 정보를 얻어오는 것
hFile : 정보를 얻고자 하는 파일의 핸들
lpFileInformation : BY_HANDLE_FILE_INFORMATION구조체 변수의 주소를 전달. 여기로 전달되는 주소의 변수에 파일 정보가 채워짐.

typedef struct _BY_HANDLE_FILE_INFORMATION {
  DWORD    dwFileAttributes; //파일의 특성 정보
  FILETIME ftCreationTime; //파일의 생성 날짜
  FILETIME ftLastAccessTime; //파일에 마지막으로 액세스 한 날짜
  FILETIME ftLastWriteTime; //파일에 마지막으로 수정한 날짜
  DWORD    dwVolumeSerialNumber;
  DWORD    nFileSizeHigh; //대용량이 이 변수도 읽어와야함
  DWORD    nFileSizeLow; //4G이하 파일의 크기를 얻어올 때
  DWORD    nNumberOfLinks;
  DWORD    nFileIndexHigh;
  DWORD    nFileIndexLow;
} BY_HANDLE_FILE_INFORMATION, *PBY_HANDLE_FILE_INFORMATION;

=================================================
DWORD WINAPI GetFullPathName(
  __in   LPCTSTR lpFileName,
  __in   DWORD nBufferLength,
  __out  LPTSTR lpBuffer,
  __out  LPTSTR *lpFilePart
);
파일 이름을 통해서 파일경로 정보를 얻어야 할 때 사용
lpFileName : Full Path를 확인하고자 하는 파일 이름을 전달한다.
nBufferLength : Full Path를 저장할 버퍼에 저장 가능한 문자열 길이를 지정한다.(바이트크기가 아니라 문자열 길이!)
lpBuffer : Full Path를 저장할 버퍼의 주소값을 지정한다.
lpFilePart : Full Path가 문자열로 버퍼에 저장된 이후, 버퍼의 특정 위치를 가리키는 포인터 값이 저장(특정 위치부터 출력하면 파일명 나옴)
=================================================






함 수 기 능
 select() 소켓의 상태 변화(읽기, 쓰기, 오류 발생)를 알려줌
 gethostbyaddr() 호스트 주소로부터 호스트 정보를 얻음
 gethostbyname() 호스트 이름으로부터 호스트 정보를 얻음
 getprotobyname() 프로토콜 이름으로부터 프로토콜 번호를 얻음
 getprotobynumber() 프로토콜 번호로부터 프로토콜 이름을 얻음
 getservbyname() 서비스 이름으로부터 서비스 정보를 얻음
 getservbyport() 포트번호로부터 서비스 정보를 얻음

위와 같은 blocking 함수들은 소켓의 동작 모드에 관계없이 항상 블록될 수 있는 함수이다
이러한 함수를 사용할 때 프로그램이 블록되는 문제를 해결하기 위하여, 윈속에서는 이들 blocking 함수와 같은 기능을 수행하면서 실제로는 비동기 모드로 동작하는 즉, 함수 실행결과를 비동기적으로 알려주는 비동기 함수들을 제공하고 있다. 
  함 수  기 능
 WSAAsyncSelect() 소켓의 I/O 상태 변화 즉, 연결요청, 데이터 수신, 송신 버퍼의 사용가능 등의 이벤트를 시스템이 메시지를 통하여 알려주도록 요청함 
 WSAAsyncGetHostByAddr() 호스트 주소로부터 호스트 정보를 얻음 
 WSAAsyncGetHostByName() 호스트 이름으로부터 호스트 정보를 얻음
 WSAAsyncGetProtoByName()  프로토콜 이름으로부터 프로토콜 번호를 얻음
 WSAAsyncGetProtoByNumber() 프로토콜 번호로부터 프로토콜 이름을 얻음
 WSAAsyncGetServByName()  서비스 이름으로부터 서비스 정보를 얻음
 WSAAsyncGetServByPort() 포트번호로부터 서비스 정보를 얻음







'NativeCode > api' 카테고리의 다른 글

[팁] Heap 메모리 검증하기.. | VC++ 일반  (0) 2010.05.09
WSAGetLastError()  (0) 2010.04.06
소켓 옵션  (0) 2010.04.01
Broadcasting 예제  (0) 2010.03.31
UDP Server, Client  (0) 2010.03.31

소켓을 사용하다 보면 옵션이 필요한 경우가 있습니다.

책에서 나오는 예제를 보면 가장 처음에는 send, recv buffer 크기 변경하는게 나오더 군요.

하지만 더 많죠. 거기에 대해서 블로깅 하겠습니다.

 

소켓 옵션 설정하기

 

소켓 옵션 얻기

 

일단 소켓 옵션 사용하는 것은 아래와 같은 함수를 사용합니다.

인자를 설명하자면

소켓, 변경할 옵션의 프로토콜 레벨, 변경할 옵션 이름, 변경할 옵션의 값을 저장한 버퍼, 전달하는 옵션의 바이트 단위 길이.

이렇게 5개가 되네요.

 

소켓 옵션은 크게 세 개가 있습니다.

SOL_SOCKET

IPPROTO_IP

IPPROTO_TCP

소켓에 대한 가장 일반적인 옵션

IP Protocol의 옵션

TCP Protocol의 옵션

 

소켓 옵션 - SOL_SOCKET

 

소켓 옵션 - IPPROTO_IP

 

소켓 옵션 - IPPROTO_TCP

 

SO_REUSEADDR 옵션

소켓을 사용해서 함수를 만들다 보면 bind가 되지 않는 현상을 자주 봤을 것입니다.. 이것은 4Way-handshaking 과정 때문인데 서버가 먼저 강제 종료 된다면 소켓이 바로 종료가 되는 것이 아니라 TIME-WAIT 상태로 빠지게 되기 때문에 그렇습니다. (Handshaking 참조) 그래서 현재 bind가 되어 있는 상태이기 때문에 다시 bind 하려고 하면 에러가 나는 것이죠. 이 상태는SOL_SOCKET의 SO_REUSEADDR을 바꿈으로써 해결 할 수 있습니다.  

실제로 SO_REUSEADDR의 옵션은 default값은 0(FALSE)입니다. 그래서 반드시 TRUE옵션을 주는 것을 추천을 합니다.

'NativeCode > api' 카테고리의 다른 글

WSAGetLastError()  (0) 2010.04.06
Sock 정보들  (0) 2010.04.04
Broadcasting 예제  (0) 2010.03.31
UDP Server, Client  (0) 2010.03.31
Server Socket  (0) 2010.03.19
이번엔 브로드캐스팅 예제입니다. 자신이 속한 세그먼트에 데이터를 한방에 보낼 때 사용하는 프로토콜 이죠.
일단 예제를 보시죠. 아주 아주 간단합니다.
이제까지와 다른 점이라곤 소켓에 옵션을 주는 것 밖에 없는데요. 그 옵션들은 다음에 포스팅 하도록 하겠습니다.


Sender
 

Receiver
 

'NativeCode > api' 카테고리의 다른 글

Sock 정보들  (0) 2010.04.04
소켓 옵션  (0) 2010.04.01
UDP Server, Client  (0) 2010.03.31
Server Socket  (0) 2010.03.19
Client Socket  (0) 2010.03.19
UDP의 가장 큰 특징은 비 연결성이라는 것이겠죠? 아래 소스를 보시면 알겠지만 접속 하는 것이 없어요. 서버는 바인드에서 끝나서 데이터를 받기 위해 recvfrom을 호출하고 있고 클라이언트는 sendto로 전송대기만 하고 있음.
TCP 보다 조금더 간단하게 생겼음

Server

 


Client
 

'NativeCode > api' 카테고리의 다른 글

소켓 옵션  (0) 2010.04.01
Broadcasting 예제  (0) 2010.03.31
Server Socket  (0) 2010.03.19
Client Socket  (0) 2010.03.19
① WSAStartup  (0) 2010.03.19
소켓 소스입니다. 이번엔 Server 구요. 가장 기본형이죠.

'NativeCode > api' 카테고리의 다른 글

Broadcasting 예제  (0) 2010.03.31
UDP Server, Client  (0) 2010.03.31
Client Socket  (0) 2010.03.19
① WSAStartup  (0) 2010.03.19
소켓 함수 정리  (0) 2010.03.19
우리가 자주 사용하게 되는 소켓. 근데 보통 책에 있는 모양대로 사용하게 되죠? MFC는 CSocket이 있고 QT에는 QSocket이 있어서 WinSock을 직접 조작해서 플그래밍 할 일이 별로 없죠. 

하지만 가끔 WinSock을 이용해서 프로그래밍 할 필요가 있는데요. 그때마다 책에 있는 것을 참조 해서 사용하죠. 그리고 책에는 대부분 C스타일로 사용하고 있구요. 

근데 C++ 플그래밍 하는데 C스타일은 좀 어색하지 않나요? 나만 그런가? ㅋㅋ 암튼 전 아래와 같은 방식으로 사용한답니다. C++ 스타일이라고 해봐야 try/catch가 섞여 있는 것 뿐이지만요. 이 블로그 찾아보면 Exeption을 클래스로 전달하는 것으로 대신하고 있는 소스도 있습니다. 참고 하실 분은
http://moltak.tistory.com/entry/C-Style-Socket 여기를 참고 하세요.

암튼 모두들 즐프~~ 하세요ㅋㅋ

 

'NativeCode > api' 카테고리의 다른 글

UDP Server, Client  (0) 2010.03.31
Server Socket  (0) 2010.03.19
① WSAStartup  (0) 2010.03.19
소켓 함수 정리  (0) 2010.03.19
C++ Style Socket  (0) 2010.03.19

WSAData wsaData;

int nError;


nError = WSAStartup( MAKEWORD( 2, 2 ), &wsaData );

if( nError != 0 )

// error 

'NativeCode > api' 카테고리의 다른 글

Server Socket  (0) 2010.03.19
Client Socket  (0) 2010.03.19
소켓 함수 정리  (0) 2010.03.19
C++ Style Socket  (0) 2010.03.19
Client Source  (0) 2010.03.19

소켓 초기화 시키기

보통 WSAStartup -> socket -> closesocket -> WSACleanup 순으로 이뤄진다.

서버 WSAStartup -> socket -> bind -> listen ->  accept -> closesocket -> WSACleanup

클라 WSAStartup -> socket -> connect -> closesocket -> WSACleanup


WSADATA wsaData;

SOCKET sock;

SOCKADDR_IN addr;


WSAStartup( MAKEWORD( 2, 2 ), &wsaData );

sock = socket( AF_INET, SOCK_STREAM, 0 );


memset( ( void * )&addr, 0, sizeof( SOCKADDR_IN ) );

addr.sin_family = AF_INET;

addr.sin_addr.s_addr = inet_addr( IP_ADDRESS );

addr.sin_port = htons( PORT_NUMBER );


closesocket( sock );

WSACleanup();

'NativeCode > api' 카테고리의 다른 글

Client Socket  (0) 2010.03.19
① WSAStartup  (0) 2010.03.19
C++ Style Socket  (0) 2010.03.19
Client Source  (0) 2010.03.19
Server Source  (0) 2010.03.19

Snap6.png


바로 쓸수 있는 건 아니고... 이런 식으로 만들면 된다. 앞에 있는 예제는 api 에서는 저렇게 하고 C++ 문법으로는 이렇게 한다.

나는 Exception Class를 직접 만들어서 사용했으며 그 예외를 호출쪽에서 처리할 수 있도록 제작하였다.

기존 api 방식의 문법은 눈에 잘 들어오지 않는 단점이 있으며 위의 방법은 눈에 잘 들어오고 구조적이라는 장점이 있다.

'NativeCode > api' 카테고리의 다른 글

① WSAStartup  (0) 2010.03.19
소켓 함수 정리  (0) 2010.03.19
Client Source  (0) 2010.03.19
Server Source  (0) 2010.03.19
winsock 자신의 아이피 알아내기  (0) 2010.03.19

#include <stdio.h>
#include <winsock2.h>

#pragma comment(lib,"wsock32.lib")

void main()
{
    SOCKET s;
    SOCKADDR_IN sin;
    WSADATA wsaData;
    if(WSAStartup(MAKEWORD(2,0),&wsaData) != NO_ERROR)
    {
        printf("WSAStartup failed, error code : %d\n",WSAGetLastError());
        return ;
    }

    s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

    if(s == INVALID_SOCKET)
    {
        printf("make socket failed, error code : %d\n",WSAGetLastError());
        WSACleanup();
        return ;
    }

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr("127.0.0.1");
    sin.sin_port = htons(912);

    if(connect(s,(struct sockaddr*)&sin,sizeof(sin)) != NO_ERROR)
    {
        printf("connect failed, error code = %u \n",WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return ;
    }

    if(closesocket(s) != NO_ERROR)
    {
        printf("remove socket failed, error code : %u\n",WSAGetLastError());
        WSACleanup();
        return ;
    }

    if(WSACleanup() != NO_ERROR)
    {
        printf("WSACleanup failed, error code = &u\n",WSAGetLastError());
        return ;
    }

    printf("127.0.0.1의 912번 포트에 접속을 성공하셨습니다.\n");
}

'NativeCode > api' 카테고리의 다른 글

소켓 함수 정리  (0) 2010.03.19
C++ Style Socket  (0) 2010.03.19
Server Source  (0) 2010.03.19
winsock 자신의 아이피 알아내기  (0) 2010.03.19
Winsock 상대방 아이피, 포트 알아내기  (0) 2010.03.19

#include <stdio.h>
#include <winsock2.h>

void main()
{

    // Initialize Winsock.
    WSADATA wsaData;
    int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
    if ( iResult != NO_ERROR )
        printf("Error at WSAStartup()\n");

    // Create a socket.
    SOCKET m_socket;
    m_socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );

    if ( m_socket == INVALID_SOCKET )
    {
        printf( "Error at socket(): %ld\n", WSAGetLastError() );
        WSACleanup();
        return;
    }

    // Bind the socket.
    sockaddr_in service;

    service.sin_family = AF_INET;
    service.sin_addr.s_addr = inet_addr( "127.0.0.1" );
    service.sin_port = htons( 27015 );

    if ( bind( m_socket, (SOCKADDR*) &service, sizeof(service) ) == SOCKET_ERROR )
    {
        printf( "bind() failed.\n" );
        closesocket(m_socket);
        return;
    }

    // Listen on the socket.
    if ( listen( m_socket, 1 ) == SOCKET_ERROR )
        printf( "Error listening on socket.\n");

    // Accept connections.
    SOCKET AcceptSocket;

    printf( "Waiting for a client to connect...\n" );
    while (1)
    {
        AcceptSocket = SOCKET_ERROR;
        while ( AcceptSocket == SOCKET_ERROR )
        {
            AcceptSocket = accept( m_socket, NULL, NULL );
        }
        printf( "Client Connected.\n");
        m_socket = AcceptSocket;
        break;
    }

    // Send and receive data.
    int bytesSent;
    int bytesRecv = SOCKET_ERROR;
    char sendbuf[32] = "조산 서버";
    char recvbuf[32] = "";

    bytesRecv = recv( m_socket, recvbuf, 32, 0 );
    printf( "Bytes Recv: %ld\n", bytesRecv );

    bytesSent = send( m_socket, sendbuf, strlen(sendbuf), 0 );
    printf( "Bytes Sent: %ld\n", bytesSent );

    printf("Recv Buf: %s\n", recvbuf);

    return;
}

'NativeCode > api' 카테고리의 다른 글

C++ Style Socket  (0) 2010.03.19
Client Source  (0) 2010.03.19
winsock 자신의 아이피 알아내기  (0) 2010.03.19
Winsock 상대방 아이피, 포트 알아내기  (0) 2010.03.19
IPHLPAPI(IP Helper API) functions  (0) 2010.03.19

char lHostName[ 255 ];

PHOSTENT lHostInfo;

char *IpBuffer;

WSADATA wsaData;


if( WSAStartup( MAKEWORD( 2,0 ), &wsaData ) == 0 )

{

if( gethostname( lHostName, 255 ) == 0 )

{

if( ( lHostInfo = gethostbyname( lHostName ) ) != NULL )

{

IpBuffer = inet_ntoa( *( struct in_addr * )*lHostInfo->h_addr_list );

}

}

}


CString LocalAdap;
 CString LocalMac;
 CString LocalIP;
 CString LocalSub;
 CString LocalGate;

 PIP_ADAPTER_INFO pAdapterInfo;
 PIP_ADAPTER_INFO pAdapter = NULL;

 DWORD dwRetVal = 0;
 BOOL m_bBreak = FALSE;

 pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof(IP_ADAPTER_INFO));
 unsigned long ulOutBufLen = sizeof(IP_ADAPTER_INFO);

 UpdateData(true);

 if(GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) != ERROR_SUCCESS)
 {
  GlobalFree(pAdapterInfo);
  pAdapterInfo = (IP_ADAPTER_INFO*)malloc(ulOutBufLen);
 }

 if((dwRetVal=GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR)
 {
  pAdapter = pAdapterInfo;
  while(pAdapter)
  {
   LocalAdap.Format (_T("%s"), (CA2W)pAdapter->Description);
   m_listbox.AddString(LocalAdap);

   LocalMac.Format (_T("MAC : %02X-%02X-%02X-%02X-%02X-%02X"),pAdapter->Address[0],pAdapter->Address[1], 
    pAdapter->Address[2], pAdapter->Address[3], pAdapter->Address[4], pAdapter->Address[5]);
   m_listbox.AddString((_T("MAC :"), LocalMac));

   LocalIP.Format (_T("IP : %s"), (CA2W)pAdapter->IpAddressList.IpAddress.String);
   m_listbox.AddString(LocalIP);

   LocalSub.Format (_T("Local : %s"), (CA2W)pAdapter->IpAddressList.IpMask.String);
   m_listbox.AddString(LocalSub);

   if(pAdapter->Next == NULL)
   {
    m_bBreak = TRUE;
    break;
   }
   pAdapter = pAdapter->Next;
  }
 }

'NativeCode > api' 카테고리의 다른 글

Client Source  (0) 2010.03.19
Server Source  (0) 2010.03.19
Winsock 상대방 아이피, 포트 알아내기  (0) 2010.03.19
IPHLPAPI(IP Helper API) functions  (0) 2010.03.19
CSocket 과 CAsyncSocket에서 Thread문제  (0) 2010.03.19

클라이언트의 접속을 받아 드릴때 accept 함수를 사용하게 된다.

이때 accept 함수에 인자를 세개를 넣게 되는데 두번째 인자에 클라이언트에 대한 정보를 넣을 수 있다.

이때 세번째 인자에는 받아야할 데이터 크기에 대한 변수를 넣어야 한다. 데이터 크기가 다르면

뻑날 수도 있다. ㅋ


sockaddr_in clientInfo = { 0 };

int addrsize= sizeofsockaddr_in );
client = accept( m_Socket, ( sockaddr * )&clientInfo, &addrsize );


printf( "ip = %s  port = %d\n", inet_ntoa( clientInfo.sin_addr ),  ntohs( ( short )clientInfo.sin_port ) );

'NativeCode > api' 카테고리의 다른 글

Server Source  (0) 2010.03.19
winsock 자신의 아이피 알아내기  (0) 2010.03.19
IPHLPAPI(IP Helper API) functions  (0) 2010.03.19
CSocket 과 CAsyncSocket에서 Thread문제  (0) 2010.03.19
HTTP 긁어오기 (소스파일 有)  (0) 2010.03.19

IPHLPAPI(IP Helper API) functions

Headers


#include <winsock2.h>
#include <iphlpapi.h>

Library

  • WS2_32.lib
  • IPHLPAPI.lib

Adapter Configuration


DWORD
GetAdapterIndex(
  LPWSTR AdapterName,
  PULONG IfIndex
);

DWORD WINAPI
GetAdaptersAddresses(
  ULONG Family,
  DWORD Flags,
  PVOID Reserved,
  PIP_ADAPTER_ADDRESSES pAdapterAddresses,
  PULONG pOutBufLen
);

DWORD
GetAdaptersInfo(
  PIP_ADAPTER_INFO pAdapterInfo,
  PULONG pOutBufLen
);

DWORD
GetPerAdapterInfo(
  ULONG IfIndex,
  PIP_PER_ADAPTER_INFO pPerAdapterInfo,
  PULONG pOutBufLen
);

DWORD
GetUniDirectionalAdapterInfo(
  PIP_UNIDIRECTIONAL_ADAPTER_ADDRESS pIPIfInfo,
  PULONG dwOutBufLen
);

ARP


DWORD
CreateIpNetEntry(
  PMIB_IPNETROW pArpEntry
);

DWORD
CreateProxyArpEntry(
  DWORD dwAddress,
  DWORD dwMask,
  DWORD dwIfIndex
);

DWORD
DeleteIpNetEntry(
  PMIB_IPNETROW pArpEntry
);

DWORD
DeleteProxyArpEntry(
  DWORD dwAddress,
  DWORD dwMask,
  DWORD dwIfIndex
);

DWORD
FlushIpNetTable(
  DWORD dwIfIndex
);

DWORD
GetIpNetTable(
  PMIB_IPNETTABLE pIpNetTable,
  PULONG pdwSize,
  BOOL bOrder
);

DWORD
SendARP(
  IPAddr DestIP,
  IPAddr SrcIP,
  PULONG pMacAddr,
  PULONG PhyAddrLen
);

DWORD
SetIpNetEntry(
  PMIB_IPNETROW pArpEntry
);

Network Interface


DWORD
GetFriendlyIfIndex(
  DWORD IfIndex
);

DWORD
GetIfEntry(
  PMIB_IFROW pIfRow
);

DWORD
GetIfTable(
  PMIB_IFTABLE pIfTable,
  PULONG pdwSize,
  BOOL bOrder
);

DWORD
GetInterfaceInfo(
  PIP_INTERFACE_INFO pIfTable,
  PULONG dwOutBufLen
);

DWORD
GetNumberOfInterfaces(
  PDWORD pdwNumIf
);

DWORD
SetIfEntry(
  PMIB_IFROW pIfRow
);

IP, ICMP


DWORD
GetIcmpStatistics(
  PMIB_ICMP pStats
);

DWORD
GetIpStatistics(
  PMIB_IPSTATS pStats
);

BOOL
IcmpCloseHandle(
  HANDLE IcmpHandle
);

HANDLE
IcmpCreateFile(void);

DWORD
IcmpParseReplies(
  LPVOID ReplyBuffer,
  DWORD ReplySize
);

DWORD
IcmpSendEcho(
  HANDLE IcmpHandle,
  IPAddr DestinationAddress,
  LPVOID RequestData,
  WORD RequestSize,
  PIP_OPTION_INFORMATION RequestOptions,
  LPVOID ReplyBuffer,
  DWORD ReplySize,
  DWORD Timeout
);

DWORD
IcmpSendEcho2(
  HANDLE IcmpHandle,
  HANDLE Event,
  FARPROC ApcRoutine,
  PVOID ApcContext,
  IPAddr DestinationAddress,
  LPVOID RequestData,
  WORD RequestSize,
  PIP_OPTION_INFORMATION RequestOptions,
  LPVOID ReplyBuffer,
  DWORD ReplySize,
  DWORD Timeout
);

DWORD
SetIpStatistics(
  PMIB_IPSTATS pIpStats
);

DWORD
SetIpTTL(
  UINT nTTL
);

IP address configuration


DWORD
AddIPAddress(
  IPAddr Address,
  IPMask IpMask,
  DWORD IfIndex,
  PULONG NTEContext,
  PULONG NTEInstance
);

DWORD
DeleteIPAddress(
  ULONG NTEContext
);

DWORD
GetIpAddrTable(
  PMIB_IPADDRTABLE pIpAddrTable,
  PULONG pdwSize,
  BOOL bOrder
);

DWORD
IpReleaseAddress(
  PIP_ADAPTER_INDEX_MAP AdapterInfo
);

DWORD
IpRenewAddress(
  PIP_ADAPTER_INDEX_MAP AdapterInfo
);

Network configuration


DWORD
GetNetworkParams(
  PFIXED_INFO pFixedInfo,
  PULONG pOutBufLen
);

Notification


DWORD
NotifyAddrChange(
  PHANDLE Handle,
  LPOVERLAPPED overlapped
);

DWORD
NotifyRouteChange(
  PHANDLE Handle,
  LPOVERLAPPED overlapped
);

Routing


DWORD
CreateIpForwardEntry(
  PMIB_IPFORWARDROW pRoute
);

DWORD
DeleteIpForwardEntry(
  PMIB_IPFORWARDROW pRoute
);

DWORD WINAPI
EnableRouter(
  HANDLE* pHandle,
  OVERLAPPED* pOverlapped
);

DWORD
GetBestInterface(
  IPAddr dwDestAddr,
  PDWORD pdwBestIfIndex
);

DWORD
GetBestRoute(
  DWORD dwDestAddr,
  DWORD dwSourceAddr,
  PMIB_IPFORWARDROW pBestRoute
);

DWORD
GetIpForwardTable(
  PMIB_IPFORWARDTABLE pIpForwardTable,
  PULONG pdwSize,
  BOOL bOrder
);

BOOL
GetRTTAndHopCount(
  IPAddr DestIpAddress,
  PULONG HopCount,
  ULONG MaxHops,
  PULONG RTT
);

DWORD
SetIpForwardEntry(
  PMIB_IPFORWARDROW pRoute
);

DWORD WINAPI
UnenableRouter(
  OVERLAPPED* pOverlapped,
  LPDWORD lpdwEnableCount
);

TCP, UDP


DWORD
GetExtendedTcpTable(
  PVOID pTcpTable,
  PDWORD pdwSize,
  BOOL bOrder,
  ULONG ulAf,
  TCP_TABLE_CLASS TableClass,
  ULONG Reserved
);

DWORD
GetExtendedUdpTable(
  PVOID pUdpTable,
  PDWORD pdwSize,
  BOOL bOrder,
  ULONG ulAf,
  UDP_TABLE_CLASS TableClass,
  ULONG Reserved
);

DWORD
GetOwnerModuleFromTcpEntry(
  PMIB_TCPROW_OWNER_MODULE pTcpEntry,
  TCPIP_OWNER_MODULE_INFO_CLASS Class,
  PVOID Buffer,
  PDWORD pdwSize
);

DWORD
GetOwnerModuleFromTcp6Entry(
  PMIB_TCP6ROW_OWNER_MODULE pTcpEntry,
  TCPIP_OWNER_MODULE_INFO_CLASS Class,
  PVOID Buffer,
  PDWORD pdwSize
);

DWORD
GetOwnerModuleFromUdpEntry(
  PMIB_UDPROW_OWNER_MODULE pUdpEntry,
  TCPIP_OWNER_MODULE_INFO_CLASS Class,
  PVOID Buffer,
  PDWORD pdwSize
);

DWORD
GetOwnerModuleFromUdp6Entry(
  PMIB_UDP6ROW_OWNER_MODULE pUdpEntry,
  TCPIP_OWNER_MODULE_INFO_CLASS Class,
  PVOID Buffer,
  PDWORD pdwSize
);

DWORD
GetTcpStatistics(
  PMIB_TCPSTATS pStats
);

DWORD
GetTcpTable(
  PMIB_TCPTABLE pTcpTable,
  PDWORD pdwSize,
  BOOL bOrder
);

DWORD
SetTcpEntry(
  PMIB_TCPROW pTcpRow
);

DWORD
GetUdpStatistics(
  PMIB_UDPSTATS pStats
);

DWORD
GetUdpTable(
  PMIB_UDPTABLE pUdpTable,
  PDWORD pdwSize,
  BOOL bOrder
);

'NativeCode > api' 카테고리의 다른 글

winsock 자신의 아이피 알아내기  (0) 2010.03.19
Winsock 상대방 아이피, 포트 알아내기  (0) 2010.03.19
CSocket 과 CAsyncSocket에서 Thread문제  (0) 2010.03.19
HTTP 긁어오기 (소스파일 有)  (0) 2010.03.19
FTP  (0) 2010.03.19

1.요약

CSocket / CAsyncSocket을 생성한 곳이 아닌 다른 Thread로 넘겨 처리할 경우 CSocket이 가진 Thread state가 변해 에러가 발생합니다.

예를들어 한쪽에서는 Listen을 하여 클라이언트 Socket을 Accept하고, Thread를 생성시켜 Socket전송을 맡길 경우에 Thread문제를 해결하는 방법을 소개하겠습니다.


2.본문

방법은 간단합니다.
Accept한 Socket을 Deatch시키고 거기에서 나온 handle을 Thread로 넘김니다.
그리고 Thread에서 handle을 Attach시켜 CSocket / CAsyncSocket 개체 인스턴스를 만들어 사용하면 됩니다. 

'NativeCode > api' 카테고리의 다른 글

Winsock 상대방 아이피, 포트 알아내기  (0) 2010.03.19
IPHLPAPI(IP Helper API) functions  (0) 2010.03.19
HTTP 긁어오기 (소스파일 有)  (0) 2010.03.19
FTP  (0) 2010.03.19
HTTPS GET - Download HTML from HTTPS URL  (0) 2010.03.19

MFC Source HTTP_소스_긁어오기.zip

#include "AFXINET.H"

CInternetSession* pSession = NULL;
CStdioFile* pFile = NULL;
TCHAR szError[1024] = {0};

TRY 
{
  pSession = new CInternetSession;
  pFile = pSession->OpenURL( "http://www.devpia.com" );
}
CATCH( CInternetException, pEx )
{
  pEx->GetErrorMessage( szError, 1024 );
}

END_CATCH

if( pFile != NULL )
{
  //여기서 웹페이지 내용을 가지고 처리
  //HTTP를 호출했을 시
  CHttpFile* pHttpFile = (CHttpFile*)pFile;
  
  /*원하는 코드를 실행한다.*/
  // pHttpFile->ReadString(CString DataType);
  
  delete pHttpFile;
  pHttpFile = NULL;
  pFile = NULL;
}

if( pSession != NULL )
{
  //pSession->Close(); //CInternetSession 소멸자가 알아서 호출함
  delete pSession;
  pSession = NULL;
}

/*
  파라미터를 GET 방식으로 넘기시려면 ...

  CInternetSession::OpenURL()로 접속할 URL과 파라미터 넘겨주면,
  CStdioFile 포인터를 넘겨 주거든요. 그냥 파일 처럼 처리하시면 되겠네요.

  OpenURL("http://localhost/test.asp?name=하나")
  이런 식으로 처리하실 수 있습니다


  파라미터를 POST 방식으로 넘기시려면 ...
  CInternetSession::GetHttpConnection()으로 HTTP 커넥션 맺어 주시고,
  CHttpConnection::OpenRequest()로 URL에 접속해서,
  CHttpFile::AddRequestHeaders()로 헤더와 POST 정보를 추가해서 ...
  CHttpFile::SendRequest()로 데이터 날려 주시면 됩니다.
*/

'NativeCode > api' 카테고리의 다른 글

IPHLPAPI(IP Helper API) functions  (0) 2010.03.19
CSocket 과 CAsyncSocket에서 Thread문제  (0) 2010.03.19
FTP  (0) 2010.03.19
HTTPS GET - Download HTML from HTTPS URL  (0) 2010.03.19
CkLibrary  (1) 2010.03.19

void OnDownLoad()

{

CInternetSession session;

CFtpConnection *pConnection = NULL;

CFtpFileFind *pFileFind = NULL;

CString csStr;

CString csFileName;

BOOL bContinue;


try

{

pConnection = session.GetFtpConnection( "220.67.202.123", "h7", "h7" );

pConnection->GetCurrentDirectory( csStr );

pFileFind = new CFtpFileFind( pConnection );

bContinue = pFileFind->FindFile( csStr );


while( bContinue )

{

bContinue = pFileFind->FindNextFile();

csFileName = pFileFind->GetFileName();


if( !pFileFind->IsDirectory() )

{

pConnection->GetFile( csFileName, csFileName );

}

}

catch (CFileException* e)

{

e->ReportError();

}

catch(CInternetException *e)

{

e->ReportError();

}

}


pConnection->Close();

delete pFileFind;

}


Snap1.bmp

'NativeCode > api' 카테고리의 다른 글

CSocket 과 CAsyncSocket에서 Thread문제  (0) 2010.03.19
HTTP 긁어오기 (소스파일 有)  (0) 2010.03.19
HTTPS GET - Download HTML from HTTPS URL  (0) 2010.03.19
CkLibrary  (1) 2010.03.19
Sendarp 사용법  (0) 2010.03.19

Snap8.png   옆의 소스는 https 에서 페이지 정보를 얻어오는 함수이다. wininet 을 이용해서 https 의 소스를 얻어올 수 있다곤 하는데 잘 모르겠더라. 계속 검색중 우연히 찾은 사이트에서 얻은 소스이다. 그 홈페이지는 자체 개발한 라이브러리를 이용해서 쉽게 구현하고 있었다.

// Any string unlock the component for the 1st 30-days.이 부분은 뭘 나타내는지 잘 모르겠다.

'NativeCode > api' 카테고리의 다른 글

HTTP 긁어오기 (소스파일 有)  (0) 2010.03.19
FTP  (0) 2010.03.19
CkLibrary  (1) 2010.03.19
Sendarp 사용법  (0) 2010.03.19
Event  (0) 2010.03.19

인터넷에서 mfc https 를 검색하다 우연히 찾아낸 홈페이지.

유용한 정보가 굉장히 많이 들어있다. ㅋㅋ

http://www.example-code.com/mfc/https_get_with_cert.asp

굉장히 좋더라 일단 가보자ㅋㅋ


'NativeCode > api' 카테고리의 다른 글

FTP  (0) 2010.03.19
HTTPS GET - Download HTML from HTTPS URL  (0) 2010.03.19
Sendarp 사용법  (0) 2010.03.19
Event  (0) 2010.03.19
Deadlock 4가지 조건  (0) 2010.03.19
ULONG   ulMACAddr[2],
ulSize = 6;
LPBYTE pBuffer;

if (SendARP(inet_addr(_T("207.219.70.31")), 0,
ulMACAddr, &ulSize) == NO_ERROR)
{
pBuffer = (LPBYTE) ulMACAddr;

// access the address one byte at a time m_strText.Format("%02X:%02X:%02X:%02X:%02X:%02X\r\n",
pBuffer[0],
pBuffer[1],
pBuffer[2],
pBuffer[3],
pBuffer[4],
pBuffer[5]);

m_edit1.ReplaceSel(m_strText);
}
#include <IPHlpApi.h> 를 잊지 말아야할것

'NativeCode > api' 카테고리의 다른 글

HTTPS GET - Download HTML from HTTPS URL  (0) 2010.03.19
CkLibrary  (1) 2010.03.19
Event  (0) 2010.03.19
Deadlock 4가지 조건  (0) 2010.03.19
Synchronization Function  (0) 2010.03.19

HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, 
BOOL bInitialState, LPCTSTR lpName); 

HANDLE OpenEvent(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName); 

bManualReset은 이벤트가 수동리셋 이벤트(manual)인지 자동리셋 이벤트(automatic)인지 지정하는데 TRUE이면 수동리셋 이벤트가 된다. 
bInitialState가 TRUE이면 이벤트를 생성함과 동시에 신호상태로 만들어 이벤트를 기다리는 쓰레드가 곧바로 실행을 하도록 해준다. 
이벤트도 이름(lpName)을 가지므로 프로세스간의 동기화에 사용될 수 있다. 

또한 이벤트가 임계영역이나 뮤텍스와 다른점은 
대기함수를 사용하지 않고도, 쓰레드에서 임의적으로 신호상태와 비신호상태를 설정할 수 있다는 점이다. 다음 함수를 사용한다. 

BOOL SetEvent(HANDLE hEvent); 
BOOL ResetEvent(HANDLE hEvent);



1. 함수의 원형


    HANDLE WINAPI CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, 
                                          BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName);


2. 함수의 기능
 
    이름이 정해진 또는 이름이 없는 이벤트 객체를 열거나 생성하는 함수이다. 이 객체에
    접근할수 있는 경우를 명시하고 싶다면 CreateEventEx 함수를 사용해야 한다.
   
3. 함수의 매개변수에 대한 설명
 
    3.1 lpEventAttributes
        
        SECURITY_ATTRIBUTES 구조체로 선언된 변수의 주소를 명시한다. 이 매개변수에 NULL을 명시하면 
        생성된 이벤트 핸들은 자식 프로세스에 상속되지 않는다. SECURITY_ATTRIBUTES 구조체의 항목인
        lpSecurityDescript-xor에는 새로운 이벤트의 보안 기술자(security descript-xor)에 대하여 명시하도록 되어있다.
        만약, 이 매개변수에 NULL을 사용하여 보안 기술자에 대한 명시를 하지 않은 경우에는, 새로 생성될
        이벤트 객체에 일반적인 보안 기술자가 명시된걸로 간주한다. 일반적인 보안 기술자의
        ACL(Access Control List)은 현재 로그인되어 있는 사용자의 로그인정보에 있는 ACL을 이용하거나 
        이벤트 객체를 생성한 프로세스의 ACL을 가져와 사용한다.

        ACL에 대한 좀더 자세한 내용은 아래의 항목을 참고하시기 바랍니다.
        http://www.tipssoft.com/bulletin/tb.php/FAQ/15

    3.2 bManualReset
 
        이 매개변수에 TRUE를 명시하면 발생된 이벤트 정보가 계속 유지되는 이벤트 객체를 생성한다. 
        즉, 이벤트 객체에 SetEvent 함수를 이용하여 이벤트를 설정하면 ResetEvent 함수를 호출하여
        해제하기 전까지 계속 이벤트가 발생한 상태로 유지된다.
        반대로 이 매개변수에 FALSE를 명시하면 자동으로 이벤트가 해제되는 이벤트 객체를 생성한다.
        즉, 이벤트를 기다리는 스레드가 이벤트가 발생한 상황을 체크하게 되면 자동으로 이벤트
        정보가 해제된다. 따라서 별도의 ResetEvent 함수를 호출할 필요가 없다.
 
    3.3 bInitialState
 
        이 매개변수가 TRUE라면 이벤트 객체는 이벤트가 발생한 상태로 생성되며 FALSE이면 이벤트가
        발생하지 않은 상태로 생성된다.
 
    3.4 lpName
 
        이 매개변수에는 이벤트 객체에 사용할 이름을 명시한다. 여기에 사용되는 이름의 길이는
        MAX_PATH까지로 제한되며 대소문자를 구별(Tips, tips, tipS는 서로 다른걸로 간주)한다.
        만약, 명시한 이름이 이미 생성된 다른 이벤트 객체에 사용되고 있다면 이미 생성된 객체와
        이벤트 발생정보를 공유하게 된다. 따라서 먼저 만들어진 이벤트 객체의 정보가 우선시
        되기 때문에 현재 사용한 bManualReset(3.2), bInitialState(3.3)에 명시한 정보는 무시된다.
        그리고 lpEventAttributes(3.1)에 NULL이 아닌 보안정보가 명시된 경우, 먼저 생성된 
        이벤트 객체의 보안 기술자에 의해서 사용되어질것인지 결정되며 사용가능하다고
        판단되더라도 해당 구조체의 세부항목인 보안기술자 항목에 명시된 정보는 무시된다.
        따라서 고유한 이벤트 객체를 생성하려면 이벤트 이름을 명시할때 주의해야 한다.

        만약, 이 매개변수에 명시한 이름이 이미 생성된 Semaphore, Mutex, Waitable timer, Job 
        또는 File-mapping 객체에 사용되고 있다면 이 함수는 실패할것이고 정확한 오류체크를
        위해 GetLastError 함수를 호출하면 ERROR_INVALID_HANDLE 값이 반환될 것이다. 
        즉, 하나의 객체는 다른 형식의 객체와 이름을 공유할수 없고 동일한 형식의 객체에서만
        공유여부가 결정되어진다.

        이 매개변수에 NULL을 명시하면 이름없는 이벤트 객체를 생성한다.
        
        추가적으로 객체의 이름을 명시할때, 시스템 전역적으로 객체의 이름을 공유하고 싶다면 
        사용하고자 하는 이름앞에 "Global\"이라는 접두어를 사용하고 현재 세션공간에서만
        사용하고 싶을때에는 "Local\"이라는 접두어를 사용할수 있다.

        "Global" 또는  "Local" 이라는 접두어를 사용할수 있는 커널 객체에 대해서 좀더 자세한 
        내용을 원하신다면 아래 내용을 참고하시기 바랍니다.
        ( Kernal Objcet Namespace ) -http://www.tipssoft.com/bulletin/tb.php/FAQ/17

        그리고 사적인 이름공간에도 이벤트 객체를 생성할수 있는데 이방법에 대해서 좀더
         자세한 내용을 원하신다면 아래 내용을 참고하시기 바랍니다.
         [ Object Namespaces ] - http://www.tipssoft.com/bulletin/tb.php/FAQ/18


4. 함수의 반환값
    
    이 함수가 성공적으로 수행을 완료했다면 생성된 이벤트 객체의 핸들이 반환될 것이다. 만약,
    lpName에 사용한 이름과 동일한 이름으로 생성된 이벤트 객체가 이미 생성되어 있다면
    해당 객체의 핸들이 반환될것이다. 이때, GetLastError 함수를 호출해보면 
    ERROR_ALREADY_EXISTS가 반환될 것이다. 따라서 다른 프로세스와 이벤트 객체가
    공유되는것을 원치 않는다면 CreateEvent 함수가 정상적인 이벤트 핸들값을 반환하더라도
    GetLastError 함수를 호출하여 ERROR_ALREADY_EXISTS 값이 반환되는지를 체크하는것도
    좋은 방법일 것이다.

    만약 함수가 실패한다면 NULL값이 반환될 것이고, GetLastError 함수를 호출해서 좀더 자세한 
    오류 상황을 확인할 수 있다.

'NativeCode > api' 카테고리의 다른 글

CkLibrary  (1) 2010.03.19
Sendarp 사용법  (0) 2010.03.19
Deadlock 4가지 조건  (0) 2010.03.19
Synchronization Function  (0) 2010.03.19
Win Version & Macro  (0) 2010.03.18

개 이상의 프로세스가 각각의 공유 자원을 사용하고 있으면서 다른 자원을 요구하여 무한정 대기하는 상태를 교착상태라 하는데 이는 4가지 반드시 필요한 조건이 있다. 다음의 정리 내용을 확인하자.

교착상태가 일어나기 위한 네 가지 조건(하나라도 만족하지 않으면 일어나지 않음)

상호배제(Mutual Exclusion)

▪공유 자원을 하나의 프로세스만 공유해야 한다.

점유와 대기(Hold and Wait)

▪최소한 하나의 자원을 점유하고 있고, 다른 프로세스가 점유한 자원을 이용하기 위해 대기하는 프로세스가 있어야 한다.

비선점(Non-Preemption)

▪다른 프로세스에 할당된 자원을 강제로 중지할 수 없어야 한다.

환형대기(Circular Wait)

▪공유 자원과 공유 자원을 사용하려는 프로세스가 원형으로 구성되어 자신에게 할당된 자원을 점유하면서 인접한 양쪽 프로세스의 자원을 요구해야 한다.


'NativeCode > api' 카테고리의 다른 글

Sendarp 사용법  (0) 2010.03.19
Event  (0) 2010.03.19
Synchronization Function  (0) 2010.03.19
Win Version & Macro  (0) 2010.03.18
사용법  (0) 2010.03.18

Critical Section

크리티컬 섹션을 사용하는 이유.

속도가 뮤텍스나 세마포어보다 빠르다.

동기화시 가장 주의할 점은 DeadLock이다

한 프로세스 안에서만 사용이 가능하다.


CRITICAL_SECTION m_CriticalSection;

InitializeCriticalSection( &m_CriticalSection );

DeleteCriticalSection( &m_CriticalSection );


EnterCriticalSection( &m_CriticalSection );

LeaveCriticalSection( &m_CriticalSection );



Mutex

뮤텍스는 크리티컬 섹션과 비슷해서 크리티컬 섹션이 쓰이는 곳에 대신 사용할 수 있다.

그리고 이름을 갖고 있으므로 다른 프로세스에서도 사용이 가능하다.

하지만 속도는 더 느리다.


HANDLE hMutex;

hMutex = CreateMutex( NULL, FALSE, NULL );

ReleaseMutext( hMutex );

WaitForSingleObject( hMutex, INFINITE );



Semaphore

세마포어는 뮤텍스와 유사한 동기화 객체이다. 물론 차이점이 있다. 뮤텍스는 하나의 공유 자원을 보호하기 위해 사용하지만 세모포어는 제한된 일정 개수를 가지는 자원을 보호하고 관리한다. 여기서 자원이라 함은 상당히 추상적이지만 그것이 하드웨어일 수도 윈도우 프로세스, 스레드와 같은 소페트웨어적인 것을 수도 있다.

HANDLE hSemaphore = ( HANDLE )CreateSemaphore( NULL, 3, 3, NULL );

WaitForSingleObject( hSemaphore, INFINITE );

ReleaseSemaphore( hSemaphore, 1, NULL );


HANDLE CreateSemaphore( LPSECURTY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCSTR lpName );

lMaximumCount 는 최대 사용 개수 lInitialCount = 초기값


HANDLE OpenSemaphore( DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName );

OpenSemaphore 함수로 세마포어의 핸들을 구할 수 있다.

OpenSemaphore(SYNCHRONIZE, FALSE, "MySemaphore");


BOOL ReleaseSemaphore( HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount );

자원의 사용이 끝난 스레드는 이 함수를 호출하여 사용 종료를 세마포어에 알려야 한다. lReleaseCount 로 자신이 사용한 자원의 개수를 알리는데 하나만 사용했으면 이 값은 1이고 만약 여러개를 사용했으면 사용한 만큼 자원을 풀어줘야 한다. 세번째 함수는 이전 카운트를 리턴받기 위한 참조 인수이다.


파괴 void CloseHandle( HANDLE );


// 세마포어의 카운트를 조사하는 함수

int RetSemaCount( HANDLE hSema )

{   

int result;

int count;

result = WaitForSingleObject( hSema, 0 );

if( result == WAIT_TIMEOUT )

return 0;

ReleaseSemaphore( hSema, 1, &count );

return count +1;

}

'NativeCode > api' 카테고리의 다른 글

Event  (0) 2010.03.19
Deadlock 4가지 조건  (0) 2010.03.19
Win Version & Macro  (0) 2010.03.18
사용법  (0) 2010.03.18
LoadLibrary  (0) 2010.03.18

+ Recent posts