pcap라이브
사용하여 캡쳐한 패킷의 헤더를 추출하자.
● 프로세서간 통신 ┬ 내부 – IPC
└ 외부 – Network
이건 전에 했으니 생략
● 슬라이딩 윈도우
슬라이딩 윈도(Sliding window)는 두 개의 네트워크 호스트간의 패킷의 흐름을 제어하기 위한 방법이다.
TCP와 같이 데이터의 전달을 보증하는 프로토콜에서는 패킷 하나하나가 전달되었음을 확인 신호(acknowledgement, 이하 ACK)를 받아야하며, 만약 패킷이 중도에 잘못되었거나 분실되어 확인받지 못하는 경우, 해당 패킷을 재전송해야하는 필요가 있다. 슬라이딩 윈도는 일단 '윈도(메모리 버퍼의 일정 영역)'에 포함되는 모든 패킷을 전송하고, 그 패킷들의 전달이 확인되는대로 이 윈도를 옆으로 옮김(slide)으로서 그 다음 패킷들을 전송하는 방식이다.
슬라이딩 윈도는 아직 확인을 받지 않고도 여러 패킷을 보내는 것을 가능케 하기 때문에, 매번 전송한 패킷에 대해 확인을 받아야만 그 다음 패킷을 전송하는 방법(stop-and-wait)을 사용하는 것보다 훨씬 네트워크를 효율적으로 사용할 수 있다.
[출처] 위키백과 http://ko.wikipedia.org/wiki/%EC%8A%AC%EB%9D%BC%EC%9D%B4%EB%94%A9_%EC%9C%88%EB%8F%84
교양이니까~
버퍼에 들은 데이터를 포장해 조각조각 전송하고 상대방이 잘 받았으면 ACK응답을 하고 아니면 일정시간 기다린 후 재전송한다.
다음 데이터도 포장해 전송하고 마찬가지로 ACK응답처리를 한다.
상기의 돗식도식은 출발지에서 목적지까지의 경로를 그린 ㄳ그림이고,
먼저 전송한 패킷#1이 항상 먼저 도착한다는 보장은 없다.
경로는 어떻게 선택되는지 설명을 놓쳐 모르겠으나 허브와 PC간 MAC과 IP교환에 대한 말씀을 들어보니..
라우터가 임의로 선택하는 것 같고 왠지 자세히 들어가면 복잡할 듯 하다.
하나를 배우면 열 개를 아는게 아니라 하나도 모르는게 문제다. (흑흑)
상기의 도식은 패킷의 크기(데이터)를 ACK응답을 받은 경우 상황에 따라 크기를 점차 늘려가 속도를 증가시키는 법을 그린 것이다.
● 이더넷 헤더 파일
# cd /usr/include
# cd net
# vi ethernet.h
이더넷 헤더 파일 ethernet.h는 기본 헤더파일 경로의 하위 net 디렉토리에 있다.
vi에디터로 열어보자.
초반에 이더넷헤더의 원형이 정의되어 있고 10Mb/s라는 것을 보니 100Mb/s는 다른 헤더를 사용하는 것 같다.
1: /* 10Mb/s ethernet header */ 2: struct ether_header 3: { 4: u_int8_t ether_dhost[ETH_ALEN]; /* 도착지 LAN카드 MAC주소 (6Bytes) */ 5: u_int8_t ether_shost[ETH_ALEN]; /* 출발지 LAN카드 MAC주소 (6Bytes) */ 6: u_int16_t ether_type; /* 패킷 타입 ID (2Bytes) */ 7: } __attribute__ ((__packed__)); // #pragma pack 1 과 비슷한 것으로 8: // 컴파일시 구조체의 크기가 14Bytes가 되도록 9: // 최적화하고 없으면 32bit시스템에 최적화해 10: // 16Bytes가 된다. |
32bit CPU에선 모든 레지스터가 32bit이고 메모리도 32bit단위로 취급하니 맨 아래의 16bit ID는 32bit로 크게 확장된다.
ETH_ALEN은 어디에 정의 되어 있을까? linux/if_ether.h에 있다. (너무 쉽게 낼름낼름 먹는 것 같아)
EHT_ALEN ETH_ALEN은 6으로 정의되어 있다.
u_int8_t는 unsigned char로 1Byte이니 배열로 6개의 원소를 선언할 경우 6Bytes가 된다. (아직 선언한 것은 아니다.)
ether_type은 상기와 같은 값을 가질 수 있고 빅엔디안방식으로 버퍼에 저장된다.
(Intel CPU는 리틀엔디안이고, 네트워크는 빅엔디안방식을 주로 쓰는 것 같아)
1: /* Ethernet protocol ID's */ 2: #define ETHERTYPE_PUP 0x0200 /* Xerox PUP 이건 설명 안 함 */ 3: #define ETHERTYPE_IP 0x0800 /* 헤더 다음에 오는 데이터가 IP주소를 가지고 있다. */ 4: #define ETHERTYPE_ARP 0x0806 /* Address resolution - MAC을 요청 */ 5: #define ETHERTYPE_REVARP 0x8035 /* Reverse ARP - IP를 요청 */ |
MAC요청은 IP주소로 MAC주소를 알아내는 것이고,
IP요청은 MAC주소로 IP주소를 알아내는 것이다.
(왠지 이런 개념은 windows API에 컨트롤ID와 핸들 얻어 내는 것하고 소켓 프로그래밍에서도 비슷한 것을 본 기억이 난다.)
어지럽게 떠돌아 다니는 패킷을 하나 건져 보니,
목적지의 MAC주소가 학교에 있는 허브가 아니고,
출발지의 MAC주소는 나의 LAN카드 MAC주소가 아니다.
마지막 type ID는 08 00으로 빅엔디안방식으로 그대로 읽으면 0x0800으로 다음에 오는 데이터가 IP주소이다라는 것까지 알 수 있다.
(이더넷헤더의 정의를 알았으니 실제 수집한 패킷의 헤더에 표기하기 위해 스크린샷을 찍은 것임.)
● LAN도식과 ehter_type의 ID값 설명
공유기(hub + 라우터)에 연결된 모든 컴퓨터는 부팅 후 공유기로 자신의 MAC주소와 IP주소를 알려준다.
공유기는 이 주소들을 수집하여 자신의 메모리에 저장해 두고,
컴퓨터(host)들이 전송해 온 패킷을 분석하는데 참조한다.
패킷낚시를 계속 하다 보면 상기와 같은 패킷이 걸리는데 처음 오는 목적지의 MAC주소가 모두 1이고, (0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)
출발지의 MAC주소는 내 PC의 MAC주소는 아니고,
type ID는 0x08 0x06으로 빅엔디안방식으로 읽으면 0x0806으로 ARP로 MAC주소를 요청하는 것이다.
이더넷헤더는 MAC주소가 필요하니 상대방의 IP주소로 MAC주소를 얻어내는 일이 빈번하게 일어나는 것 같다.
● Packet Analayzer 프로젝트
소스인사이드로 소스코드를 보기 좋게 편집하자.
▷ main.c
1: #include <stdio.h>
2: #include <pcap/pcap.h>
3: #include "HexaView.h"
4: #include "L1_Ethernet.h"
5:
6: int main()
7: {
8: char errbuf[PCAP_ERRBUF_SIZE]; //에러가 나면 여기에 기록됨.
9: char *cpNIC_Name; //NIC는 장치 (Network Interface Card)
10: pcap_t *stpNIC; // 구조체포인터
11: struct pcap_pkthdr stInfo; // 정보를 담을 구조체
12: const u_char *ucpData; // 데이터
13:
14:
15: // 1. 네트웍 장치명 읽어오기
16: cpNIC_Name = pcap_lookupdev(errbuf); // 장치이름을 알아오고,
17: if(NULL == cpNIC_Name) // 에러난 경우 에러버퍼에 에러가 난 이유를 저장.
18: {
19: printf(errbuf);
20: putchar('\n');
21: exit(-1); //exit()가 나은가?
22: //return -1; //리턴이 나은가?
23: }
24:
25: printf(cpNIC_Name);
26: putchar('\n');
27:
28:
29: // 2. 네트웍 장치 열기
30: // 장치명, 나중에 설명, 에러버퍼
31: stpNIC = pcap_open_live(cpNIC_Name, 1500, 1, 0, errbuf);
32: if(NULL == stpNIC) //에러가 난 경우
33: {
34: printf(errbuf);
35: putchar('\n');
36: exit(-2);
37: //return -2;
38: }
39:
40: printf("패킷캡쳐 오픈 successful(성공)\n");
41:
42: // 3. 패킷분석을 위해 패킷캡쳐
43: ucpData = pcap_next(stpNIC, &stInfo);
44:
45: // 4. 패킷내용을 출력
46: HexaView(ucpData, 160);
47: //MSDFunction(ucpData, 10);
48:
49: // 5. 이더넷 헤더
50: L1_Ethernet(ucpData);
51:
52: // last. 열린 네트웍장치를 닫는다.
53: pcap_close(stpNIC);
54:
55: return 0;
56: }
57:
▷ L1_Ethernet.c
1: #include "L1_Ethernet.h"
2:
3: void L1_Ethernet(const void *vpData)
4: {
5: const struct ether_header *stpEth = vpData;
6:
7: printf("%02X:%02X:%02X:%02X:%02X:%02X->"
8: "%02X:%02X:%02X:%02X:%02X:%02X\n"
9: , stpEth->ether_shost[0]
10: , stpEth->ether_shost[1]
11: , stpEth->ether_shost[2]
12: , stpEth->ether_shost[3]
13: , stpEth->ether_shost[4]
14: , stpEth->ether_shost[5]
15: , stpEth->ether_dhost[0]
16: , stpEth->ether_dhost[1]
17: , stpEth->ether_dhost[2]
18: , stpEth->ether_dhost[3]
19: , stpEth->ether_dhost[4]
20: , stpEth->ether_dhost[5]);
21:
22: return ;
23: }
24:
49: // 5. 이더넷 헤더
50: L1_Ethernet(ucpData);
main.c의 50행에서 L1_Ethernet을 호출하며 캡쳐한 패킷을 저장하고 있는 메모리의 주소를 넘겨준다.
3: void L1_Ethernet(const void *vpData)
5: const struct ether_header *stpEth = vpData;
L1_Ethernet()의 첫 번째 인자는 const void *타입으로 어떤 자료형이라도 받아 들일 수 있다.
이더넷구조체 ether_header의 구조체포인터 변수 stpEth를 선언하고 받은 주소값으로 초기화한다.
7: printf("%02X:%02X:%02X:%02X:%02X:%02X->"
8: "%02X:%02X:%02X:%02X:%02X:%02X\n"
9: , stpEth->ether_shost[0]
10: , stpEth->ether_shost[1]
14: , stpEth->ether_shost[5]
15: , stpEth->ether_dhost[0]
16: , stpEth->ether_dhost[1]
20: , stpEth->ether_dhost[5]);
printf()로 이더넷 구조체 포인터 변수 stpEth가 보는 관점으로 메모리의 내용을 출력한다.
ucpData는 unsigned char *형이니 1Byte씩 읽을 수 있고,
stpEth는 구조체포인터 변수니 이더넷구조체대로 읽을 수 있다.
windows API 대화상자와 컨트롤간의 통신
● 일반 컨트롤과 대화상자의 차일드 컨트롤의 차이점
에디트 컨트롤을 생성하는 코드는 아래와 같고,
1: c1 = CreateWindow(TEXT("button"), TEXT("Draw Ellipse?"), WS_CHILD | WS_VISIBLE |
2: BS_AUTOCHECKBOX, 20, 20, 160, 25, hWnd, (HMENU)0, g_hInst, NULL);
9번째 인자로 컨트롤의 ID(여기서는 0)를 넣고 컨트롤이 생성이 되고 생성된 컨트롤을 다룰 수 있는 핸들이 반환된다.
그런데 대화상자를 생성하는 코드를 보면,
1: // 리턴값은 어떤 버튼을 눌렀는지 확인. IDOK, IDCANCEL
2: iResult = DialogBox(g_hInst, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, AboutDlgProc); //대화상자 생성
두 번째 인자로 다이얼로그의 ID를 넣고 대화상자를 생성한 뒤에,
EndDialog( )로 대화상자를 닫아야 블로킹이 풀리고 EndDialog()의 두 번째 인자가 전달되어 리턴된다.
여기서 잘 보면 대화상자 내부의 컨트롤들의 ID와 핸들은 관여하지 않는다는 것을 알 수 있다.
(대화상자는 컨트롤 종합선물세트(컨테이너)이니까 일일이 핸들을 반환하거나 인자로 넣기 곤란하겠지..)
대화상자 내부의 컨트롤들(차일드 컨트롤)의 값을 읽고 쓰려면 그 컨트롤의 핸들이 필요하다.
( malloc()로 할당받은 메모리를 엑세스하려면 가리키는 포인터가 필요하듯이 핸들이 필요함.)
● 차일드 컨트롤의 ID를 입력하여 핸들을 얻어내는 함수 GetDlgItem()
HWND GetDlgItem(HWND hDlg, // 대화상자의 핸들 (소속) int nlDDlgItem); // 핸들을 얻고자 하는 컨트롤의 ID |
아래와 같이 리소스를 만들 때 대화상자내의 차일드 컨트롤들의 ID도 정할 수 있으니,
엑세스 하고 (핸들을 얻고) 싶은 컨트롤의 ID를 두 번째 인자로 넣으면 그 컨트롤을 엑세스할 수 있는 핸들이 리턴된다.
● 차일드 컨트롤의 핸들을 입력하여 ID를 알아내는 함수 GetDlgCtrlID()
int GetDlgCtrlID(HWND hwndCtl); // 컨트롤의 핸들 –> 컨트롤의 ID |
이 함수는 좀 생뚱맞게 핸들로 ID를 알아낸다. 사용빈도가 별로 높지 않으니 있다는 것만 알아두자.
이런 변환은 어디서 많이 본 것 같은데 TCP/IP소켓 프로그래밍에서 32bit주소와 도메인, dotted주소 변환과 비슷하다.
왜 ID와 핸들을 따로 두어 프로그래머들을 귀찮게 하는가?
일단 컨트롤은 윈도우이며 따라서 윈도우를 관리하기 위해서는 핸들이 필요하다.
그런데 핸들이라는 것은 그 특성상 운영체제가 일방적으로 발급하는 것이기 때문에 번호의 연속성이 없으며,
그러다 보니 반복적인 처리에는 사용할 수 없다는 문제가 있다.
그래서 연속성을 가질 수 있고 사용자가 직접 번호를 지정할 수 있는 ID가 필요한 것이다.
ID는 연속적이라 for루프로 반복작업이 가능하고 CheckRadioButton( )로 라디오버튼의 그룹범위 지정도 가능하다.
● 대화상자 –> 차일드 컨트롤 메시지전송
대화상자가 차일드 컨트롤을 프로그래밍하는 주된 방법은 SendMessage( )로 메시지를 보내는 것인데,
이 함수는 대상 윈도우의 핸들을 요구한다. SendMessage( )의 첫 번째 인자로 윈도우의 핸들을 넣는데 여기에 엑세스하고 싶은 차일드 컨트롤의 핸들을 넣는다. 그래서 GetDlgItem( )로 핸들을 알아내 SendMessage( )의 인자로 넘겨준다.
SendMessage(GetDlgItem(hDlg, 컨트롤의 ID),… ); |
이렇게 사용하면 번거로우니 아래와 같은 함수를 사용하여,
LONG SendDlgItemMessage(HWND hDlg, // 대화상자의 핸들 int nID, // 메시지를 받을 컨트롤의 ID UINT Msg, // 메시지 WPARAM wParam, // 파라미터 w (이건 그 때 그 때 달라요~) LPARAM lParam); // 파라미터 l (이건 그 때 그 때 달라요~) |
알고 있는 (resouce.h에 등록된) 차일드 컨트롤의 ID와 소속된 대화상자의 핸들을 사용하여 한 번에 해결한다.
이 함수는 GetDlgItem( )와 SendMessage( )를 호출하는 레퍼함수라고 할 수 있다.
(레퍼함수는 printf( )처럼 다른 함수를 호출하는 함수)
● 대화상자 <-> 컨트롤간 문자열 교환
UINT GetDlgItemText(HWND hDlg, // 대화상자 윈도우 핸들 int nID, // 엑세스할 컨트롤의 ID LPTSTR lpString, // 문자열을 담을 버퍼 int nMaxCoutn); // 문자열길이 |
GetDlgItemText( )는 컨트롤로부터 문자열을 읽어와 버퍼에 저장하는 함수이고,
fgets( )와 비슷하게 읽어 올 문자수를 지정할 수 있다.
BOOL SetDlgItemText(HWND hDlg, // 대화상자 윈도우 핸들 int nID, // 엑세스할 컨트롤의 ID LPCTSTR lpString); // 쓸 문자열 |
SetDlgItemText( )는 컨트롤에 문자열을 쓰는 함수이다.
● 대화상자 <-> 컨트롤간 정수 교환
UINT GetDlgItemInt(HWND hDlg, // 대화상자 윈도우 핸들 int nID, // 엑세스할 컨트롤의 ID BOOL *lpTranslated, // 에러 검사할 필요없으면 NULL BOOL bSigned); // TRUE = 부호있는 정수, FALSE = 부호없는 정수 리턴 |
GetDlgItemInt( )는 컨트롤로부터 정수값을 읽어와 리턴하는 함수이다.
변환의 성공 여부를 리턴받기 위한 포인터 변수. 정수로 변환하지 못할 문자열이면 이 변수로 FALSE가 리턴된다.
성공 여부를 조사할 필요가 없으면 NULL로 줄 수 있다. - 참조URL: http://winapi.co.kr
BOOL SetDlgItemInt(HWND hDlg, // 대화상자 윈도우 핸들 int nID, // 엑세스할 컨트롤의 ID UINT uValue, // 컨트롤에 대입할 정수값 BOOL bSigned); // TRUE = 부호있는 정수, FALSE = 부호없는 정수 리턴 |
SetDlgItemInt( )는 컨트롤에 정수값을 대입하는 함수이다.
● 대화상자내의 에디트 컨트롤의 정수값을 기억하는 예제
1: // --대화상자 메시지 처리--
2: // WM_INITDIALOG메세지
3: BOOL OnInitDialog(HWND hDlg, WPARAM wParam, LPARAM lParam) //대화상자 생성시 초기화 위한 메시지
4: {
5: // 9월 28일 수정.
6: SetDlgItemInt(hDlg, IDC_EDIT1, Age, FALSE); //부호없는 정수값을 에디트 컨트롤로 보냄.
7:
8: return TRUE;
9: }
10:
11: // WM_COMMAND메시지
12: BOOL OnDlgCommand(HWND hDlg, WPARAM wParam, LPARAM lParam) //대화상자 컨트롤 메시지처리
13: {
14: switch(LOWORD(wParam)) {
15: case IDOK:
16: // 9월 28일 수정.
17: Age = GetDlgItemInt(hDlg, IDC_EDIT1, NULL, FALSE); //에디트로부터 부호없는 정수값을 받아 리턴. 에러검사X
18: EndDialog(hDlg, IDOK);
19: return TRUE;
20: case IDCANCEL:
21: EndDialog(hDlg, IDCANCEL); //CANCEL버튼 누르면 아무 일도 하지 않고 대화상자 소멸.
22: return TRUE;
23: }
24:
25: return TRUE;
26: }
대화상자가 생성되면 WM_INITDIALOG메세지가 발생하여 OnInitDialog( )가 호출된다.
6: SetDlgItemInt(hDlg, IDC_EDIT1, Age, FALSE); //부호없는 정수값을 에디트 컨트롤로 보냄.
SetDlgItemInt( )로 에디트박스(ID는 IDC_EDIT1)에 변수Age에 저장된 값을 부호를 없애고 기록한다.
(Age는 전역변수, DATA영역 –> heap영역)
※ 초기화 되지 않은 전역변수는 BSS영역에 배치되나 DATA가 맘에 들어서! DATA영역.
17: Age = GetDlgItemInt(hDlg, IDC_EDIT1, NULL, FALSE); //에디트로부터 부호없는 정수값을 받아 리턴. 에러검사X
GetDlgItemInt( )는 에디트박스(ID는 IDC_EDIT1)로부터 부호없는 정수값을 에러검사하지 않고 읽어와 리턴하여 Age에 저장한다.
(heap영역 –> DATA영역)
heap영역은 실행 중에 생성과 소멸을 반복한다. 대화상자가 생성되면 자리가 확보되고 대화상자가 소멸되면 모두 사라진다.
※ 실제 heap영역에 윈도우(컨트롤)의 데이터가 있는지 확인은 하지 않았으나 heap영역이외에 적재될 곳이 없다고 생각함.
<실행결과>
어제 작성한 About예제코드에 상기의 코드들을 삽입하고 전역변수 Age를 선언하고 에디트리소스를 추가한 뒤에,
ID는 디폴트로 두고 실행하면 Age는 전역변수니 초기값이 0이다.
이 값이 마우스 왼쪽클릭하여 대화상자 생성 후 바로 기록되니 왼쪽 스크린샷과 같이 0이 출력된다.
에디트박스를 클릭하여 숫자를 300으로 수정하고 OK버튼을 클릭하여 대화상자를 닫으면,
아무 것도 없는 공허한 부모윈도우가 다시 보이고, 이 상태로 종료하지 말고 차분하게 마우스왼쪽클릭하면,
대화상자가 생성되고 DATA영역에 저장된
● 대화상자 <-> 컨트롤간 논리값 교환
상기 예제에서와 같이 에디트 컨트롤 엑세스는 문자열과 정수로 하고,
체크박스와 같이 체크가 되어 있나 아닌가의 상태만 있는 경우 BOOL형을 사용한다.
BOOL CheckDlgButton(HWND hDlg, // 부모 원도우 핸들 (대화상자) int nIDButton, // 버튼의 ID (체크박스는 “button”클래스) UINT nCheck); // 원하는 체크 상태 |
CheckDlgButton( )는 체크박스나 라디오 버튼 등 체크 상태를 가지는 버튼의 체크 상태를 변경한다.
이 때 해당 컨트롤로 BM_SETCHECK 메시지를 보낸다고 하는데 이는 우리가 처리할 필요 없으니 몰라도 된다.
세 번째 인자로 체크 상태를 지정하며 아래의 세 값 중 하나를 지정한다.
nCheck값 | 설명 |
BST_CHECKED | 체크 상태로 바꿈 |
BST_UNCHECKED | 체크 상태를 해제 |
BST_INDETERMINATE | 체크 상태를 알 수 없는 grayed로 체크. BS_3STATE, BS_AUTO3STATE 스타일을가지는 체크박스에만 사용해야함 |
UINT IsDlgButtonChecked(HWND hDlg, // 부모 윈도우 핸들 (대화상자) int nIDButton); // 버튼의 ID (체크박스는 “button”클래스) |
IsDlgButtonChecked( )는 체크박스의 상태를 검사하여 체크상태면 TRUE를 리턴하고,
체크해제상태면 FALSE를 리턴한다.
리턴값은 상기의 CheckDlgButton( )의 nCheck값 표를 참조하면 된다.
● 부모윈도우와 대화상자간 데이터 전달 예제: InfoDlg
▷ main.cpp (시작 + CALLBACK함수 정의)
1: // winAPI p.242~244 InfoDlg
2: // 2011년 9월 28일 작성
3: // 작성자: 김수만
4:
5: #include <windows.h> // 모든 API함수들의 원형과 사용하는 상수들이 정의되어 있음.
6: #include "MsgProc.h" //add
7: #include "resource.h"
8:
9: LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //메시지처리함수
10: HINSTANCE g_hInst; //핸들 인스턴스
11: LPCTSTR lpszClass = TEXT("InfoDlg"); //클래스명
12:
13: //add
14: typedef struct MESSAGEMAP {
15: UINT iMessage;
16: LRESULT (*lpfnMsgProc)(HWND, WPARAM, LPARAM);
17: } MESSAGEMAP;
18:
19: typedef struct DLGMESSAGEMAP {
20: UINT iMessage;
21: BOOL (*lpfnMsgProc)(HWND, WPARAM, LPARAM);
22: } DLGMESSAGEMAP;
23:
24:
25: //시작함수.
26: int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
27: {
28: HWND hWnd;
29: MSG Message;
30: WNDCLASS WndClass;
31: g_hInst = hInstance;
32:
33: WndClass.cbClsExtra = 0;
34: WndClass.cbWndExtra = 0;
35: WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
36: WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
37: WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
38: WndClass.hInstance = hInstance;
39: WndClass.lpfnWndProc = WndProc;
40: WndClass.lpszClassName = lpszClass;
41: WndClass.lpszMenuName = NULL;
42: WndClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
43: RegisterClass(&WndClass);
44:
45:
46: hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW,
47: CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
48: NULL, (HMENU)NULL, hInstance, NULL);
49:
50: ShowWindow(hWnd, nCmdShow);
51:
52: while(GetMessage(&Message, NULL, 0, 0)) {
53: TranslateMessage(&Message);
54: DispatchMessage(&Message);
55: }
56:
57: return (int)Message.wParam;
58: }
59:
60: //운영체제가 호출하는 CALLBACK함수 (메시지 처리)
61: LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
62: {
63: //add
64: int i;
65:
66: static MESSAGEMAP MessageMaps[] = {
67: {WM_CREATE, OnCreate},
68: {WM_COMMAND, OnCommand},
69: {WM_HSCROLL, OnHScroll},
70: {WM_LBUTTONDOWN, OnLButtonDown},
71: {WM_RBUTTONDOWN, OnRButtonDown},
72: {WM_MOUSEMOVE, OnMouseMove},
73: {WM_LBUTTONUP, OnLButtonUp},
74: {WM_LBUTTONDBLCLK, OnLButtonDBLCLK},
75: {WM_PAINT, OnPaint},
76: {WM_DESTROY, OnDestroy}
77: };
78:
79: for(i = 0 ; i < sizeof(MessageMaps) / sizeof(MessageMaps[0]) ; ++i)
80: {
81: if(MessageMaps[i].iMessage == iMessage)
82: {
83: return (*MessageMaps[i].lpfnMsgProc)(hWnd, wParam, lParam);
84: }
85: }
86: /////////////////////////////////
87:
88:
89:
90: return(DefWindowProc(hWnd, iMessage, wParam, lParam));
91: }
92:
93:
94: BOOL CALLBACK InfoDlgProc(HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam)
95: {
96: // 20110927 add
97: int i;
98:
99: static DLGMESSAGEMAP DMessageMaps[] = {
100: //메시지 추가할 것.
101: {WM_INITDIALOG, OnInitDialog},
102: {WM_COMMAND, OnDlgCommand}
103: };
104:
105: for(i = 0 ; i < sizeof(DMessageMaps) / sizeof(DMessageMaps[0]) ; ++i)
106: {
107: if(DMessageMaps[i].iMessage == iMessage)
108: {
109: return (*DMessageMaps[i].lpfnMsgProc)(hDlg, wParam, lParam);
110: }
111: }
112:
113: return FALSE; //정의되어 있지 않은 메시지는 모두 FALSE, 정의된건 TRUE
114: }
▷ MsgProc.cpp (메시지 처리)
1: //MsgProc.cpp
2:
3: #include "MsgProc.h"
4: #include "resource.h"
5:
6: int x;
7: int y;
8: TCHAR str[128];
9: HWND hWndMain; //이건 어디서 쓰지?
10:
11: LRESULT OnCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
12: {
13: hWndMain = hWnd; //모르겠다.
14: x = 100; //초기x좌표
15: y = 100; //초기y좌표
16: lstrcpy(str, TEXT("String")); //str배열에 초기값으로 "String"문자열 복사.
17:
18: return 0;
19: }
20:
21: LRESULT OnCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
22: {
23: return 0;
24: }
25:
26: LRESULT OnHScroll(HWND hWnd, WPARAM wParam, LPARAM lParam) //수평스크롤 변화시 발생하는 메시지처리
27: {
28: return 0;
29: }
30:
31: LRESULT OnLButtonDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
32: {
33: if(DialogBox(g_hInst, MAKEINTRESOURCE(IDD_DIALOG1), // IDD_DIALOG1 리소스를 사용하여 대화상자생성
34: hWnd, InfoDlgProc) == IDOK) // OK버튼을 눌렀다면
35: {
36: InvalidateRect(hWnd, NULL, TRUE); //부모윈도우 전체영역을 무효화함.
37: }
38:
39: return 0;
40: }
41:
42: LRESULT OnRButtonDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
43: {
44: return 0;
45: }
46:
47:
48: LRESULT OnMouseMove(HWND hWnd, WPARAM wParam, LPARAM lParam)
49: {
50:
51: return 0;
52: }
53:
54: LRESULT OnLButtonUp(HWND hWnd, WPARAM wParam, LPARAM lParam)
55: {
56: return 0;
57: }
58:
59: LRESULT OnLButtonDBLCLK(HWND hWnd, WPARAM wParam, LPARAM lParam)
60: {
61:
62: return 0;
63: }
64:
65:
66: LRESULT OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
67: {
68: HDC hdc;
69: PAINTSTRUCT ps;
70:
71: hdc = BeginPaint(hWnd, &ps); //DC얻기
72:
73: TextOut(hdc, x, y, str, lstrlen(str)); // 에디트에 넣은 좌표와 문자열을 표시
74:
75: EndPaint(hWnd, &ps);
76:
77: return 0;
78: }
79:
80: LRESULT OnDestroy(HWND hWnd, WPARAM wParam, LPARAM lParam)
81: {
82: PostQuitMessage(0);
83: return 0;
84: }
85:
86:
87: //////////////////////////////////////////////////////////////////////////////////////
88:
89: BOOL OnInitDialog(HWND hDlg, WPARAM wParam, LPARAM lParam)
90: { //대화상자 초기화시
91: SetDlgItemText(hDlg, IDC_STR, str); //IDC_STR에디트는 str배열에 저장된 문자열로 초기화
92: SetDlgItemInt(hDlg, IDC_X, x, FALSE); //IDC_X에디트는 x좌표표시
93: SetDlgItemInt(hDlg, IDC_Y, y, FALSE); //IDC_Y에디트는 y좌표표시
94:
95: return TRUE; //메시지를 처리했으니 TRUE리턴
96: }
97:
98: BOOL OnDlgCommand(HWND hDlg, WPARAM wParam, LPARAM lParam)
99: { // 대화상자내의 메시지 발생시
100: switch(LOWORD(wParam)) { // 통지메시지를 검사해
101: case IDOK: // OK버튼을 눌렀다면
102: GetDlgItemText(hDlg, IDC_STR, str, 128); //IDC_STR에디트로부터 문자열 읽어와 str배열에 저장
103: x = GetDlgItemInt(hDlg, IDC_X, NULL, FALSE); //IDC_X에디트로부터 x좌표 읽어와 리턴
104: y = GetDlgItemInt(hDlg, IDC_Y, NULL, FALSE); //IDC_Y에디트로부터 y좌표 읽어와 리턴
105: EndDialog(hDlg, IDOK); //대화상자를 소멸시키고 DialogBox()의 리턴값은 IDOK
106: return TRUE; //메시지를 처리했으니 TRUE리턴
107:
108: case IDCANCEL: //Cancel버튼을 눌렀다면
109: EndDialog(hDlg, IDCANCEL); //대화상자를 소멸시키고 DialogBox()의 리턴값은 IDCANCEL
110: return TRUE; //메시지를 처리했으니 TRUE리턴
111: }
112:
113: return FALSE; //정의된 메시지처리가 없으면 FALSE를 리턴함.
114: } //DefWindowProc()와 비슷.
<실행결과>
처음 실행하면 좌표(100, 100)에 “String”문자열만 달랑 출력되어 있는 윈도우가 보인다.
윈도우 내부에서 마우스 왼쪽 클릭하여 대화상자를 띄운 후 좌표와 문자열을 입력하고 OK버튼을 클릭하면,
대화상자에서 입력했던 좌표에 문자열이 출력된다.
<소스분석>
DATA, BSS, heap영역은 모두 컴퓨터 메인보드에 꽂힌 메모리에 존재하는 영역으로 상기의 그림과 같이 따로 나뉘어 있는 것이 아니다.
여기서 설명하고자 하는 것은 전연변수는 BSS영역에 위치하므로 윈도우가 존재하는 한 그 값을 계속 유지한다는 것이고,
대화상자와 자식윈도우인 컨트롤들은 모두 실행 중에 생성되므로 heap영역이니 값을 유지할 수 없다.
(핸들은 윈도우 생성시 받고 소멸 시 돌려준다.(제거))
그래서 설정값을 부모윈도우의 전역변수가 가지고 있게 하는 것이다. stack영역은 함수 리턴 시 제거되니 이 영역도 안 된다.
그리고 메모리를 선형으로 그리면 통신하는 기분이 안 드니 좌우에 배치해 예쁘게 했다.
소감 - 오늘 배운 내용은 모두 통신에 관련된 것이다.
사람 人자는 서로 지탱하는 모양으로 사람은 서로 돕고 살아야 한다고 생각한다.
의사소통 능력을 키워 제대로 된 인간이 되자. 기술보다 먼저 인간이 되야 한다고 생각한다.
암튼 커뮤니케이션은 재미있다.
'코스웨어 > 11년 내장형하드웨어' 카테고리의 다른 글
[내장형]이수란_2011.10.06 (10) | 2011.10.06 |
---|---|
[내장형]김동화_2011년10월5일 일일보고서 (12) | 2011.10.05 |
[내장형]최남식_2011년10월4일 일일보고서 (19) | 2011.10.04 |
[내장형]이성재_20110929 일일보고서 (17) | 2011.09.29 |
[내장형]이상만_110927 (14) | 2011.09.28 |
잠깐 내려 놓는 시간.. (7) | 2011.09.27 |
[내장형]윤병도_20110926일일보고서 (18) | 2011.09.26 |
[내장형]공정우_2011.9.23 윈 API 보고서 (정우) (15) | 2011.09.24 |