Win32 API 강사 : 이준근 (egenie@korea.com) 1. 윈도우 프로그래밍의 이해 1. API의 Positioning Application MFC VB ATL/COM owl Delphi DirectX OPEN/GL API Windows Kernel HardWare HAL 2. Windows의 구성 • Windows의 특징 한 시점에 도는 함수가 여러 개 다.!! – 32bit 선점형 멀티태스킹과 멀티스레딩을 지원하는 그래픽 운영 체제이다. • 비선점형 : 운영체제 타임 슬라이싱 기법!! A B A B A – 프로그램이 스스로 제어를 다른 프로그램에게 넘겨 주어야 한다. – GUI환경이다. ack(1M) 영역(200k) 역공간(1K) Heap • “GUI는 개인용 컴퓨터 산업의 가장 중요하고 유일한 ‘대중적 합 의’라는 점은 이제 명백해 졌다.” - Charles Simonyi • GUI의 특징 – 1. 더 이상 컴퓨터나 새로운 프로그램의 사용법을 배우는데 많은 시 간을 소비하지 않아도 된다. – 2. 일관된 사용자 인터페이스를 이용한다는 것은 메뉴와 대화상자를 구성할 때 Windows에 내장된 루틴을 이용한다는 것을 의미한다. – ‘동적 연결’이라는 개념을 중심으로 동작한다. • Windows에서 실행되는 프로그램은 ‘동적 연결 라이브러리’라고 하 는 파일에 존재하는 루틴을 공유한다. 동적 연결 C => .obj + .Lib + .dll => Exe + .dll 2. Windows의 구성 – 프로그램은 시스템에 연결되어 있는 장치의 종류를 몰라도 된다. • 화면 및 프린터와 같은 그래픽 디스플레이 장치의 하드웨어를 직접 사용하지 않는다. 3. 첫 번째 API프로그램 #include <windows.h> int WINAPI WinMain (HINSTANCE hInstance,HINSTANCE hPrevInstance, PSTR szCmdLine, int CmdShow) { MessageBox (NULL, "Hello, Windows 98!","HelloMsg", MB_OK) ; return 0 ; } • #include <windows.h> – 다른 Windows헤더 파일을 포함하는 마스터 include 파일이다. – – – – – WINDEF.H 기본형식 정의 WINNT.H 유니코드지원을 위한 형식 정의 WINBASE.H Kernel함수 WINUSER.H 사용자 인터페이스 함수 WINGDI.H 그래픽 장치 인터페이스 함수 4. 프로그램 진입점 int WINAPI WinMain (HINSTANCE hInstance,HINSTANCE hPrevInstance, PSTR szCmdLine, int CmdShow) – WINAPI windef.h에 정의되어 있다. • #define WINAPI __stdcall – Parameters • HINSTANCE hInstance : – 프로그램의 인스턴스 핸들 • HINSTANCE hPrevInstance : – 항상 NULL이다. • PSTR szCmdLine : – 프로그램을 실행할 때 사용된 명령줄 • int iCmdShow : – 최대화할 것인지 또는 아이콘화 하여 작업 표시줄에 표시할 것인지 지 정 __stdcall은 함수를 호출하면 호출된 함수쪽에서 리턴하기 전에 스텍에 저장된 인자들을 정리하는 방식으로 16비트시절의 PASCAL호출규약과 동일하다. 4. 프로그램 진입점 – MessageBox : • 메시지 박스를 나타내는 함수 4. 사고의 전환을 위하여 • 도스 프로그래밍과 윈도우 프로그래밍의 차이점 – 도스 프로그램 : 절차적 (또는 순차적) • 프로그램의 실행 흐름이 프로그래머가 기술한 코드의 순서에 따라 진행한다. • 사용자의 키보드 조작이나 마우스의 움직임을 프로그래머가 일일이 알아서 직접 제어해야 한다. • 외부에서 무슨 일(이벤트)이 일어났는지 프로그래머가 알아내야 하고 이에 따른 처리까지 맡아서 해야 한다. – 윈도우 프로그램 : • 이벤트 구동(Event-Driven)방식 또는 메시지 처리 방식 – 프로그램의 실행 흐름을 윈도우 시스템과 일을 분담하여 처리한다. – 외부에서 일어나는 일을 윈도우 시스템이 감지하여 해당 프로그램에 메 시지를 전달하여 알려준다. – 프로그램은 이에 대한 처리만 한다. – 프로그래밍이 한결 수월해 진다. • Ex) Spy++ Test 4. 사고의 전환을 위하여 도스 프로그래밍 윈도우 프로그래밍 프로그래머가 제어하는 데로 순차적 으로 실행. 프로그램의 실행 흐름을 윈도우시스템 과 일을 분담하여 처리한다. 프로그래머가 외부에서 어떤 일이 발생했는지를 알아내야 하고 이에 따른 처리까지 맡아서 한다. 외부에서 발생하는 모든 일을 윈도우 시스템이 감지하여 Message를 전달하여 알려 준다 ( 프로그램은 이에 대한 처리만 해주면 된다 ) 5. 윈도우 프로그래밍과 친해지자 이벤트(Event)와 메시지(Message) 메시지 루프(Message Loop) 메시지 큐 (Queue) 핸들(Handle) 윈도우 프로시져 리소스(Resource) 인스턴스(Instance) 하드웨어 운용 방식 5. 윈도우 프로그래밍과 친해지자 • 이벤트(Event)와 메시지(Message) – 사용자가 키보드의 특정 키를 누르거나 마우스의 좌측 버튼을 클릭하는 등의 일을 할 때 이벤트가 발생한다. – 이벤트가 발생하면 윈도우 OS는 이름 감지하여 해당 프로그램 으로 메시지를 전달한다. – 메시지에는 마우스의 좌측 버튼을 눌렀을 때 커서의 위치등과 같은 부가 정보가 함께 포함되어 있다. – WinUser.h [마우스에 관련된 Message들] #define WM_MOUSEMOVE #define WM_LBUTTONDOWN #define WM_LBUTTONUP #define WM_LBUTTONDBLCLK #define WM_RBUTTONDOWN #define WM_RBUTTONUP 0x0200 0x0201 0x0202 0x0203 0x0204 0x0205 ( Winuser.h) 5. 윈도우 프로그래밍과 친해지자 typedef struct tagMSG { HWND UINT WPARAM LPARAM DWORD POINT } MSG; 윈도우 번호를 나타냄!! hwnd; // 메시지를 받을 윈도우의 핸들 message; //전달되는 메시지 유형 wParam; //메시지와 관련된 부가적인 정보 lParam; //메시지와 관련된 부가적인 정보 time; // 메시지가 전달된 시간 pt; //메시지가 큐에 저장될 때의 마우스 위치 typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM 32비트lParam; DWORD time; POINT pt; }MSG; WM_LBUTTONDOWN Key Flag LOWORD(lParam) X좌표(16비 HIWORD(lParam) Y좌표(16비 5. 윈도우 프로그래밍과 친해지자 • 메시지 큐(Message Queue) – 사용자의 컴퓨터 조작에 의해 발생한 이벤트는 메시지형태로 만 들어져 윈도우 OS가 관리하는 “메시지 큐”에 저장된다. – 윈도우 시스템 전체를 관리하기 위한 [시스템 메시지 큐]와 응용 프로그램마다 별도 갖고 있는 [프로그램 메시지 큐]가 있다. 자료구조를 저장하고 이를 처리 System Message Queue program Message Queue 5. 윈도우 프로그래밍과 친해지자 시스템 메시지 큐 시스템 분배기 윈도우 OS Dispatch Thread (RIT:Raw Input Thread) 프로그램 메시지 큐 프로그램 A 프로그램 메시지 큐 프로그램 B 5. 윈도우 프로그래밍과 친해지자 • 메시지 루프 – OS로부터 전달된 메시지를 보관하는 큐에 어떤 메시지가 들어 왔는지를 지속적으로 감시하고 분석해주는 부분이 필요하다. While( GetMessage( &msg,NULL,0,0) ) 값을 바꾸기 위해 Message 구조체의 주소 { TranslateMessage( &msg ); DispatchMessage( &msg ); } wm_quit message x 를 누르면은 GetMessage의 리턴값이 0이 된다!! 5. 윈도우 프로그래밍과 친해지자 • 윈도우 프로시저(Window procedure) – 자신이 만든 함수이지만 자신이 직접 호출하지 않고 운영체제에 서 호출하는 함수를 콜백함수라고 한다. – 함수 앞에 CALLBACK으로 선언 – 윈도우 프로시저는 콜백함수이다. – 모든 윈도우는 해당 윈도우 프로시저를 갖고 있다. 윈도우의 특징을 – 메시지 루프에서 해석한 메시지를 구체적으로 처리하는 기능을 수행 – Call Back 함수는 윈도우에서 자동으로 불려지는 함수이다.그러므 로 Language independent 해야 한다. • (호출형식을 통일해야 한다) – windef.h LRESULT CALLBACK WndProc( HWND hWnd, UNIT uMgs, WPARAM wParam,LPARAM lParam); #define CALLBACK __stdcall #define WINAPI __stdcall WinMain에서 Message를 얻어와서 WndProc를 실행해준다!! 5. 윈도우 프로그래밍과 친해지자 LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { HDC hdc ; PAINTSTRUCT ps ; RECT rect ; switch (iMsg) { case WM_CREATE : PlaySound ("hellowin.wav", NULL, SND_FILENAME | SND_ASYNC) ; return 0 ; case WM_PAINT : hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; DrawText (hdc, "Hello, Windows 95!", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, iMsg, wParam, lParam) ; } 5. 윈도우 프로그래밍과 친해지자 • 핸들 (Handle) – 핸들은 프로그램에서 현재 사용중인 객체들을 식별하기 위해 윈 도우 OS가 부여하는 고유 번호이다. – 그러면 왜 포인터를 이용하지 않고 핸들을 이용할까? Ex) HWND HCURSOR HICON HMENU 윈도우에 대한 핸들 커서에 대한 핸들 아이콘에 대한 핸들 메뉴에 대한 핸들 총 메모리가 80KB라고 가정하자. 이때 30KB의 그림판, 40KB를 차지 OS자체에서 조각모음을 하는 Visual C++을 실행시키면 그림판 (30KB) 비주얼 C++ (40KB) 여유공간 (10KB) 핸들을 사용하는 것이 메모리의 주소만 바뀌고 바뀌지 않음 그림판을 종료하고 20KB를 필요로 하는 메모장을 실행시키면 메모장 (20KB) 여유공간 (10KB) 비주얼 C++ (40KB) 여유공간 (10KB) 5. 윈도우 프로그래밍과 친해지자 => 내부적으로 윈도우는 메모리 블록을 이리저리 이동시켜 필요 공간을 확보한다. (윈도우OS의 메모리 관리 기법) Ex) HGLOBAL GlobalAlloc( UINT uFlags, // allocation attributes DWORD dwBytes // number of bytes to allocate); GMEM_FIXED Allocates fixed memory. The return value is a pointer. GMEM_MOVEABLE Allocates movable memory. 메모리 블록이 이리저리 이동되는 상황에서 도스에서처럼 포인터를 직접 활용한다면 OS가 할당된 메모리 블록을 함부로 이동시킬 수 없게 되고, 이동시킨다고 해도 프로그램쪽에서 이동되는 상황을 알 수 없기 때문에 엉뚱한 메모리 주소를 참조하게 될 것이다. 5. 윈도우 프로그래밍과 친해지자 • 인스턴스 – 프로그램은 명령들이 나열된 코드 영역(Code Segment)과 데이 터를 보관하는 데이터 영역(Data Segment)으로 구분 – 동일한 프로그램에 코드 영역까지 별도의 메모리를 할당하면 메 모리만 낭비하게 된다. – 실제 메모리 상에 할당된 객체를 인스턴스(Instance)라 한다. – 코드 영역에 대한 모듈 인스턴스(Module Instance) – 데이터 영역에 대한 데이터 인스턴스(Data Instance)두 개의 인 스턴스가 있다. 코드영역 Read Only 코드 영역 공유 -> 메모리 아끼기 위해 코드영역 (모듈 인스턴스) 메모장1 데이터 영역_1 (데이터 인스턴스) 데이터 영역_2 (데이터 인스턴스) 메모장2 5. 윈도우 프로그래밍과 친해지자 • 메모리 관리상에서 콘솔과 차이가 확실한 차 리소스(Resource) 이 이때문에 리소스가 이를인터페이스를 관리한다. – 메뉴,아이콘,커서,비트맵 등 사용자 구성하는 자원 들의 정적 데이터를 말한다. – 프로그램 실행 중 변경되지 않는 정형화된 데이터로 C나 C++같은 언어로 기술하지 않고 리소스 스크립트에 의해 정의된다. IDR_MAINFRAME MENU PRELOAD DISCARDABLE BEGIN POPUP "파일(&F)" BEGIN MENUITEM "새 파일(&N)\tCtrl+N", MENUITEM "열기(&O)...\tCtrl+O", MENUITEM "저장(&S)\tCtrl+S", MENUITEM "다른 이름으로 저장(&A)...", MENUITEM SEPARATOR MENUITEM "인쇄(&P)...\tCtrl+P", MENUITEM "인쇄 미리 보기(&V)", MENUITEM "인쇄 설정(&R)...", MENUITEM SEPARATOR MENUITEM "최근 파일", MENUITEM SEPARATOR MENUITEM "종료(&X)", END END 사용자가 필요하면 메모리에 남아 있고 필 내려간다. ID_FILE_NEW ID_FILE_OPEN ID_FILE_SAVE ID_FILE_SAVE_AS ID_FILE_PRINT ID_FILE_PRINT_PREVIEW ID_FILE_PRINT_SETUP ID_FILE_MRU_FILE1,GRAYED ID_APP_EXIT 5. 윈도우 프로그래밍과 친해지자 – 리소스는 프로그램 코드와 분리하여 작성되며 자체 컴파일과정을 갖는다. – 리소스 스크립트 파일(.RC)은 리소스 컴파일러(RC.EXE)에 의해 이진화된 리소스 파일 (.RES)이 만들어 진다. – 리소스를 별도로 정의하는 이유는 메모리를 효율적으로 사용하기 위함이다. – 리소스에는 정적인 데이터가 있기 때문에 일반 변수와는 다른 메 모리 관리를 한다. – 보통 리소스 데이터는 필요한 시점에 파일로부터 로딩이 되며 여 러 개의 프로그램이 실행되어 메모리가 부족시 리소스 데이터가 할당된 메모리 블록을 이동(Moveable)기키거나 폐기 (Discardable)한다. 리소스가 따로 분리되어 나와 있다. 5. 윈도우 프로그래밍과 친해지자 GUI 아이콘,커서,다이얼로그등 중복/증가로 파일크기가 커진다. 리소스를 프로그램과 분리, 필요 시에 Call 링크 시 소스코드와 리소스가 결합 리소스는 메모리 부족 시 폐기 가능(DISCARDABLE) 필요 시EXE나 DLL파일로부터 로딩 프로그램이 차지하는 메모리가 감소되어 메모리 효율성이 높아지고 소스 코드와 분리되어 작성되므로 프로그램 작성이 편리하다. 5. 윈도우 프로그래밍과 친해지자 • 하드웨어 운용 방식(장치 독립적인 그래픽 인터페이스) – 도스에서는 비디오 메모리나 프린터 포트 등을 직접 제어함으로 써 특정한 하드웨어 장치에 종속된 프로그램을 작성하였다. – 장치 종속적 • 비디오 카드나 프린터 등과 같은 장치의 종류나 모델에 따라 출력방 법이 달라진다. • 특정 장치에서만 제한적으로 실행되기 때문에 프로그램에서 지원하 지 않는 장치는 사용할 수 없다. • 프로그램에서 필요한 디바이스 드라이버를 제공해야 한다. – 장치 독립적 • 장치의 종류와 모델에 상관없이 일관된 처리 방법으로 하드웨어 장 치들을 다룰 수 있다. • 출력하고자 하는 출력장치의 종류에 상관없이 출력 방법이 동일하 다. • 필요한 디바이스 드라이버를 윈도우 OS가 내장하고 있으면 어떤 하 드웨어 장치건 상관없이 동일하게 작동되기 때문에 프로그래머는 중요한 기능에 보다 많은 시간을 투자할 수 있다. • 출력 장치에 대한 정보를 포함하는 구조체 디바이스 컨텍스트를 가지 고 GDI함수를 호출하여 출력 작업을 수행 5. 윈도우 프로그래밍과 친해지자 case WM_PAINT : hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; DrawText (hdc, "Hello, Windows 95!", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; EndPaint (hwnd, &ps) ; return 0 ; Display.drv GDI32.DLL Printer.drv Keyboard.drv Mouse.drv 윈도우 프로그램 USER32.DLL KERNEL32.DLL Sound.drv System.drv Timer H/W Comm.drv RS-232 Ms-Dos file I/O Memory Management 5. 윈도우 프로그래밍과 친해지자 커널모듈 KRNL386.EXE KERNEL32.DLL Windows OS의 핵심 메모리관리,파일 입출력, 프로그램의 로드와 실행 등 운영체제의 기본기능 수행 GDI 모듈 GDI.EXE GDI32.DLL 화면이나 프린터 같은 장치의 출력을 관장하며 메시지를 관리 (펜,브러시,폰트,비트맵..) 사용자 인터페이스 모듈 USER.EXE USER32.DLL 윈도우,다이얼로그,메뉴, 커서,아이콘 등과 같은 인터페이스 객체들을 관리한다. 2. Windows 와 메시지 1. 첫번째 API프로그램 #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("HelloWin") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra =0; wndclass.cbWndExtra =0; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, "The Hello Program", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } // window class name // window caption // window style // initial x position // initial y position // initial x size // initial y size // parent window handle // window menu handle // program instance handle // creation parameters LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps ; RECT rect ; switch (message) { case WM_CREATE: PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME |SND_ASYNC) ; return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; DrawText (hdc, "Hello, Windows 98!", -1, &rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 2. 첫 번째 API프로그램 분석 • 헝거리언 표기법 – MS사의 전설적인 프로그래머 Charles Simonyi를 기리는 뜻으로 붙여진 이름) – char szAddress[50]; 해당 변수의 데이터 타입을 나타내는 접두어 표기 접두어 데이터 타입 접두어 데이터 타입 a Array i index b BOOL l long int ch Character lp long pointer cb Count of bytes n integer dw unsigned long sz NULL로 끝나는 문자열 Handle w unsigned int h 2. 첫 번째 API프로그램 분석 • 접두어 CS 클래스 스타일 CW 윈도우 생성 옵션 DT 문자열 그리기 옵션 IDI 아이콘에 대한 ID IDC 커서에 대한 ID MB 메시지 상자 옵션 SND 사운드 옵션 WM 윈도우 메시지 WS 윈도우 스타일 2. 첫 번째 API프로그램 분석 • 새로운 데이터 형식들 UINT LRESULT HINSTANCE HWND 부호 없는 정수 LONG형 unsigned int 프로그램 자신의 인스턴스 핸들 윈도우 핸들 HDC 장치 컨텍스트에 대한 핸들 PSTR char * 16비트 시절 32비트 시절 WPARAM 16bit unsigned int인 WORD형 LPARAM 32bit signed long형인 LONG형 WPARAM 32bit unsigned int인 WORD형 LPARAM 32bit signed long형인 LONG형 2. 첫 번째 API프로그램 분석 • 새로운 데이터 형식들 MSG WNDCLASS 메시지 구조체 윈도우 클래스 구조체 PAINTSTRUCT Paint구조체 RECT Rectangle구조체 • 윈도우 클래스 등록하기 – 윈도우는 항상 윈도우 클래스를 기반으로 하여 생성된다. – 모든 Button윈도우는 동일한 윈도우 클래스를 기반으로 하여 생 성된다. – 윈도우 클래스는 윈도우의 특성들을 정의한다. – 응용 프로그램은 윈도우를 생성하기 전에 반드시 RegisterClass 를 호출하여 윈도우 클래스를 등록해야 한다. 2. 첫 번째 API프로그램 분석 typedef struct { UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; } WNDCLASS, * PWNDCLASS,NEAR * NPWNDCLASSA, FAR * LPWNDCLASSA; – wndclass.style=CS_HREDRAW|CS_VREDRAW; • 수평 또는 수직윈도우가 변경될 때마다 완전히 새로 클래스의 Brush로 다시 칠하게 된다. • winuser.h에 정의되어 있다. 2. 첫 번째 API프로그램 분석 /* Class styles */ #define CS_VREDRAW #define CS_HREDRAW #define CS_DBLCLKS #define CS_OWNDC #define CS_CLASSDC #define CS_PARENTDC #define CS_NOCLOSE #define CS_SAVEBITS #define CS_BYTEALIGNCLIENT #define CS_BYTEALIGNWINDOW #define CS_GLOBALCLASS #define CS_IME 0x0001 0x0002 0x0008 0x0020 0x0040 0x0080 0x0200 0x0800 0x1000 0x2000 0x4000 0x00010000 – wndclass.lpfnWndProc = WndProc ; • 윈도우와 연결되는 윈도우 함수를 연결한다. – wndclass.cbClsExtra = 0; – wndclass.cbWndExtra = 0; • 윈도우 클래스에 여유공간을 두거나, 윈도우에 여유공간을 예약하는 데 사용 2. 첫 번째 API프로그램 분석 – cbClsExtra • 윈도우 클래스에서 사용하고자 하는 여분의 메모리 양 • 바이트 수로 지정한다. • 운영체제는 윈도우 클래스를 등록할 때 이 멤버가 지정하는 만큼의 메모리를 추가로 할당해 준다 • SetClassLong, GetClassLong함수로 이 메모리를 사용한다. – cbWndExtra • 개별 윈도우에서 사용하고자 하는 여분의 메모리 양을 지정한다. • 운영체제는 개별 윈도우가 만들어질 때마다 이 멤버가 지정하는 만 큼의 메모리를 추가로 할당해 준다. • GetWindowLong,SetWindowLong함수로 이 메모리를 사용한다. 2. 첫 번째 API프로그램 분석 – wndclass.hInstance = hInstance • 프로그램의 인스턴스 핸들 – wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION) • 이 윈도우 클래스를 기반으로 하여 생성된 모든 윈도우에 대한 아이콘 을 설정한다. • 프로그램이 실행될 때 아이콘은 Windows작업 표시줄에 나타난다. • 프로그램에 있는 ICON을 지정하려면 NULL에 hInstance를 넣는다. – wndclass.hCursor = LoadCursor(NULL,IDI_APPLICATION) • 윈도우에서 사용하는 커서를 지정한다. – wndcalss.hbrBackground = GetStocObject(WHITE_BRUSH) ; • Window의 배경색을 지정한다. • Windows에는 여러 가지의 표준 혹은 ‘stock’브러쉬가 있다. – wndclass.hMenu = NULL; • 윈도우의 메뉴를 지정한다. – wndclass.lpszClassName=szAppName; • 클래스는 반드시 이름을 부여 받아야 한다. – RegisterClass를 호출하여 윈도우 클래스를 등록 2. 첫 번째 API프로그램 분석 if (!RegisterClass(&wndclass)) { MessageBox(NULL,TEXT(“This program requires Windows NT!”, szAppName,MB_ICONERROR); return 0; } 2. 첫 번째 API프로그램 분석 • 윈도우 생성하기 hwnd = CreateWindow ( szAppName, "The Hello Program", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); // // // // // // // // // // // window class name window caption window style initial x position initial y position initial x size initial y size parent window handle window menu handle program instance handle creation parameters – CreateWindow를 호출하여 윈도우 생성 – CreateWindow시에는 윈도우의 개별적인 특징을 지정한다. • 윈도우의 크기, 윈도우의 위치,… 2. 첫 번째 API프로그램 분석 – WS_OVERLAPPENDWINDOW • 제목표시줄,시스템메뉴,두꺼운 윈도우 크기 변경 경계, 제목표시줄 의 최소화,최대화,닫기 단추 • (WINUSER.H에서 확인) #define WS_OVERLAPPEDWINDOW ( WS_OVERLAPPED | WS_CAPTION | \ WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | \ WS_MAXIMIZEBOX) – 부모 윈도우 핸들 • 부모 윈도우를 만드는 경우는 NULL • 자식 윈도우를 만드는 경우는 부모 윈도우의 핸들값 – 윈도우 메뉴 핸들 • 윈도우 클래스에서 지정한 메뉴를 사용하려면 NULL • 윈도우 클래스에서 지정한 메뉴를 사용하지 않으려면 메뉴의 핸들을 지정한다. – 프로그램 인스턴스 핸들 • WinMain의 매개변수로 프로그램에 전달되는 인스턴스의 핸들을 지 정한다. – Return값 2. 첫 번째 API프로그램 분석 – CreateWindow()후에는 윈도우가 내부적으로 생성되어 있다. • 화면에는 보이지 않는 상태 – 화면에 출력하려면 • ShowWindow(hwnd,iCmdShow) – hwnd : CreateWindow()의 리턴 값. – iCmdShow : 초기에 화면에 윈도우가 어떻게 표시되는 지를 나타낸다. – 윈도우를 화면에 나타내며 지정된 브러쉬로 윈도우의 Client영역을 칠한다. 매크로 상수 의미 SW_HIDE 윈도우를 숨긴다. SW_MINIMIZE 윈도우를 최소화시키고 활성화시키지 않는다. SW_RESTORE 윈도우를 활성화시킨다. SW_SHOW 윈도우를 활성화시켜 보여준다. SW_SHOWNORMAL 윈도우를 활성화시켜 보여준다. 2. 첫 번째 API프로그램 분석 • 메시지 루프 – 메시지 루프는 세 개의 함수 호출로 이루어져 있으며 전체 루프는 while문으로 싸여져 있어 무한히 반복되는 구조를 가지고 있다. – 이벤트가 발생하면 Windows는 이벤트를 메시지로 변환하여 프로 그램의 메시지 큐에 저장한다. – GetMessage( &msg,NULL,0,0) • 메시지 큐로부터 메시지를 읽어 온다. • 읽어들인 메시지는 첫 번째 인수가 지정하는 MSG 구조체에 저장된다. • WM_QUIT 메시지인 경우 0을 Return한다. 그 이외의 메시지이면 TRUE를 리턴한다. • 나머지 세 개의 인수는 읽어 들일 메시지의 범위를 지정한다. 잘 사용 하지 않는다. 2. 첫 번째 API프로그램 분석 while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } typedef struct { HWND UINT WPARAM LPARAM DWORD POINT } MSG , * PMSG tagMSG hwnd; message;//어떤 종류의 메시지인가를 나타낸다. wParam; // 전달된 메시지의 부가적인 정보 lParam; // time; // 메시지가 메시지 큐에 저장된 시간 pt; //메시지 큐에 저장된 시간의 마우스 좌표 typedef struct tagPOINT { LONG x; LONG y; } POINT, * PPOINT; 2. 첫 번째 API프로그램 분석 – TranslateMessage(&msg); • 키보드 번역 – DisplatchMessage(&msg); • 메시지 큐에서 꺼낸 메시지를 윈도우 프로시저로 전달한다. • 윈도우 프로시저 LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam, LPARAM lParam) – 윈도우 프로시저는 항상 RegisterClass를 호출하여 등록한 특정 한 윈도우 클래스와 연결되어 있다. – SendMessage라는 함수를 통해서 자신의 윈도우 프로시저를 간접적으로 호출한다. – Message는 WINUSER.H에 define되어 있다. – 윈도우 프로시저가 메시지를 처리할 때에 반드시 윈도우 프로시 저로부터 0이 반환되어야 한다. – 윈도우 프로시저가 처리하지 않는 모든 메시지들은 반드시 DefWindowProc라는 이름의 Windows함수에 전달되어야 한다. 2. 첫 번째 API프로그램 분석 • WM_PAINT 메시지 – 윈도우 클라이언트 영역의 일부가 무효화 영역이 되면 발생한다. – 윈도우가 다시 그려져야 함을 알린다. – CS_HREDRAW,CS_VREDRAW는 윈도우의 크기가 변경될 때 WM_PAINT를 발생시킨다. – WM_PAINT는 항상 hdc=BeginPaint(hwnd,&ps) – EndPaint( hwnd, &ps)를 이용하여 처리한다. – BeginPaint는 dc의 handle를 반환한다. – BeginPaint호출중에 Windows는 클라이언트 영역의 배경을 윈도우 클래스에 지정한 브러쉬로 지운다. – GetClientRect(hwnd,&rect)는 클라이언트의 크기를 Return한다. – DrawText (hdc, "Hello, Windows 98!",-1, &rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; • 텍스트를 그려준다. • 세 번째 인자 –1은 문자열이 NULL로 종료됨을 의미 2. 첫 번째 API프로그램 분석 – DC란 • 출력에 필요한 모든 정보를 가지는 데이터 구조체이다. • 어떤 폰트를 사용할 것이지, 선의 색상과 굵기, 채움 무늬와 색상, 출력방법 과 같은 정보를 담고 있다. • 모든 출력은 윈도우를 기준으로 하며 이러한 원점에 대한 정보도 DC에 있다. • 현재 상황에서 어떤 영역이 출력이 허가된 영역인가를 보고 허가된 영역에만 출력을 내 보낸다. • GDI모듈에 의해 관리된다. – DrawText(HDC uDC, LPCTSTR lpString, int nCount, LPRECT lpRect, UINT uFormat); 값 설명 DT_LEFT 수평 왼쪽 정렬한다. DT_RIGHT 수평 오른쪽 정렬한다. DT_CENTER 수평 중앙 정렬한다. DT_BOTTOM 사각영역의 바닥에 문자열을 출력한다. DT_VCENTER 사각영역의 수직 중앙에 문자열을 출력한다. DT_WORDBREAK 사각영역의 오른쪽 끝에서 자동 개행되도록 한다. DT_SINGLELINE 한 줄로 출력한다. DT_NOCLIP 사각영역의 경계를 벗어나도 문자열을 자르지 않고 그대로 출력한다. 2. 첫 번째 API프로그램 분석 – int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) • hWnd : 메시지 박스의 오너 윈도우 핸들 값 설명 MB_ABORTRETRYIGN ORE Abort, Retry, Ignore 세 개의 버튼이 나타난다. MB_OK OK 버튼 하나만 나타난다. MB_OKCANCEL OK, Cancel 두 개의 버튼이 나타난다. MB_RETRYCANCEL Retry, Cancel 두 개의 버튼이 나타난다. MB_YESNO Yes, No두 개의 버튼이 나타난다. MB_YESNOCANCEL Yes, No, Cancel 세 개의 버튼이 나타난다. 값 설명 값 설명 IDABORT Abort 버튼 클릭 IDOK OK 버튼 클릭 IDCANCEL Cancel 버튼 클릭 ID_RETRY Retry 버튼 클릭 IDIGNORE Ignore 버튼 클릭 ID_YES Yes 버튼 클릭 IDNO No 버튼 클릭 2. 첫 번째 API프로그램 분석 • WM_DESTROY메시지 – 윈도우가 종료되고 있음을 나타낸다. – 사용자가 종료단추를 클릭하거나 시스템 메뉴의 종료 메뉴를 선택 하면 WM_DESTOROY를 발생시킨다. – PostQuitMessage(0); • 메시지큐에 WM_QUIT를 삽입한다. • GetMessage()는 메시지가 WM_QUIT면 0을 리턴한다. – 종료 버튼 클릭시 종료순서 • • • • • • => => => => => => WM_SYSCOMMAND WM_CLOSE DestoryWindow() WM_DESTORY PostQuitMessage()를 call 메시지큐에 WM_QUIT를 넣는다. 2. 첫 번째 API프로그램 분석 • 큐에 저장되는 메시지와 큐에 저장되지 않는 메시지 – 큐에 저장되는 메시지 • 키 스트로크 메시지 – WM_KEYDOWN,WM_KEYUP,WM_CHAR • 마우스 메시지 – WM_LBUTTONDOWN,WM_RBUTTONDOWN,WM_MOUSEMOVE • 타이머 메시지 : WM_TIMER • 다시 그리기 메시지 : WM_PAINT • 종료 메시지 : WM_QUIT – 큐에 저장되지 않는 메시지 • • • • • 특정한 Window함수 호출의 결과이다. CreateWindow() : WM_CREATE ShowWindow() : WM_SIZE,WM_SHOWWINDOW UpdateWindow() : WM_PAINT메시지를 윈도우 프로시저에 보낸다. WM_COMMAND – 메시지 큐와 윈도우 프로시저는 동시에 수행되지 않는다. – DispatchMessage()는 윈도우 프로시저가 Window에게 컨트롤을 넘기기 전까지는 반환되지 않는다. WndPoc가 종료되기 전까지는 종료되 3. 문자출력 1. 문자출력 • 클라이언트 영역 – 전체윈도우에서 제목표시줄이나 윈도우의 경계선,메뉴 바,도구 모 음,상태 바,스코롤 바를 제외한 영역 – 프로그램이 자유로이 그릴 수 있는 부분 – 프로그램은 다른 윈도우와 비디오 디스플레이를 공유해야 한다. • 그리기와 다시 그리기 – 윈도우에서는 윈도우의 클라이언트 영역에만 텍스트와 그래픽을 그릴 수 있다. – Windows는 다른 응용 프로그램이 덮어버린 윈도우를 저장하지 않 는다. – 윈도우는 WM_PAINT메시지를 전달하여 윈도우의 클라이언트 영역 의 일부가 그려질 필요가 있다는 것을 윈도우 프로시저에 알린다. 2. WM_PAINT메시지 – 다음과 같은 이벤트가 발생할 때 WM_PAINT가 발생한다. • 윈도우를 옮기거나 제거했을 때 이전에 감추어졌던 영역이 보이게 될 때 • 윈도우의 크기를 조절할 때 • 클라이언트의 일부를 Scroll했을 때 • InvalidateRect나 InvalidateRgn을 사용했을 때 • Windows가 윈도우의 일부가 겹친 대화상자나 메시지 상자를 제거했 을때 • 메뉴가 나타났다가 사라질때 • 풍선 도움말이 나타났을 때 – 저장 후 복구할 때 • 마우스 커서가 클라이언트 영역을 지나갈 때 • 아이콘이 클라이언트 영역을 지나서 드래그될 때 3. 유효 영역과 무효 영역 – 무효화 영역 • 만약에 대화상자가 제거되었을 경우에는 대화상자가 있던 영역만 복원 되면 된다. 이 영역을 무효화영역 또는 업데이트 영역이라고 한다. • 무효 영역은 Windows가 작업의 메시지 큐에 WM_PAINT메시지를 보 내게 된다. – 페인트 정보 구조체 • Windows는 내부적으로 각 윈도우를 위한 ‘페인트 정보 구조체를 가지 고 있다. • 이 구조체는 무효 영역을 둘러싸고 있는 가장 작은 사각형의 좌표를 가 지고 있다. – InvalidateRect • InvalidateRect를 호출하여 자기 자신의 클라이언트 영역에서 지정한 사각형을 무효로 만들 수 있다 – GetUpdateRect() • 무효화 사각형의 좌표를 얻을 수 있다. – BeginPaint를 호출한 후 전체 클라이언트 영역이 유효화된다. – ValidateRect() • 클라이언트 영역에 있는 모든 사각형을 유효화한다. • 메시지 큐에 있는 모든 WM_PAINT는 제거된다. 4. GDI – 윈도우의 클라이언트 영역에 그리기를 하려면 Windows의 GDI함 수를 사용해야 한다. • TextOut (hdc,x,y,psText,iLength); • 화면에 가장 일반적으로 사용하는 출력함수는 이다. • 널 종료 문자열을 사용하지 않으므로 문자열의 길이를 인수로 반드시 입력해 주어야 한다. • SetTextAlign(HDC hdc, UINT fMode); • 문자열의 정렬 방법을 변경하는 함수이다. 값 설명 TA_TOP 지정한 좌표가 상단 좌표가 된다. TA_BOTTOM 지정한 좌표가 하단 좌표가 된다. TA_CENTER 지정한 좌표가 수평 중앙 좌표가 된다. TA_LEFT 지정한 좌표가 수평 왼쪽 좌표가 된다. TA_RIGHT 지정한 좌표가 수평 오른쪽 좌표가 된다. TA_UPDATECP 지정한 좌표대신 CP를 사용하며 문자열 출력 후에 CP를 변경한다. TA_NOUPDATE CP CP를 사용하지 않고 지정한 좌표를 사용하며 CP를 변경 하지 않는다. 4. GDI • 디폴트 정렬 상태는 TA_TOP | TA_LEFT로 되어 있으며 지정한 좌표를 좌 상단으로 하여 문자열이 출력된다. – 장치 컨텍스트 • • • • • 장치 컨텍스트 핸들은 GDI함수들에 대한 허가권이다. 핸들이 있어야 클라이언트 영역에 그래픽을 그릴 수 있다. GDI에 의해 내부적으로 관리되는 데이터 구조체이다. 프로그램이 그리기가 필요할 때 hDC를 먼저 얻어야 한다. 핸들을 얻을 때 Windows는 내부 장치 컨텍스트를 기본 속성값으로 채 운다. • 프로그램이 클라이언트 영역에 그리기를 종료했으면 장치 컨텍스트 핸 들을 제거 해야 한다. 5. 장치 컨텍스트 핸들 얻기 : 1 – WM_PAINT시 • BeginPaint를 이용하여 얻는다. • WM_PAINT시에는 꼭 BeginPaint()와 EndPaint()를 해야 한다. case WM_PAINT: hdc = BeginPaint (hwnd,&ps); [GDI함수 이용] EndPaint(hwnd,&ps); return 0; case WM_PAINT: return 0; =>절대 안 된다. • 이렇게 하면 클라이언트의 무효화 영역이 유효화가 되지 않으므로 WM_PAINT를 메시지 큐에 다시 집어 넣는다. 5. 장치 컨텍스트 핸들 얻기 : 1 • 페인트 정보 구조체 – – – – BeginPaint를 호출할 때 Window는 이 구조체의 필드를 채운다. 처음 3개만 사용할 수 있다. 나머지는 Windows가 내부 적으로 사용 fErase는 대부분 FALSE(0)이다. • FALSE : Windows가 이미 유효하지 않는 영역의 배경을 지웠다는 것 을 의미 typedef struct tagPAINTSTRUCT { HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; BYTE rgbReserved[32]; } PAINTSTRUCT; 5. 장치 컨텍스트 핸들 얻기 : 1 – Windows는 WNDCLASS구조체의 hbrBackground에서 지정된 브 러쉬를 사용하여 배경을 지운다. • Wndclass.hbrBackground= (HBRUSH)GetStockObject(WHITE_BRUSH); – InvalidateRect를 호출하여 배경을 지울 것인지를 결정할 수 있다. • InvalidateRect(hwnd,NULL,FALSE) – 무효한 영역을 지우지 않고 화면에 그대로 남긴다. • InvalidateRect(hwnd,NULL,TRUE) – 무효한 영역을 지운다. 5. 장치 컨텍스트 핸들 얻기 : 2 • WM_PAINT이외의 Message에서 DC얻기 – GetDC와 ReleaseDC는 언제나 같은 메시지 핸들러 안에 있어야 한다. – GetDC는 어떤 무효화 영역도 유효화 하지 않는다. – 전체 클라이언트 영역을 유효하게 하려면 • ValidateRect(hwnd,NULL) – 전체 윈도우를 무효화 하려면 • Invalidate(hwnd,NULL); – 전체 윈도우를 대상으로 작업을 하려면 • GetWindowDC hdc = GetDC(hwnd); [GDI 함수 사용] ReleaseDC(hwnd,hdc); 바로 무효화 영역을 유효화시키기 위하여 !! updateWindow() 를 호출 wM_ paint는 우선 순위가 낮다. 6. 문자의 크기 Typedef struct tagTEXTMETRIC { LONG tmHeight; LONG tmAscent; LONG tmDescent; LONG tmInternalLeading; LONG tmExternalLeading; LONG tmAveCharWidth; LONG tmMaxCharWidth; } TEXTMETRIC, * PTEXTMETERIC; TEXTMETRIC tm; hdc = GetDC(hwnd); GetTextMetrics(hdc,&tm); ReleaseDC(hwnd,hdc); 7. 실습 /*----------------------------------------------SYSMETS.H -- System metrics display structure -----------------------------------------------*/ #define NUMLINES ((int) (sizeof sysmetrics / sizeof sysmetrics [0])) struct { int iIndex ; TCHAR * szLabel ; TCHAR * szDesc ; } sysmetrics [] = { SM_CXSCREEN, TEXT ("SM_CXSCREEN"), TEXT ("Screen width in pixels"), SM_CYSCREEN, TEXT ("SM_CYSCREEN"), TEXT ("Screen height in pixels"), SM_CXVSCROLL, TEXT ("SM_CXVSCROLL"), TEXT ("Vertical scroll width"), SM_CYHSCROLL, TEXT ("SM_CYHSCROLL"), TEXT ("Horizontal scroll height"), SM_CYCAPTION, TEXT ("SM_CYCAPTION"), TEXT ("Caption bar height"), SM_CXBORDER, TEXT ("SM_CXBORDER"), TEXT ("Window border width"), SM_CYBORDER, TEXT ("SM_CYBORDER"), TEXT ("Window border height"), SM_CXFIXEDFRAME, TEXT ("SM_CXFIXEDFRAME"), TEXT ("Dialog window frame width"), SM_CYFIXEDFRAME, TEXT ("SM_CYFIXEDFRAME"), TEXT ("Dialog window frame height"), SM_CYVTHUMB, TEXT ("SM_CYVTHUMB"), TEXT ("Vertical scroll thumb height"), SM_CXHTHUMB, TEXT ("SM_CXHTHUMB"), TEXT ("Horizontal scroll thumb width"), SM_CXICON, TEXT ("SM_CXICON"), TEXT ("Icon width"), SM_CYICON, TEXT ("SM_CYICON"), TEXT ("Icon height"), SM_CXCURSOR, TEXT ("SM_CXCURSOR"), TEXT ("Cursor width"), SM_CYCURSOR, TEXT ("SM_CYCURSOR"), TEXT ("Cursor height"), SM_CYMENU, TEXT ("SM_CYMENU"), TEXT ("Menu bar height"), SM_CXFULLSCREEN, TEXT ("SM_CXFULLSCREEN"), TEXT ("Full screen client area width"), SM_CYFULLSCREEN, TEXT ("SM_CYFULLSCREEN"), TEXT ("Full screen client area height"), SM_CYKANJIWINDOW, TEXT ("SM_CYKANJIWINDOW"), TEXT ("Kanji window height"), SM_MOUSEPRESENT, TEXT ("SM_MOUSEPRESENT"), TEXT ("Mouse present flag"), SM_CYVSCROLL, TEXT ("SM_CYVSCROLL"), TEXT ("Vertical scroll arrow height"), SM_CXHSCROLL, TEXT ("SM_CXHSCROLL"), TEXT ("Horizontal scroll arrow width"), SM_DEBUG, TEXT ("SM_DEBUG"), TEXT ("Debug version flag"), SM_SWAPBUTTON, TEXT ("SM_SWAPBUTTON"), TEXT ("Mouse buttons swapped flag"), SM_CXMIN, TEXT ("SM_CXMIN"), TEXT ("Minimum window width"), SM_CYMIN, TEXT ("SM_CYMIN"), TEXT ("Minimum window height"), SM_CXSIZE, TEXT ("SM_CXSIZE"), TEXT ("Min/Max/Close button width"), SM_CYSIZE, TEXT ("SM_CYSIZE"), TEXT ("Min/Max/Close button height"), SM_CXSIZEFRAME, TEXT ("SM_CXSIZEFRAME"), TEXT ("Window sizing frame width"), SM_CYSIZEFRAME, TEXT ("SM_CYSIZEFRAME"), TEXT ("Window sizing frame height"), SM_CXMINTRACK, TEXT ("SM_CXMINTRACK"), TEXT ("Minimum window tracking width"), SM_CYMINTRACK, TEXT ("SM_CYMINTRACK"), TEXT ("Minimum window tracking height"), SM_CXDOUBLECLK, TEXT ("SM_CXDOUBLECLK"), TEXT ("Double click x tolerance"), SM_CYDOUBLECLK, TEXT ("SM_CYDOUBLECLK"), TEXT ("Double click y tolerance"), SM_CXICONSPACING, TEXT ("SM_CXICONSPACING"), TEXT ("Horizontal icon spacing"), SM_CYICONSPACING, TEXT ("SM_CYICONSPACING"), TEXT ("Vertical icon spacing"), SM_MENUDROPALIGNMENT, TEXT ("SM_MENUDROPALIGNMENT"), TEXT ("Left or right menu drop"), SM_PENWINDOWS, TEXT ("SM_PENWINDOWS"), TEXT ("Pen extensions installed"), SM_DBCSENABLED, TEXT ("SM_DBCSENABLED"), TEXT ("Double-Byte Char Set enabled"), SM_CMOUSEBUTTONS, TEXT ("SM_CMOUSEBUTTONS"), TEXT ("Number of mouse buttons"), SM_SECURE, TEXT ("SM_SECURE"), TEXT ("Security present flag"), SM_CXEDGE, TEXT ("SM_CXEDGE"), TEXT ("3-D border width"), SM_CYEDGE, TEXT ("SM_CYEDGE"), TEXT ("3-D border height"), SM_CXMINSPACING, TEXT ("SM_CXMINSPACING"), TEXT ("Minimized window spacing width"), SM_CYMINSPACING, TEXT ("SM_CYMINSPACING"), TEXT ("Minimized window spacing height"), SM_CXSMICON, TEXT ("SM_CXSMICON"), TEXT ("Small icon width"), SM_CYSMICON, TEXT ("SM_CYSMICON"), TEXT ("Small icon height"), SM_CYSMCAPTION, TEXT ("SM_CYSMCAPTION"), TEXT ("Small caption height"), SM_CXSMSIZE, TEXT ("SM_CXSMSIZE"), TEXT ("Small caption button width"), SM_CYSMSIZE, TEXT ("SM_CYSMSIZE"), TEXT ("Small caption button height"), SM_CXMENUSIZE, TEXT ("SM_CXMENUSIZE"), TEXT ("Menu bar button width"), SM_CYMENUSIZE, TEXT ("SM_CYMENUSIZE"), TEXT ("Menu bar button height"), SM_ARRANGE, TEXT ("SM_ARRANGE"), TEXT ("How minimized windows arranged"), SM_CXMINIMIZED, TEXT ("SM_CXMINIMIZED"), TEXT ("Minimized window width"), SM_CYMINIMIZED, TEXT ("SM_CYMINIMIZED"), TEXT ("Minimized window height"), SM_CXMAXTRACK, TEXT ("SM_CXMAXTRACK"), TEXT ("Maximum draggable width"), SM_CYMAXTRACK, TEXT ("SM_CYMAXTRACK"), TEXT ("Maximum draggable height"), SM_CXMAXIMIZED, TEXT ("SM_CXMAXIMIZED"), TEXT ("Width of maximized window"), SM_CYMAXIMIZED, TEXT ("SM_CYMAXIMIZED"), TEXT ("Height of maximized window"), SM_NETWORK, TEXT ("SM_NETWORK"), TEXT ("Network present flag"), SM_CLEANBOOT, TEXT ("SM_CLEANBOOT"), TEXT ("How system was booted"), SM_CXDRAG, TEXT ("SM_CXDRAG"), TEXT ("Avoid drag x tolerance"), SM_CYDRAG, TEXT ("SM_CYDRAG"), TEXT ("Avoid drag y tolerance"), SM_SHOWSOUNDS, TEXT ("SM_SHOWSOUNDS"), TEXT ("Present sounds visually"), SM_CXMENUCHECK, TEXT ("SM_CXMENUCHECK"), TEXT ("Menu check-mark width"), /*---------------------------------------------------SYSMETS1.C -- System Metrics Display Program No. 1 (c) Charles Petzold, 1998 ----------------------------------------------------*/ #define WINVER 0x0500 #include <windows.h> #include "sysmets.h" LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("SysMets1") ; HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra =0; wndclass.cbWndExtra =0; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics No. 1"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static int cxChar, cxCaps, cyChar ; HDC hdc ; int i; PAINTSTRUCT ps ; TCHAR szBuffer [10] ; TEXTMETRIC tm; switch (message) { case WM_CREATE: hdc = GetDC (hwnd) ; GetTextMetrics (hdc, &tm) ; cxChar = tm.tmAveCharWidth ; cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ; cyChar = tm.tmHeight + tm.tmExternalLeading ; ReleaseDC (hwnd, hdc) ; return 0 ; case WM_PAINT : hdc = BeginPaint (hwnd, &ps) ; for (i = 0 ; i < NUMLINES ; i++) { TextOut (hdc, 0, cyChar * i, sysmetrics[i].szLabel,lstrlen (sysmetrics[i].szLabel)) ; TextOut (hdc, 22 * cxCaps, cyChar * i, sysmetrics[i].szDesc, lstrlen (sysmetrics[i].szDesc)) ; SetTextAlign (hdc, TA_RIGHT | TA_TOP) ; TextOut (hdc, 22 * cxCaps + 40 * cxChar, cyChar * i, szBuffer, wsprintf (szBuffer, TEXT ("%5d"),GetSystemMetrics (sysmetrics[i].iIndex))) ; SetTextAlign (hdc, TA_LEFT | TA_TOP) ; } EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 4. 기본적인 그리기 1. GDI 철학 – 비디오 출력과 프린터에 그래픽을 책임지고 있는 Windows의 하 위 시스템은 그래픽 디바이스 인터페이스(GDI)이다. – Windows 98과 Microsoft NT에서의 그래픽은 주로 동적 링크 라 이브러리 GDI32.DLL로부터 얻어진 함수들에 의해 처리된다. – GDI의 목적 중의 하나는 장치에 구애 받지 않고 그래픽을 지원하 는 것이다. – Windows는 디폴트로 픽셀을 기반으로 하는 좌표계를 사용 – 가로축과 세로축이 0에서 32,767까지 가능한 가상 좌표계를 사 용 2. GDI함수 호출 – 장치 컨텍스트를 얻고 해제하는 함수들 • • • • BeginPaint EndPaint GetDC ReleaseDC – 장치 컨텍스트에 대한 정보를 획득하는 함수들 • GetTextMetrics – 그리는 함수들 • TextOut – 장치 컨텍스트의 속성을 설정하고 얻어내는 함수들 • SetTextColor • SetTextAlign – GDI개체를 다루는 함수들 • CreatePen • CreatePenIndirect 3. 장치 컨텍스트 핸들 얻기 – WM_PAINT hdc = Beginpaint(hwnd,&ps) EndPaint(hwnd,&ps); – WM_PAINT가 아닌 다른 메시지에서 hdc = GetDC(hwnd) ReleaseDC(hwnd,hdc); – 전체 윈도우에 대한 hDC얻기 hdc = GetWindowDC(hwnd) ReleaseDC(hwnd,hdc); – 화면전체에 출력을 하려면 hdc = CreateDC (pszDriver,pszDevice,pszOutput,pData); [다른 명령들] DeleteDC(hdc); 3. 장치 컨텍스트 핸들 얻기 hdc=CreateDC(“DISPLAY”,NULL,NULL,NULL); Ex) hdc=CreateDC(TEXT("DISPLAY"),NULL,NULL,NULL); TextOut(hdc,0,0,TEXT("여기는 처음"),12); DeleteDC(hdc); hdcMem=CreateCompatibleDC(hdc); [다른 명령들] DeleteDC(hdc); 4. 점과 선 그리기 – 픽셀 정하기 • SetPixcel(hdc,x,y,crColor); – crColor는 COLORREF형식으로 색상을 지정 • crColor = GetPixcel(hdc,x,y) – 지정된 좌표의 픽셀 색상을 반환한다. – 직선 • • • • • – – – – – LineTo : 직선을 그린다. Polyline와 PolylineTo : 연결된 직선을 그린다. PolyPolyline : 여러 개의 Polyline을 그린다. Arc : 호를 그린다. PolyBezier와 PolyBezierTo : 베지어 스플라인을 그린다. Rectangle : 사각형을 그린다. Ellipse : 타원을 그린다. RoundRect : 모서리가 둥근 사각형을 그린다. Pie : 파이모양을 그린다. Chord : 현 형태의 다원 부분을 그린다. 5. 직선 그리기 – 직선을 그리려면 • MoveToEx(hdc,xBeg,yBeg,NULL); – MoveToEx는 펜의 위치는 옮긴다. – MoveToEx의 마지막 인자는 POINT 구조체에 대한 포인터이다. – 함수로부터 반환 시에 POINT구조체의 x,y 필드는 이전의 위치를 체운 다. – Windows 98에서의 좌표값이 32bit이지만 오직 하위 16비트만 사용한 다. – Windows NT는 32비트를 모두 사용한다. • LineTo(hdc,xEnd,yEnd); – LineTo함수는 현재의 위치로 부터 xEnd,yEnd까지 선을 그린다. • GetCurrentPositionEx(hdc,&pt); – 현재의 위치를 알아 내려면 – pt는 POINT구조체이다. 5. 직선 그리기 GetClientRect(hwnd,&rect); for (x = 0; x < rect.right; x+=100) { MoveToEx (hdc,x,0,NULL); LineTo(hdc,x,rect.bottom); } for (y = 0; y < rect.bottom; y+=100) { MoveToEx (hdc,0,y,NULL); LineTo(hdc,rect.right,y); } POINT apt[5] = {100,100,200,100,200,200,100,200,100,100}; MoveToEx(hdc,apt[0].x,apt[0].y); for (I = 1; I < 5;I++) { LineTo(hdc,apt[I].x,apt[I].y); } Polyline(hdc,apt,5); MoveToEx(hdc,apt[0].x,apt[0].y,NULL); PolylineTo(hdc,apt+1,4); 5. 경계 상자 함수 – Rectangle(hdc,xLeft,yTop,xRight,yBottom); • 채워진 사각형을 그린다. – Ellipse(hdc,xLeft,yTop,xRight,yBottom); • 채워진 타원을 그린다. – RoundRect(hdc,xLeft,yTop,xRight,yBottom,xCornerEllipse,yC ornerEllipse); • 모서리가 둥근 채워진 사각형을 그린다. – Arc(hdc,xLeft,yTop,xRight,yBottom,xStart,yStart,xEnd,yEnd); • 호를 그린다. – Chord(hdc,xLeft,yTop,xRight,yBottom,xStart,yStart,xEnd,yEn d); • 현을 그린다. – Pie(hdc,xLeft,yTop,xRight,yBottom,xStart,yStart,xEnd,yEnd); • 파이 모양의 도형을 그린다. 5. 경계 상자 함수 case WM_LBUTTONDOWN: hdc=GetDC(hwnd); LineTo(hdc,LOWORD(lParam),HIWORD(lParam)); m_OldPT.x = LOWORD(lParam); m_OldPT.y = HIWORD(lParam); ReleaseDC(hwnd,hdc); return 0; case WM_MOUSEMOVE: if (wParam & MK_LBUTTON) { hdc=GetDC(hwnd); SelectObject(hdc,GetStockObject(WHITE_PEN)); LineTo(hdc,m_OldPT.x,m_OldPT.y); MoveToEx(hdc,0,0,NULL); SelectObject(hdc,GetStockObject(BLACK_PEN)); LineTo(hdc,LOWORD(lParam),HIWORD(lParam)); m_OldPT.x = LOWORD(lParam); m_OldPT.y = HIWORD(lParam); ReleaseDC(hwnd,hdc); } return 0; 6. 스톡 펜 사용하기 – Windows가 기본적으로 제공하는 스톡 펜 • BLACK_PEN,WHITE_PEN,NULL_PEN – 디폴트 장치 컨텍스트 펜 • BLACK_PEN – PEN 정의 • HPEN hPen; – 윈도우 스톡에 있는 Pen가져오기 • hPen = GetStockObject(WHITE_PEN); – 장치 컨텍스트에 지정하려면 • SelectObject(hdc,hPen) • SelectObject(hdc,GetStockObject(WHITE_PEN)); 7. 펜의 생성과 선택, 그리고 삭제 – 펜의 사용 • 펜은 CreatePen과 CreatePenIndirect를 이용하여 펜을 생성한다. • SelectObject을 이용하여 만들어진 이 펜을 사용할 수 있다. • 새로운 펜으로 그린다. – 오직 하나의 펜만이 장치 컨텍스트에서 선택될 수 있다. • SelectObject를 이용하여 이전의 펜으로 되돌린다. • DeleteObject를 이용하여 만들어진 펜을 삭제한다 – GDI객체 사용 규칙 • 마지막에 항상 생성한 GDI객체를 삭제해야 한다. • 유효한 장치 컨텍스트에서 선택되어 있는 GDI객체를 삭제하지 않는다. • 스톡 객체는 삭제하지 않는다. – hPen = CreatePen(iPenStyle,iWidth,crColor); • iPenStyle :PS_SOLID,PS_DASH,PS_DOT,PS_DASHDOT,PS_DASH DOTDOT,PS_NULL,PS_INSIDEFRAMER • iWidth : 펜의 두께 , crColor : COLORREF의 펜의 Color – CreatePenIndirect • • • • • 먼저 LOGPEN형식의 구조체를 정의한다. LOGPEN logpen; ( lopnStyle => pen의 스타일, lopnWidht => pen의 두께, lopnColor => pen의 칼라 ) hPen = CreatePenIndirect(&logpen); 7. 펜의 생성과 선택, 그리고 삭제 -프로그램에서 3개의 Pen을 사용하면 HPEN hPen1 hPen2 hPen3 hPen1,hPen2,hPen3; = CreatePen(PS_SOLID,1,0); = CreatePen(PS_SOLID,1,RGB(255,0,0)); = CreatePen (PS_DOT,0,0); hOldPen = (HPEN)SelectObject(hdc, hPen1); SelectObject(hdc,hPen1); –[선그리기] SelectObject(hdc,hPen2); –[선그리기] SelectObject(hdc,hPen3); –[선그리기] SelectObject(hdc, hOldPen); DeleteObject (hPen1); DeleteObject (hPen2); DeleteObject (hPen3); 8. 틈새 채우기 – 도트 펜과 대쉬 펜 사이의 틈새는 어떻게 처리할까? • 틈새의 색은 Windows디폴트 배경색인 흰색으로 틈을 칠한다. (OPAQUE) – 틈새의 색을 바꾸려면 • SetBkColor(hdc,crColor); – 틈새를 칠하지 않게 할 수도 있다 • 배경모드를 TRANSPARENT로 지정한다. • SetBkMode(hdc,TRANSPARENT); 9. 그리기모드 – 디스플레이에 그려진 선의 외형은 장치 컨텍스트에서 정의된 그 리기 모드에 영향을 받는다. • 펜을 사용하여 선을 그릴 때 실제로는 펜의 픽셀과 목표가 되는 디 스플레이 픽셀의 bitwise 2진 연산을 수행한다. • 픽셀을 가지고 2진 연산을 수행하는 것을 “래스터 연산” 또는 “ROP” 라고 한다. – R2_COPYPEN • 디폴트 그리기 모드 • 단순히 펜의 픽셀을 목표에 복사하는 것을 나타낸다. – R2_BLACK • 항상 검은 선을 그린다. – R2_NOTMERGEPEN • 펜과 배경이 검정색일 때 선은 흰색으로 그린다. – R2_NOT • 펜의 색상과는 상관없이 항상 목표 색상을 반전하여 선의 색을 결정 한다. – 그리기 모드 지정 • SetROP2(hdc,iDrawMode); – 그리기 모드를 읽어 온다. • iDrawMode = GetROP2(hdc) 9. 그리기모드 원본 그림 COPY OR AND 그리기 모드 설명 R2_BLACK 항상 검정색이다. R2_WHITE 항상 흰색이다. R2_NOP 아무런 그리기도 하지 않는다. R2_NOT 원래의 그림을 반전시킨다. R2_COPYPEN 원래의 그림을 덮어버리고 새 그림을 그린다. R2_NOTCOPYPEN 새 그림을 반전시켜 그린다. R2_MERGEPEN OR연산으로 두 그림을 합친다. R2_MASKPEN AND연산으로 겹치는 부분만 그린다. R2_XORPEN XOR연산으로 겹치는 부분만 반전 시킨다. XOR LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; static int xPos, yPos, xOldPos, yOldPos; static bool bNowDraw = FALSE; switch (message) { case WM_LBUTTONDOWN: xPos = LOWORD(lParam); yPos = HIWORD(lParam); xOldPos = xPos; yOldPos = yPos; bNowDraw = true; return 0; case WM_LBUTTONUP: bNowDraw = false; hdc = GetDC(hwnd); MoveToEx(hdc,xPos,yPos,NULL); LineTo(hdc,xOldPos,yOldPos); ReleaseDC(hwnd,hdc); return 0; case WM_MOUSEMOVE: if (bNowDraw) { hdc = GetDC(hwnd); SetROP2(hdc,R2_NOT); MoveToEx(hdc,xPos,yPos,NULL); LineTo(hdc,xOldPos,yOldPos); int xNewPos = LOWORD(lParam); int yNewPos = HIWORD(lParam); MoveToEx(hdc,xPos,yPos,NULL); LineTo(hdc,xNewPos,yNewPos); xOldPos = xNewPos; yOldPos = yNewPos; ReleaseDC(hwnd,hdc); } return 0; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 10. 채워진 영역 그리기 – – – – – – – – Rectangle : 직각 모서리를 가지는 사각형 Ellipse : 타원 RoundRect :둥근 모서리를 가지는 사각형 Chord : 종료점은 현에 의해 연결되는 타원 외곽의 호 Pie : 타원 외곽선으로 정의되는 Pie Polygon : 다변체 그림 PolyPolygon : 다중 다변체 그림 현재 장치 컨텍스트에서 선택된 브러쉬로 채워진다. • 디폴트 : WHITE_BURSH • 6개의 스톡 브러쉬 : – WHITE_BRUSH , LTGRAY_BRUSH,GRAY_BRUSH, DKGRAY_BRUSH,BLACK_BRUSH,NULL_BRUSH 10. 채워진 영역 그리기 HBRUSH hBrush,hOldBrush; hBrush = GetStockObject(GRAY_BRUSH); hOldBrush = SelectObject(hdc,hBrush); [도형 그리기] SelectObject(hdc, hOldBrush); 경계가 없는 그림을 그리려면 SelectObject(hdc,GetStockObject(NULL_PEN)); 내부를 채우지 않고 그림의 외곽선 그리려면 SelectObject(hdc,GetStockObject(NULL_BRUSH)); 11. 내부를 브러쉬로 채우기 – 단색의 브러쉬를 만든다. • hBrush = CreateSolidBrush(crColor); – 수평,수직,혹은 대각선으로 구성되는 hatch표시를 이용하여 브 러쉬를 생성 • hBrush = CreateHatchBrush(iHatchStyle,crColor); – PS_HORIZONTAL,PS_BDIAGONAL,PS_VERTICAL,PS_CROSS, – PS_FDIAGONAL,PS_DIAGCROSS 값 설명 HS_BDIAGONAL 좌 하향 줄 무늬 HS_CROSS 바둑판 모양 HS_DIAGCROSS 좌 하향 및 우 하향 줄무늬 HS_FDIAGONAL 우 하향 줄무늬 HS_HORIZONTAL 수평선 HS_VERTICAL 수직선 11. 내부를 브러쉬로 채우기 – Bitmap으로 도형을 채우려면 • hBitMap = LoadBitmap(hinst,MAKEINTRESOURCE(IDB_BITMAP1)); • hNewBrush = CreatePatternBrush(hBitMap); – FillRect(hdc, &rect, hBrush) • 지정된 브러쉬로 사각형을 채운다. – FrameRect(hdc, &rect, hBrush); • 사각형의 Frame을 그리지만 채우지는 않는다. – InvertRect(hdc,&rect); • 사각형 내의 모든 픽셀들을 반전하여 1은 0으로 0은 1로 만든다. – Rect구조체를 채운다. • SetRect (&rect,xLeft,yTop,xRight,yBottom); – 사각형 내에 포인트가 있는지를 결정한다. • bInRect = PtInRect(&rect,point); 12. 랜덤 사각형 – PeekMessage(&msg,NULL,0,0,PM_REMOVE); • 처음 4개의 매개변수는 GetMessage와 동일하다. • PM_REMOVE는 메시지를 읽은 후 제거한다. • PM_NOREMOVE는 메시지를 읽은 후 제거하지 않는다. – PeekMessage()의 Return 값 • 메시지를 읽었는지 읽지 못 했는지를 Return한다. /*-----------------------------------------RANDRECT.C -- Displays Random Rectangles (c) Charles Petzold, 1998 ------------------------------------------*/ #include <windows.h> #include <stdlib.h> // for the rand function LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; void DrawRectangle (HWND) ; int cxClient, cyClient ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("RandRect") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Random Rectangles"),WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (TRUE) { if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break ; TranslateMessage (&msg) ; DispatchMessage (&msg) ; } else DrawRectangle (hwnd) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { switch (iMsg) { case WM_SIZE: cxClient = LOWORD (lParam) ; cyClient = HIWORD (lParam) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, iMsg, wParam, lParam) ; } 12. 랜덤 사각형 – WM_SIZE 메시지 • 윈도우의 크기가 변경될 때 보내진다. • lParam에는 하위 워드에 윈도우의 폭, 상위 워드에 윈도우의 높이 • wParam에는 메시지가 발생한 이유를 나타내는 플래그 전달 플래그 설명 SIZE_MAXHIDE 다른 윈도우가 최대화되어 이 윈도우가 가려졌다. SIZE_MAXIMIZED 최대화되었다. SIZE_MAXSHOW 다른 윈도우가 원래 크기로 복구되어 이 윈도우가 나 타났다. SIZE_MINIMIZED 최소화되었다. SIZE_RESTORED 크기가 변경되었다. 13. 영역을 생성하고 색칠하기 – – – – – 영역은 영역을 영역도 생성된 영역에 사각형,다각형,타원의 조합으로 이루어진다. 사용하여 그리거나 잘라내기를 할 수 있다. GDI객체이다. 모든 영역은 DeleteObject을 통하여 삭제한다. 대한 Handle은 HRGN을 반환한다. – 영역 생성 • • • • hRgn hRgn hRgn hRgn = = = = CreateRectRgn(xLeft,yTop,xRight,yBottom); CreateRectRgnIndirect(&rect); CreateEllipticRgn(xLeft, yTop,xRight,yBottom); CreateEllipticRgnIndirect(&rect); – CreateRoundRectRgn은 둥근 모양의 사각형 영역을 생성 – 다각형영역을 생성 • hRgn=CreatePolygonRgn(&point,iCount,iPolyFillMode); – point : POINT형 구조체 – iCount : 포인트의 개수 – iPolyFillMode : ALTERNATE또는 WINDING 13. 영역을 생성하고 색칠하기 – iRgnType = CombineRgn(hDestRgn,hSrcRgn1,hSrcRgn2,iCombine); • 2개의 원본 영역을 조합하여 목표 핸들이 조합된 영역을 나타내도 록 한다. • iCombine 매개변수는 hSrcRgn1과 hSrcRgn2의 영역이 조합되는 방식이다. – – – – – RGN_AND 두개의 소스 영역의 교차영역 RGN_OR 두개의 소스 영역의 전영역 RGN_XOR 두개의 소스 영역에서 교차 부분을 뺀 영역 RGN_DIFF hSrcRgn2에 속하지 않는 hSrcRgn1영역 RGN_COPY hSrcRgn1전부 (hSrcRgn2무시) • Return값 – – – – 빈 영역이면 NULLREGION; 간단한 사각형,원,다각형이면 SIMPLEREGION 사각형,타원,혹은 다각형들의 조합이면COMPLEXREGION ERROR면 ERROR 14. 맵핑모드 – 맵핑모드란 주어진 좌표가 화면상의 실제 어디에 해당하는지를 결정하는 방법을 말한다. – 윈도우에서 사용하는 좌표는 논리 좌표와 물리 좌표 두 가지가 있다. • 논리 좌표 : 윈도우 내부에서 사용되는 좌표를 말한다. – TextOut,DrawText등에서 사용하는 좌표를 말한다. – DC핸들을 인수로 받아들이는 모든 함수는 논리 좌표이다. • 물리 좌표 : 실제 화면에 출력되는 좌표이며 픽셀 단위를 사용한다. – 물리좌표 100,100은 그 위치가 정해져 있다. • 논리 좌표의 (20,20)은 물리 좌표의 (20,20)일 수도 있고 다른 곳 일 수도 있다. • 윈도우가 사용하는 디폴트 맵핑모드는 물리좌표와 논리좌표가 일치 한다. – int SetMapMode(HDC hdc, int iMapMode); – int GetMapMode(HDC hdc); 14. 맵핑모드 맵핑모드 단위 X축 증가 Y축 증가 MM_TEXT 픽셀 오른쪽 아래쪽 MM_LOMETRIC 0.1 mm 오른쪽 위쪽 MM_HIMETRIC 0.01 mm 오른쪽 위쪽 MM_LOENGLISH 0.01인치 오른쪽 위쪽 MM_HIENGLISH 0.001인치 오른쪽 위쪽 MM_TWIPS 1/1440 인치 오른쪽 위쪽 MM_ISOTROPIC 가변 가변 가변 MM_ANISOTROPIC 가변 가변 가변 15. 윈도우와 뷰 포트 – 윈도우는 논리 좌표가 사용되는 표면을 말한다. • 그래픽 출력 함수는 윈도우에 그래픽을 출력한다. – 뷰 포트는 물리 좌표가 사용되는 영역을 말한다. • 실제로 사용자의 눈에 보이는 좌표 영역이다. – SetViewportOrgEx(HDC hdc, int X, int Y, LPPOINT lpPoint); • 윈도우의 원점을 이동시킨다. – SetWindowOrgEx(HDC hdc, int X, int Y, LPPOINT lpPoint); • 뷰 포트의 원점을 이동시킨다. 16. 가변 비율 – 윈도우 확장을 조정할 수 있는 맵핑모드에는 MM_ISOTROPIC과 MM_ANISOTROPIC 두 가지가 있다. – BOOL SetWindowExtEx(HDC hdc, int nXExtent, int nYExtent, LPSIZE lpSize); • 논리적인 좌표 범위를 지정한다. – BOOL SetViewportExtEx(HDC hdc, int nXExtent, int nYExtent, LPSIZE lpSize); • 물리적인 좌표 범위를 지정한다. 5. 키보드 1. 키보드 무시하기 – Windows는 많은 키보드 함수들을 자체적으로 처리한다. • 시스템 함수에 관계되는 키스트로크는 보통 무시할 수 있다. (보통 Alt키를 포함한다.) • 프로그램의 메뉴를 불러내는 키스트로크는 윈도우프로시저에 오지 만 보통 DefWindowProc에 전달되어 디폴트 처리된다. – 특정한 키보드 이벤트를 수신하게 되는 윈도우는 입력 포커스를 가지고 있는 윈도우이다. – 윈도우 프로시저는 WM_SETFOCUS와 WM_KILLFOCUS메시지 를 가로채어 자신의 윈도우가 언제 입력 포커스를 가지게 되는 지를 알 수 있다. – 키보드 메시지는 메시지 큐에 저장된다. • 동기화 때문에. 2. 키스트로크 메시지 – 키를 누르면 Windows는 WM_KEYDOWN이나 WM_SYSKEYDOWN을 윈도우의 메시지 큐에 전달한다. – 키에서 손을 때면 Windows는 WM_KEYUP이나 WM_SYSKEYUP 메시지를 메시지 큐에 전달한다. – WM_SYSKEYUP과 WM_SYSKEYDOWN은 보통 ALT키와 같이 눌려진 키에 의해 생성된다. – 보통 WM_SYSKEYUP과 WM_SYSKEYDOWN메시지를 무시하고 DefWindowProc에 전달해 준다. • • • • case WM_SYSKEYDOWN: case WM_SYSKEYUP: case WM_SYSCHAR: return 0; -> 모든 Alt키 동작을 무시하게 한다. – 가상 키 코드는 WM_KEYDOWN,WM_KEYUP, WM_SYSKEYDOWN ,그리고 WM_SYSKEYUP메시지의 wParam 매개 변수에 저장된다. – 가상 키 코드는 VK_로 시작하는 이름을 가진다. • WINUSER.H에 선언 되어있다. • VK_LBUTTON,VK_RBUTTON,VK_CANCEL,VK_MBUTTON, • VK_BACK,VK_TAB… 3. Shift상태 – Shift,Ctrl그리고 Alt키나 Caps Lock,Num Lock, Scroll Lock이 눌렸는지를 알고 싶을 때는 GetKeyStat함수를 이용하여 알 수 있다. • iState = GetKeyState(VK_SHIFT); – 만약에 Shift키가 눌려졌을 때는 상위비트가 Setting된다. • iState = GetKeyState(VK_CAPITAL); – 만약에 Caps Lock키가 켜져 있을 때는 하위 bit가 Setting 된다. • 만약 사용자가 Shift-Tab을 입력했는지 알고 싶다면 WM_KEYDOWN메시지를 처리할 때 – GetKeyState(VK_SHIFT) – SendMessage(hwnd,message,wParam,lParam); • 윈도우 프로시저에 메시지를 강제로 전달한다. case WM_KEYDOWN: switch(wParam) { case VK_HOME: SendMessage(hwnd,WM_VSCROLL,SB_TOP,0); break; case VK_END: SendMessage(hwnd,WM_VSCROLL,SB_BOTTOM,0); break; case VK_PRIOR: SendMessage(hwnd,WM_VSCROLL,SB_PAGEUP,0);break; } 4. 문자 메시지 – TranslateMessage() • 키스트로크 메시지를 문자 메시지로 변환한다. – wParam에 들어 있는 값은 ANSI코드이다. – 메시지 순서 • WM_KEYDOWN • WM_CHAR • WM_KEYUP ‘A’에 대한 가상 키 코드 ‘a’에 대한 문자 코드 ‘A’에 대한 가상 키 코드 • • • • • VK_SHIFT 가상 키 코드 ‘A’에 대한 가상 키 코드 ‘A’에 대한 문자 코드 ‘A’에 대한 가상 키 코드 VK_SHIFT 가상 키 코드 WM_KEYDOWN WM_KEYDOWN WM_CHAR WM_KEYUP WM_KEYUP 5. 제어문자 처리 – 윈도우에서 키보드 문자 입력을 읽고 싶은 경우는 WM_CHAR메 시지를 처리한다. – WM_CHAR 메시지는 문자만을 입력하는 메시지이므로 문자 이 외의 키는 입력 받을 수 없다. – 커서 키,함수 키,Delete, Insert, Ctrl그리고 Alt는 WM_CHAR메시 지가 전달되지 않는다. 위의 키는 KEYDOWN에서 다룬다. – Tab, Enter, Backspace, Escape는 WM_CHAR에서 다루는 것이 더 좋다. case WM_CHAR : switch(wParam) { case ‘\b’; break; case ‘\t’: break; case ‘\n’: break; case ‘\r’: break; defaule : } //backspace //tab //줄 바꿈 //캐리지 리턴 //문자 코드 6. 캐럿(커서가 아님) – Caret함수 • • • • • CreateCaret SetCaretPos ShowCaret HideCaret DestoryCaret 윈도우와 연결된 캐럿을 생성. 윈도우 내에서 캐럿의 위치를 설정. 캐럿을 보여줌. 캐럿을 숨김. 캐럿을 소멸시킴. – WM_SETFOCUS메시지에서 • CreateCaret – WM_KILLFOCUS메시지에서 • DestroyCaret 7. 폰트 HFONT CreateFont(int nHeight, int nWidth, int nEscapement, int nOrientation, int iWeight, DWORD fdwItalic, DWORD fdwUnderline, DWORD fdwStrikeOut, DWORD fdwCharSet, DWORD fdwOutputPrecision, DWORD fdwClipPrecision,DWORD fdwQuality, DWORD fdwPitchAndFamily, LPCTSTR lpszFace); 인수 설명 nHeight 폰트의 높이, 이 값이 0일 경우에는 디폴트 크기가 사용된다. nWidth 폰트의 폭, 이 값이 0이면 nHeight에서 지정한 높이에 따라 폭을 자동 으로 결정한다. nEscapement 폰트의 각도를 0.1도 단위로 설정한다. 이 각도는 문자가 출력될 X축 과 문자열과의 각도이다. nOrientation 글자 한 자와 X축과의 각도를 지정한다. nEscapement는 전체 문자열 의 기울기를 지정하는데 비해 이 인수는 개별 문자의 기울기를 설정한 다. fnWeight 폰트의 무게를 0 – 1000까지의 값으로 지정한다. 폰트의 두께를 설정 한다. 보통 굵기는 FW_NORMAL이 400이다. fdwItalic fdwUnderline fdwStrkieOut 기울임체, 밑줄, 관통선 속성을 설정한다. DWORD이지만 불린형처럼 사용한다. 7. 폰트 인수 설명 fdwCharSet 문자 셋을 설정한다. 실제 사용할 수 있는 옵션은 ANSI_CHARSET(윈도우에서 사용),OEM_CHARSET(도스에서 사용)과 HANGEUL_CHARSET이 있다. fdwOutputPre 출력 정확도를 설정한다. cision fdwClipPrecis 클리핑 정확도를 설정한다. ion fdwQuality 논리적 폰트를 물리적 폰트에 얼마나 근접시킬 것인가를 지정 fdwPitchAnd Family 폰트의 피치와 그룹을 설정한다. lpszFace 글꼴의 이름을 나타내는 문자열을 설정한다. 7. 폰트 – COLORREF SetTextColor( HDC hdc, COLORREF crColor); • Text의 컬러를 Setting하는 함수 – COLORREF SetBkColor( HDC hdc, COLORREF crColor); • 글자의 배경색을 설정하는 함수 – int SetBkMode( HDC hdc, int iBkMode ); • 배경색상을 사용할 방법을 설정 • OPAQUE : 불투명한 배경을 사용한다. (디폴트) • TRANSPARENT : 문자 사이의 여백에 있는 원래 배경이 지워지지 않는다. LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_PAINT: { HDC hdc; PAINTSTRUCT ps; hdc = BeginPaint(hwnd,&ps); for(int i = 0;i < 900;i+=100) { HFONT hMyFont = CreateFont(50,0,i,0,FW_NORMAL,FALSE,FALSE,FALSE, HANGEUL_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,VARIABLE_PITCH|FF_SWISS,"굴림"); HFONT hOldFont = (HFONT)SelectObject(hdc,hMyFont); TextOut(hdc,0,300,"강원대학교",10); SelectObject(hdc,hOldFont); DeleteObject(hMyFont); } EndPaint(hwnd,&ps); } return 0; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } /*-------------------------------------TYPER.C -- Typing Program (c) Charles Petzold, 1998 --------------------------------------*/ #include <windows.h> #define BUFFER(x,y) *(pBuffer + y * cxBuffer + x) LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("Typer") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Typing Program"),WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static DWORD dwCharSet = DEFAULT_CHARSET ; static int cxChar, cyChar, cxClient, cyClient, cxBuffer, cyBuffer,xCaret, yCaret ; static TCHAR * pBuffer = NULL ; HDC hdc ; int x, y, i ; PAINTSTRUCT ps ; TEXTMETRIC tm ; switch (message) { case WM_INPUTLANGCHANGE: dwCharSet = wParam ; case WM_CREATE: hdc = GetDC (hwnd) ; SelectObject (hdc, CreateFont (0, 0, 0, 0, 0, 0, 0, 0,dwCharSet, 0, 0, 0, FIXED_PITCH, NULL)) ; GetTextMetrics (hdc, &tm) ; cxChar = tm.tmAveCharWidth ; cyChar = tm.tmHeight ; DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ; ReleaseDC (hwnd, hdc) ; case WM_SIZE: if (message == WM_SIZE) { cxClient = LOWORD (lParam) ; cyClient = HIWORD (lParam) ; } cxBuffer = max (1, cxClient / cxChar) ; cyBuffer = max (1, cyClient / cyChar) ; if (pBuffer != NULL) free (pBuffer) ; pBuffer = (TCHAR *) malloc (cxBuffer * cyBuffer * sizeof (TCHAR)) ; for (y = 0 ; y < cyBuffer ; y++) for (x = 0 ; x < cxBuffer ; x++) BUFFER(x,y) = ' ' ; xCaret = 0;yCaret = 0 ; if (hwnd == GetFocus ()) SetCaretPos (xCaret * cxChar, yCaret * cyChar) ; InvalidateRect (hwnd, NULL, TRUE) ; return 0 ; case WM_SETFOCUS: CreateCaret (hwnd, NULL, cxChar, cyChar) ; SetCaretPos (xCaret * cxChar, yCaret * cyChar) ; ShowCaret (hwnd) ; return 0 ; case WM_KILLFOCUS: HideCaret (hwnd) ; DestroyCaret () ; return 0 ; case WM_KEYDOWN: switch (wParam) { case VK_HOME: xCaret = 0 ; break ; case VK_END: xCaret = cxBuffer - 1 ; break ; case VK_PRIOR: yCaret = 0 ; break ; case VK_NEXT: yCaret = cyBuffer - 1 ; break ; case VK_LEFT: xCaret = max (xCaret - 1, 0) ; break ; case VK_RIGHT: xCaret = min (xCaret + 1, cxBuffer - 1) ; break ; case VK_UP: yCaret = max (yCaret - 1, 0) ; break ; case VK_DOWN: yCaret = min (yCaret + 1, cyBuffer - 1) ; break ; case VK_DELETE: for (x = xCaret ; x < cxBuffer - 1 ; x++) BUFFER (x, yCaret) = BUFFER (x + 1, yCaret) ; BUFFER (cxBuffer - 1, yCaret) = ' ' ; HideCaret (hwnd) ; hdc = GetDC (hwnd) ; SelectObject (hdc, CreateFont (0, 0, 0, 0, 0, 0, 0, 0,dwCharSet, 0, 0, 0, FIXED_PITCH, NULL)) ; TextOut (hdc, xCaret * cxChar, yCaret * cyChar,&BUFFER (xCaret, yCaret),cxBuffer - xCaret) ; DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ; ReleaseDC (hwnd, hdc) ; ShowCaret (hwnd) ; break ; } SetCaretPos (xCaret * cxChar, yCaret * cyChar) ; return 0 ; case WM_CHAR: for (i = 0 ; i < (int) LOWORD (lParam) ; i++) { switch (wParam) { case '\b': // backspace if (xCaret > 0) { xCaret-- ; SendMessage (hwnd, WM_KEYDOWN, VK_DELETE, 1) ; } break ; case '\t': // tab do { SendMessage (hwnd, WM_CHAR, ' ', 1) ; }while (xCaret % 8 != 0) ; break ; case '\n': // line feed if (++yCaret == cyBuffer) yCaret = 0 ; break ; case '\r': // carriage return xCaret = 0 ; if (++yCaret == cyBuffer) yCaret = 0 ; break ; case '\x1B': // escape for (y = 0 ; y < cyBuffer ; y++) for (x = 0 ; x < cxBuffer ; x++) BUFFER (x, y) = ' ' ; xCaret = 0 ; yCaret = 0 ; InvalidateRect (hwnd, NULL, FALSE) ; break ; default: // character codes BUFFER (xCaret, yCaret) = (TCHAR) wParam ; HideCaret (hwnd) ; hdc = GetDC (hwnd) ; SelectObject (hdc, CreateFont (0, 0, 0, 0, 0, 0, 0, 0, dwCharSet, 0, 0, 0, FIXED_PITCH, NULL)) ; TextOut (hdc, xCaret * cxChar, yCaret * cyChar,&BUFFER (xCaret, yCaret), 1) ; DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))); ReleaseDC (hwnd, hdc) ; ShowCaret (hwnd) ; if (++xCaret == cxBuffer) { xCaret = 0 if (++yCaret == cyBuffer) yCaret = 0 ; } break ; } } SetCaretPos (xCaret * cxChar, yCaret * cyChar) ; return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; SelectObject (hdc, CreateFont (0, 0, 0, 0, 0, 0, 0, 0, dwCharSet, 0, 0, 0, FIXED_PITCH, NULL)) ; for (y = 0 ; y < cyBuffer ; y++) TextOut (hdc, 0, y * cyChar, & BUFFER(0,y), cxBuffer) ; DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 6. 마우스 1. 마우스의 기본 – 마우스의 존재여부 확인 • fMouse = GetSystemMetrics(SM_MOUSEPRESENT); – 마우스가 설치되어 있다면 TRUE, – 설치되어 있지 않다면 FALSE – 마우스 단추의 개수 알아내기 • cButtons = GetSystemMetrics(SM_CMOUSEBUTTONS); • 마우스가 설치되어 있지 않다면 0을 Return한다. – Windows는 몇 가지의 미리 정의된 마우스 커서를 가지고 있다. • IDC_ARROW,IDC_CROSS,IDC_WAIT • wndclass.hCursor = LoadCursor(NULL,IDC_ARROW); 2. 클라이언트 영역 마우스 메시지 – 윈도우 프로시저는 윈도우 클래스에 더블 클릭 메시지를 받도록 미리 정의된 경우만 DBCLICK메시지를 받는다. – 마우스의 모든 메시지에는 lParam값에 마우스의 위치를 담고 있다. • x = LOWORD(lParam); • y = HIWORD(lParam); – wParam에는 마우스 단추와 Shift키 및 Ctrl키의 상태를 나타낸다. • • • • • MK_LBUTTON MK_MBUTTON MK_RBUTTON MK_SHIFT MK_CONTROL 왼쪽 단추가 눌린 상태 중간 단추가 눌린 상태 오른쪽 단추가 눌린 상태 Shift가 눌린 상태 Control키가 눌린 상태 – wParam & MK_SHIFT값이 TRUE이면 SHIFT키가 눌린 상태를 나 타냄 • • • • • MK_LBUTTON MK_MBUTTON MK_RBUTTON MK_SHIFT MK_CONTROL 왼쪽 단추를 누른 상태 중간 단추를 누른 상태 오른쪽 단추를 누른 상태 SHIFT키를 누른 상태 Control키를 누른 상태 – 윈도우의 클라이언트 영역 밖에 있을 때에도 계속 마우스의 메시 지를 받을 수 있다. – 만약 시스템 메시지 상자나 대화상자가 나타나 있으면, 다른 어떤 프로그램도 마우스 메시지를 받을 수 없다. /*-------------------------------------------------CONNECT.C -- Connect-the-Dots Mouse Demo Program (c) Charles Petzold, 1998 --------------------------------------------------*/ #include <windows.h> #define MAXPOINTS 1000 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("Connect") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("Program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Connect-the-Points Mouse Demo"), WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static POINT pt[MAXPOINTS] ; static int iCount ; HDC hdc ; int i, j ; PAINTSTRUCT ps ; switch (message) { case WM_LBUTTONDOWN: iCount = 0 ; InvalidateRect (hwnd, NULL, TRUE) ; return 0 ; case WM_MOUSEMOVE: if (wParam & MK_LBUTTON && iCount < 1000) { pt[iCount ].x = LOWORD (lParam) ; pt[iCount++].y = HIWORD (lParam) ; hdc = GetDC (hwnd) ; SetPixel (hdc, LOWORD (lParam), HIWORD (lParam), 0) ; ReleaseDC (hwnd, hdc) ; } return 0 ; case WM_LBUTTONUP: InvalidateRect (hwnd, NULL, FALSE) ; return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; SetCursor (LoadCursor (NULL, IDC_WAIT)) ; ShowCursor (TRUE) ; for (i = 0 ; i < iCount - 1 ; i++) for (j = i + 1 ; j < iCount ; j++) { MoveToEx (hdc, pt[i].x, pt[i].y, NULL) ; LineTo (hdc, pt[j].x, pt[j].y) ; } ShowCursor (FALSE) ; SetCursor (LoadCursor (NULL, IDC_ARROW)) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 2. Shift Key처리 if (wParam & MK_SHIFT) { if (wParam & MK_CONTROL) { [Shift와 Ctrl 키 눌린 상태] } else { [Shift 키 눌린 상태] } } else { if (wParam & MK_CONTROL) { [Ctrl key 눌린 상태] } else { [ Shift key와 Ctrl키가 눌리지 않음] } } 3. 마우스 더블 클릭 – 윈도우가 더블 클릭을 받기를 원하면 • wndclass.style=CS_HREDRAW|CS_VREDRAW|CS_DBCLKS; – CS_DBCLKS를 지정하지 않으면 • • • • WM_LBUTTONDOWN WM_LBUTTONUP WM_LBUTTONDOWN WM_LBUTTONUP – CS_DBCLKS를 지정하면 • • • • WM_LBUTTONDOWN WM_LBUTTONUP WM_LBUTTONDBCLK WM_LBUTTONUP 4. 비 클라이언트 영역 마우스 메시지 – 윈도우에서 비클라이언트 영역은 제목표시줄, 메뉴, 윈도우 스크 롤바를 포함한다. – 비클라이언트 영역을 표시하기 위하여 ‘NC’를 포함하고 있다. • WM_NCLBUTTONDOWN, • WM_NCRBUTTONDOWN – wParam • 마우스가 움직이거나 클릭된 비클라이언트 영역을 나타낸다. – lParam • 화면상의 좌표 – 화면상의 좌표를 클라이언트 좌표로 • ScreenToClient(hwnd,&pt); – 클라이언트 좌표를 화면상의 좌표로 • ClientToScreen(hwnd,&pt); 5. Hit-Test메시지 – 비클라이언트 ‘hit-test’를 나타내는 WM_NCHITTEST 메시지 – 이 메시지는 다른 모든 클라이언트 영역과 비 클라이언트 영역 마우스 메시지에 우선한다. – lParam • 마우스의 화면좌표 – wParam • 사용하지 않는다. – 일반적으로 Windows응용 프로그램은 이 메시지를 DefWindowProc에 전달한다. – 윈도우는 이것을 바탕으로 다른 모든 마우스 메시지를 생성한다. – WM_NCHITTEST가 처리 될 때 DefWindowProc에서 반환된 값 은 마우스 메시지의 wParam매개 변수가 된다. – DefWindowProc이 WM_NCHITTEST를 처리한 후 HTCLIENT를 반환하면 Windows는 화면 좌표를 클라이언트 영역 좌표로 변환 하고 클라이언트 영역 마우스 메시지를 생성한다. • • • • • • HTCLIENT 클라이언트 영역 HTNOWHERE 창에서 눌리지 않음 HTTRANSPARENT 다른 윈도우에 의해 덮여있는 윈도우 HTERROR DefWindowProc에 의해 소리 발생 case WM_NCHITTEST: return (LRESULT)HTCAPTION; 6. 메시지를 생성하는 메시지 – 시스템 메뉴 아이콘 더블 클릭 • WM_HITTEST • DefWindowProc은 HTSYSMENU의 값을 반환 – WM_NCLBUTTONDBLCLK • wParam => HTSYSMENU • DefWindowProc에 전달 • DefWindowProc이 받으면 – WM_SYSCOMMAND • wParam =>SC_CLOSE • DefWindowProc에 전달 • DefWindowProc이 받으면 – WM_CLOSE메시지를 발생 • DefWindowProc이 받으면 • DestroyWindow()함수 호출 – WM_DESTROY메시지 발생 • PostQuitMessage(0) – WM_QUIT메시지 발생 #include <windows.h> #define DIVISIONS 5 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("Checker2") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("Program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Checker2 Mouse Hit-Test Demo"),WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static BOOL fState[DIVISIONS][DIVISIONS] ; static int cxBlock, cyBlock ; HDC hdc ; int x, y ; PAINTSTRUCT ps ; POINT point ; RECT rect ; switch (message) { case WM_SIZE : cxBlock = LOWORD (lParam) / DIVISIONS ; cyBlock = HIWORD (lParam) / DIVISIONS ; return 0 ; case WM_SETFOCUS : ShowCursor (TRUE) ; return 0 ; case WM_KILLFOCUS : ShowCursor (FALSE) return 0 ; case WM_KEYDOWN : GetCursorPos (&point) ; ScreenToClient (hwnd, &point) ; x = max (0, min (DIVISIONS - 1, point.x / cxBlock)) ; y = max (0, min (DIVISIONS - 1, point.y / cyBlock)) ; switch (wParam) { case VK_UP : y-- ; break ; case VK_DOWN : y++ ; break ; case VK_LEFT : x-- ; break ; case VK_RIGHT : x++ ; break ; case VK_HOME : x=y=0; break ; case VK_END : x = y = DIVISIONS - 1 ; break ; case VK_RETURN : case VK_SPACE : SendMessage (hwnd, WM_LBUTTONDOWN, MK_LBUTTON,MAKELONG (x * cxBlock, y * cyBloc break ; } x = (x + DIVISIONS) % DIVISIONS ; y = (y + DIVISIONS) % DIVISIONS ; point.x = x * cxBlock + cxBlock / 2 ; point.y = y * cyBlock + cyBlock / 2 ; ClientToScreen (hwnd, &point) ; SetCursorPos (point.x, point.y) ; return 0 ; case WM_LBUTTONDOWN : x = LOWORD (lParam) / cxBlock ; y = HIWORD (lParam) / cyBlock ; if (x < DIVISIONS && y < DIVISIONS) { fState[x][y] ^= 1 ; rect.left = x * cxBlock ; rect.top = y * cyBlock ; rect.right = (x + 1) * cxBlock ; rect.bottom = (y + 1) * cyBlock ; InvalidateRect (hwnd, &rect, FALSE) ; } else MessageBeep (0) ; return 0 ; case WM_PAINT : hdc = BeginPaint (hwnd, &ps) ; for (x = 0 ; x < DIVISIONS ; x++) for (y = 0 ; y < DIVISIONS ; y++) { Rectangle (hdc, x * cxBlock, y * cyBlock,(x + 1) * cxBlock, (y + 1) * cyBlock) ; if (fState [x][y]) { MoveToEx (hdc, x *cxBlock, y *cyBlock, NULL) ; LineTo (hdc, (x+1)*cxBlock, (y+1)*cyBlock) ; MoveToEx (hdc, x *cxBlock, (y+1)*cyBlock, NULL) ; LineTo (hdc, (x+1)*cxBlock, y *cyBlock) ; } } EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } /*------------------------------------------------CHECKER3.C -- Mouse Hit-Test Demo Program No. 3 (c) Charles Petzold, 1998 -------------------------------------------------*/ #include <windows.h> #define DIVISIONS 5 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; LRESULT CALLBACK ChildWndProc (HWND, UINT, WPARAM, LPARAM) ; TCHAR szChildClass[] = TEXT ("Checker3_Child") ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("Checker3") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("Program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } wndclass.lpfnWndProc = ChildWndProc ; wndclass.cbWndExtra = sizeof (long) ; wndclass.hIcon = NULL ; wndclass.lpszClassName = szChildClass ; RegisterClass (&wndclass) ; hwnd = CreateWindow (szAppName, TEXT ("Checker3 Mouse Hit-Test Demo"),WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL); ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hwndChild[DIVISIONS][DIVISIONS] ; int cxBlock, cyBlock, x, y ; switch (message) { case WM_CREATE : for (x = 0 ; x < DIVISIONS ; x++) for (y = 0 ; y < DIVISIONS ; y++) hwndChild[x][y] = CreateWindow (szChildClass, NULL,WS_CHILDWINDOW | WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU) (y << 8 | x), (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE), NULL) ; return 0 ; case WM_SIZE : cxBlock = LOWORD (lParam) / DIVISIONS ; cyBlock = HIWORD (lParam) / DIVISIONS ; for (x = 0 ; x < DIVISIONS ; x++) for (y = 0 ; y < DIVISIONS ; y++) MoveWindow (hwndChild[x][y],x * cxBlock, y * cyBlock,cxBlock, cyBlock, TRUE) ; return 0 ; case WM_LBUTTONDOWN : MessageBeep (0) ; return 0 ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } LRESULT CALLBACK ChildWndProc (HWND hwnd, UINT message,WPARAM wParam, LPARAM lParam) { HDC hdc ; PAINTSTRUCT ps ; RECT rect ; switch (message) { case WM_CREATE : SetWindowLong (hwnd, 0, 0) ; // on/off flag return 0 ; case WM_LBUTTONDOWN : SetWindowLong (hwnd, 0, 1 ^ GetWindowLong (hwnd, 0)) ; InvalidateRect (hwnd, NULL, FALSE) ; return 0 ; case WM_PAINT : hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; Rectangle (hdc, 0, 0, rect.right, rect.bottom) ; if (GetWindowLong (hwnd, 0)) { MoveToEx (hdc, 0, 0, NULL) ; LineTo (hdc, rect.right, rect.bottom) ; MoveToEx (hdc, 0, rect.bottom, NULL) ; LineTo (hdc, rect.right, 0) ; } EndPaint (hwnd, &ps) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 6. 윈도우의 종류 • WS_CHILD, WS_POPUP – 오버랜드가 아닌 윈도우는 차일드나 팝업 윈도우 둘 중 하나가 된다. – 이 두 스타일은 상호 배치되는 성질이 있기 때문에 동시에 같이 사용할 수는 없다. – WS_CHILD • 다른 윈도우의 차일드 윈도우가 된다. • CreateWindow의 hParent인수에 부모 윈도우의 핸들을 대입해 주 어 어떤 윈도우의 차일드가 될 것인가를 지정해 주어야 한다. • 차일드 윈도우는 부모 윈도우의 작업영역 밖을 벗어날 수 없다. • 메인 윈도우의 작업영역 내부에서 사용되는 컨트롤이 이 스타일을 사용한다. – WS_POPUP • 만든 윈도우는 대화상자나 메시지 박스처럼 부모 윈도우의 작업 영 역밖으로 이동할 수 있다. • 항상 부모 윈도우보다 수직적으로 위에 위치(Z 순서)하므로 부모에 의해 가려지지 않는다. #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("HelloWin") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra =0; wndclass.cbWndExtra =0; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName,MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, // window class name "The Hello Program", // window caption WS_POPUPWINDOW, // window style 10, // initial x position 10, // initial y position 200, // initial x size 100, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL) ; // creation parameters ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } 6. 확장 윈도우 스타일 – HWND CreateWindowEx( DWORD dwExStyle, LPCTSTR lpClassName) • CreateWindow에 dwExStyle이 추가되었다. WS_EX_TOPMOST 모든 윈도우보다 수직적으로 위에 있는 윈도우를 만든다. 활성화된 상태에서도 다른 윈도우에 가려지지 않는다. WS_EX_TRANSPA RENT 형제 윈도우가 다 그려지기 전에 그려지지 않아 투명하게 보이는 윈도우를 만든다. WS_EX_CLIENTED 작업영역이 쑥 들어간 음각 모양으로 만든다. GE WS_EX_LAYERD 2000에서 추가된 속성이며 레이어드 윈도우를 생성한다. 6. 확장 윈도우 스타일 • 작업영역 크기 설정 – BOOL AdjustWindowRect( LPRECT lpRect, DWORD dwStyle, BOOL bMenu); • 이 함수는 원하는 작업영역의 크기를 주면 작업영역 크기에 맞는 윈 도우 크기를 계산해 준다. • lpRect : 작업영역의 크기 • dwStyle : 윈도우의 스타일 • bMenu : 메뉴의 존재 여부를 전달 LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: { RECT rect = {0,0,300,300}; AdjustWindowRect(&rect,WS_OVERLAPPEDWINDOW,FALSE); MoveWindow(hwnd,100,100,rect.right-rect.left,rect.bottom-rect.top,FALSE); } return 0; 7. 마우스 캡쳐 – 마우스 캡처하기 • 윈도우의 클라이언트나 비클라이언트 영역 위에 있을 때 마우스 메 시지를 받을 수 있다. • 마우스가 윈도우 밖에 있을 때 마우스 메시지를 받고 싶으면 마우스 를 캡처해야 한다. – SetCapture(hwnd); – ReleaseCapture(); 7. 타이머 1. 타이머의 기초 – SetTimer를 호출하면 Windows는 프로그램에 타이머를 할당한 다. • WM_TIMER메시지가 발생한다. – KillTimer함수를 발생시켜 타이머 메시지가 전송되는 것을 중지 시킬 수 있다. – Windows는 WM_TIMER메시지를 WM_PAINT메시지와 동등하게 취급한다. • 두 메시지 모두 우선순위가 낮으며 메시지 큐에 다른 메시지가 없을 때만 이 메시지가 프로그램에 전달된다. • 메시지 큐에 WM_TIMER메시지가 있는 상태에서 다른 WM_TIMER 메시지가 들어오면 두 메시지는 하나로 결합한다. – SetTimer(hwnd,1,interval,NULL); • WM_TIMER를 받게 될 윈도우의 핸들 • 타이머의 ID • 1/1000초 – KillTimer(hwnd,1); • 윈도우 핸들, 삭제할 타이머의 ID – SetTimer(hwnd,TimerID,interval,TimerProc); • WM_TIMER를 받게 될 윈도우의 핸들, • 타이머의 ID, 1/1000초 • Timer CallBack함수 /*----------------------------------------BEEPER1.C -- Timer Demo Program No. 1 (c) Charles Petzold, 1998 -----------------------------------------*/ #include <windows.h> #define ID_TIMER 1 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("Beeper1") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("Program requires Windows NT!"),szAppName, MB_ICONERROR); return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Beeper1 Timer Demo"),WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL); ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static BOOL fFlipFlop = FALSE ; HBRUSH hBrush ; HDC hdc ; PAINTSTRUCT ps ; RECT rc ; switch (message) { case WM_CREATE: SetTimer (hwnd, ID_TIMER, 1000, NULL) ; return 0 ; case WM_TIMER : MessageBeep (-1) ; fFlipFlop = !fFlipFlop ; InvalidateRect (hwnd, NULL, FALSE) ; return 0 ; case WM_PAINT : hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rc) ; hBrush = CreateSolidBrush (fFlipFlop ? RGB(255,0,0) : RGB(0,0,255)) ; FillRect (hdc, &rc, hBrush) ; EndPaint (hwnd, &ps) ; DeleteObject (hBrush) ; return 0 ; case WM_DESTROY : KillTimer (hwnd, ID_TIMER) ; PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } /*---------------------------------------BEEPER2.C -- Timer Demo Program No. 2 (c) Charles Petzold, 1998 ----------------------------------------*/ #include <windows.h> #define ID_TIMER 1 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; VOID CALLBACK TimerProc (HWND, UINT, UINT, DWORD ) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("Beeper2") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("Program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Beeper2 Timer Demo"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: SetTimer (hwnd, ID_TIMER, 1000, TimerProc) ; return 0 ; case WM_DESTROY: KillTimer (hwnd, ID_TIMER) ; PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } VOID CALLBACK TimerProc (HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime) { static BOOL fFlipFlop = FALSE ; HBRUSH hBrush ; HDC hdc ; RECT rc ; MessageBeep (-1) ; fFlipFlop = !fFlipFlop ; GetClientRect (hwnd, &rc) ; hdc = GetDC (hwnd) ; hBrush = CreateSolidBrush (fFlipFlop ? RGB(255,0,0) : RGB(0,0,255)) ; FillRect (hdc, &rc, hBrush) ; ReleaseDC (hwnd, hdc) ; DeleteObject (hBrush) ; } 2. FindWindow – HWND FindWindow( LPCTSTR lpClassName, LPCTSTR lpWindowName); • lpClassName, lpWindowName에 캡션을 전달해주되 둘 중 하나만 지정해 줄 수도 있다. • 대소문자는 구분하지 않는다. • 조건에 맞는 윈도우를 찾으면 그 핸들을 리턴해 준다. 찾지 못할 경 우에는 NULL을 리턴한다. • 자신이 만든 윈도우에만 제한적으로 사용한다. ( 캡션을 변경하지 말아야 한다.) – HWND WindowFromPoint(POINT Point); • 이 함수는 Point화면 좌표 아래에 있는 윈도우를 조사해서 그 핸들 을 리턴해 준다. VOID CALLBACK TimerProc (HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime) { POINT pt; GetCursorPos(&pt); HWND hFindhwnd = WindowFromPoint(pt); InvalidateRect(hwnd,NULL,TRUE); UpdateWindow(hwnd); if (hFindhwnd == NULL) { HDC hdc = GetDC(hwnd); TextOut(hdc,0,0,"윈도우 없음",11); ReleaseDC(hwnd,hdc); } else { HDC hdc = GetDC(hwnd); char Temp[256]; GetWindowText(hFindhwnd,Temp,256); TextOut(hdc,0,0,Temp,strlen(Temp)); GetClassName(hFindhwnd,Temp,256); TextOut(hdc,0,20,Temp,strlen(Temp)); ReleaseDC(hwnd,hdc); } } 3. 윈도우 열거 – BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam); • 현재 실행중인 모든 최상위 윈도우들을 열거하여 첫 번째 인수로 지 정된 콜백함수를 호출해 준다. • BOOL CALLBACK EnumWindowProc(HWND hwnd, LPARAM lParam); • EnumWindows함수는 모든 최상위 윈도우를 검색하여 그 핸들을 콜 백함수로 전달해 주되 모든 윈도우를 다 찾거나 콜백함수가 FALSE 를 리턴할 때까지 검색을 계속한다. – BOOL EnumChildWindows( HWND hWndParent, WNDENUMPROC lpEnumFunc, LPARAM lParam); • 특정 부모 윈도우의 차일드만 검색 – BOOL EnumThreadWindows(DWORD dwThreadId, WNDENUMPROC lpfn, LPARAM lParam); • 스레드에 속한 윈도우의 목록을 조사 static int yPos; BOOL CALLBACK MyEnumProc (HWND hwnd, LPARAM lParam); LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_LBUTTONDOWN: yPos = 0; EnumWindows(MyEnumProc,(LPARAM)hwnd); return 0; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } BOOL CALLBACK MyEnumProc (HWND hwnd, LPARAM lParam) { HWND myhwnd = (HWND)lParam; HDC hdc = GetDC(myhwnd); char Temp[256]; GetWindowText(hwnd,Temp,256); TextOut(hdc,0,yPos,Temp,strlen(Temp)); ReleaseDC(myhwnd,hdc); yPos += 14; return TRUE; } 3. 윈도우 크기 변경 – WM_SIZING • 사용자가 윈도우 크기를 변경하고 있을 때 보내진다. • wParam : 사용자가 드래그하고 있는 윈도우의 경계선이 어느쪽인 가를 지정하는 WMSZ_BOTTOM, WMSZ_LEFT, WMSZ_TOP등의 값이 전달된다. • lParam : 현재 윈도우의 영역을 화면 좌표로 가지는 RECT구조체의 포인터가 전달된다. • 이메시지에서 좌표를 변경했으면 반드시 TRUE를 리턴 해야 한다. LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_SIZING: ((RECT *)lParam)->left = ((RECT *)lParam)->left / 50 * 50; ((RECT *)lParam)->top = ((RECT *)lParam)->top / 50 * 50; ((RECT *)lParam)->right = ((RECT *)lParam)->right / 50 * 50; ((RECT *)lParam)->bottom = ((RECT *)lParam)->bottom / 50 * 50; return TRUE; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 3. 윈도우 크기 변경 – WM_GETMINMAXINFO • 운영체제는 윈도우의 크기나 위치를 바꾸기 전에 이 메시지를 응용 프로그램으로 보내어 위치와 크기에 대해 제한을 할 수 있도록 기회 를 준다. LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_GETMINMAXINFO: { MINMAXINFO * lpMinMaxInfo = (LPMINMAXINFO)lParam; lpMinMaxInfo->ptMaxTrackSize.x = 300; lpMinMaxInfo->ptMaxTrackSize.y = 300; lpMinMaxInfo->ptMinTrackSize.x = 100; lpMinMaxInfo->ptMinTrackSize.x = 100; lpMinMaxInfo->ptMaxSize.x = 200; lpMinMaxInfo->ptMaxSize.y = 200; lpMinMaxInfo->ptMaxPosition.x = 100; lpMinMaxInfo->ptMaxPosition.y = 100; } return 0; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 4. 윈도우 이동 – WM_MOVING • 윈도우가 이동 중일 때 발생한다. • 윈도우 이동이 완료되었을 때는 WM_MOVE메시지가 전달된다. – WM_WINDOWPOSCHNAGING • • • • • 윈도우의 위치 뿐만 아니라 Z순서가 변해도 전달된다. 이동 중일 때 발생한다. 이동이 완료되면 WM_WINDOWPOSCHANGED 메시지가 전달된다. lParam에 WINDOWPOS구조체의 주소가 전달된다. 이 구조체의 값들을 참고하여 현재 위치와 크기를 알 수 있으며, 강 제로 값을 변경하여 위치를 조정할 수도 있다. LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_WINDOWPOSCHANGING: { int t = ((LPWINDOWPOS)lParam)->x; if (t < 30) ((LPWINDOWPOS)lParam)->x = 0; } return 0; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 5. 반투명한 윈도우 – 레이어드 윈도우를 만들려면 WS_EX_LAYERD 확장 스타일을 준 다. – 레이어드 윈도우가 되면 이 윈도우는 다음 함수를 호출하기 전에 는 화면에 보이지 않는다. • BOOL SetLayeredWindowAttributes( HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags); • 레이어드 윈도우의 투명 및 반투명 속성을 설정한다. • crKey는 투명으로 처리할 색상을 지정한다. • 투명으로 지정된 색상 부분은 윈도우에서 보이지 않는 투명 영역으 로 처리된다. • bAlpha는 윈도우 전체의 반투명 정도를 지정하는데 0이면 완전 투 명이며 255는 불투명이고, 128이면 반쯤 투명하다. • dwFlags는 두 효과중 어떤 효과를 줄 것인가를 지정하는데 LWA_COLORKEY 플래그는 투명 색상 지정을, LWA_ALPHA는 반투 명정도를지정하며 두 플래그를 동시에 줄 수도 있다. 6. 훅 – 훅이란 메시지가 목표 윈도우에 전달되기 전에 메시지를 가로채 는 프로시저이다. – 응용 프로그램이 훅 프로시저를 설치하면 메시지가 윈도우로 보 내지기 전에 훅 프로시저에 먼저 보내진다. – 훅 프로시저가 어떤 메시지를 받을 것인가는 훅 타입과 훅의 범 위에 따라 달라진다. – 시스템 전역 훅 프로시저 • 모든 스레드에서 발생하는 메시지를 가로챈다. – 스레드 한정적 훅 프로시저 • 특정 스레드에서 발생하는 메시지만 가로챈다. – 운영체제는 설치된 훅 프로시저들을 훅 체인으로 관리한다. • 훅 체인이란 훅 프로시저 함수들의 번지를 담고 있는 일종의 함수 포인터 배열이라고 할 수 있다. – 응용 프로그램이 훅 프로시저를 설치하면 운영체제는 훅 체인의 선두에 이 프로시저를 등록한다. – 훅 프로시저가 감시하는 메시지가 발생하면 운영체제는 훅 체인 의 선두에 등록된 훅 프로시저에게 이 메시지를 전달하고 훅 프 로시저는 체인을 따라 다음 훅 프로시저에게 메시지를 반복적으 로 전달하며 끝으로 그 메시지를 받을 윈도우에게 전달된다. 6. 훅 – LRESULT CALLBACK KeyboardProcc(int code, WPARAM wParam, LPARAM lParam); • code : 훅 프로시저에서 이 메시지를 어떻게 처리할 것인가를 알려 준다. – HHOOK SetWindowHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId); • • • • idHook : 설치하고자 하는 훅의 타입 lpfn : 훅 프로시저의 번지 hMod : 훅 프로시저를 가진 인스턴스 핸들 dwThradId : 훅 프로시저가 감시할 스레드의 ID이되 이 값이 0이면 시스템의 모든 스레드에서 발생하는 메시지가 훅 프로시저로 전달 된다. • 시스템의 모든 메시지를 감시하고자 한다거나 다른 프로그램의 메 시지를 감시하고자 할 경우 lpfn은 반드시 분리된 DLL에 있어야 하 며 hMod는 이 DLL의 핸들이어야 한다. – BOOL UnhookWindowsHookEx( HHOOK hhk ); – LRESULT CallNextHookEx(HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam); #define WINVER 0x500 #define _WIN32_WINNT 0x500 #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; HWND g_hMainWnd; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("Beeper2") ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("Program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } g_hMainWnd = CreateWindow (szAppName, TEXT ("Beeper2 Timer Demo"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ; ShowWindow (g_hMainWnd, iCmdShow) ; UpdateWindow (g_hMainWnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } HHOOK hKeyHook; LRESULT CALLBACK KeyHookProc(int nCode, WPARAM wParam, LPARAM lParam) { HDC hdc; char str[256]; RECT rect = {100,120,500,150}; static int iCount = 0; if (nCode < 0) { return CallNextHookEx(hKeyHook,nCode,wParam,lParam); } wsprintf(str,"nCode=%d, wParam=%d, lParam=%x, iCount=%d", nCode, wParam, lParam,iCount++); hdc = GetDC(g_hMainWnd); InvalidateRect(g_hMainWnd,&rect,TRUE); UpdateWindow(g_hMainWnd); TextOut(hdc,100,120,str,strlen(str)); ReleaseDC(g_hMainWnd,hdc); if (wParam == 'A') return 1; return CallNextHookEx(hKeyHook,nCode,wParam,lParam); } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: hKeyHook = SetWindowsHookEx(WH_KEYBOARD,KeyHookProc,NULL,GetCurrentThreadId()); return 0; case WM_CHAR: { static int xPos = 0; char c = (char)wParam; HDC hdc = GetDC(hwnd); TextOut(hdc,xPos,0,&c,1); ReleaseDC(hwnd,hdc); xPos+= 10; } return 0; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 8. 자식 윈도우 제어 1. 자식 윈도우 제어 – 부모 윈도우의 handle을 알아내기 • hwndParent = GetParent(hwnd); – 부모 윈도우에 메시지 보내기 • SendMessage(hwndParent,message,wParam,lParam); – 자식 컨트롤 만들기 • CreateWindow()로 자식 윈도우를 만든다. • MoveWindow()호출로 자식 윈도우의 위치와 크기를 조절한다. • 미리 정의된 컨트롤(단추,체크 상자,편집 상자,목록 상자…) 중 하나 를 만들 때는 자식 윈도우의 클래스가 이미 윈도우에 등록되어 있기 때문에 윈도우 클래스를 등록할 필요가 없다. – 각각의 단추를 클릭하면자식 윈도우 컨트롤은 자신의 부모 윈도 우에게 WM_COMMAND메시지를 보낸다. • LOWORD(wParam) : 자식윈도우 ID • HIWORD(wParam) : 알림코드 • lParam : 자식윈도우의 핸들 – BS_OWNERDRAW • 사용자가 책임지고 버튼을 그려야 한다. • 버튼이 그려져야 할 경우에 부모에게 WM_DRAWITEM메시지를 보 낸다. • lParam : DRAWITEMSTRUCT구조체의 포인터가 들어 있다. /*---------------------------------------BTNLOOK.C -- Button Look Program (c) Charles Petzold, 1998 ----------------------------------------*/ #include <windows.h> struct { int iStyle ; TCHAR * szText ; } button[] = { BS_PUSHBUTTON, TEXT ("PUSHBUTTON"), BS_DEFPUSHBUTTON, TEXT ("DEFPUSHBUTTON"), BS_CHECKBOX, TEXT ("CHECKBOX"), BS_AUTOCHECKBOX, TEXT ("AUTOCHECKBOX"), BS_RADIOBUTTON, TEXT ("RADIOBUTTON"), BS_3STATE, TEXT ("3STATE"), BS_AUTO3STATE, TEXT ("AUTO3STATE"), BS_GROUPBOX, TEXT ("GROUPBOX"), BS_AUTORADIOBUTTON, TEXT ("AUTORADIO"), BS_OWNERDRAW, TEXT ("OWNERDRAW") }; #define NUM (sizeof button / sizeof button[0]) LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("BtnLook") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Button Look"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hwndButton[NUM] ; static RECT rect ; static TCHAR szTop[] = TEXT ("message wParam lParam"), szUnd[] = TEXT ("_______ ______ ______"), szFormat[] = TEXT ("%-16s%04X-%04X %04X-%04X"), szBuffer[50] ; static int cxChar, cyChar ; HDC hdc ; PAINTSTRUCT ps ; int i; switch (message) { case WM_CREATE : cxChar = LOWORD (GetDialogBaseUnits ()) ; cyChar = HIWORD (GetDialogBaseUnits ()) ; for (i = 0 ; i < NUM ; i++) hwndButton[i] = CreateWindow ( TEXT("button"),button[i].szText, WS_CHILD | WS_VISIBLE | button[i].iStyle,cxChar, cyChar * (1 + 2 * i), 20 * cxChar, 7 * cyChar / 4,hwnd, (HMENU) i,((LPCREATESTRUCT) lParam)->hInstance, NULL) ; return 0 ; case WM_SIZE : rect.left = 24 * cxChar ; rect.top = 2 * cyChar ; rect.right = LOWORD (lParam) ; rect.bottom = HIWORD (lParam) ; return 0 ; case WM_PAINT : InvalidateRect (hwnd, &rect, TRUE) ; hdc = BeginPaint (hwnd, &ps) ; SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ; SetBkMode (hdc, TRANSPARENT) ; TextOut (hdc, 24 * cxChar, cyChar, szTop, lstrlen (szTop)) ; TextOut (hdc, 24 * cxChar, cyChar, szUnd, lstrlen (szUnd)) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DRAWITEM : case WM_COMMAND : ScrollWindow (hwnd, 0, -cyChar, &rect, &rect) ; hdc = GetDC (hwnd) ; SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ; TextOut (hdc, 24 * cxChar, cyChar * (rect.bottom / cyChar - 1), szBuffer, wsprintf (szBuffer, szFormat, message == WM_DRAWITEM ? TEXT ("WM_DRAWITEM") : TEXT ("WM_COMMAND"), HIWORD (wParam), LOWORD (wParam), HIWORD (lParam), LOWORD (lParam))) ; ReleaseDC (hwnd, hdc) ; ValidateRect (hwnd, &rect) ; break ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 1. 자식 윈도우 제어 • 단추 클래스 – class name : “button” – WindowText : button[I].szText – Window Style : WS_CHILD|WS_VISIBLE|button[I] – x Position : cxChar – y Position : cyChar * (1 + 2 * I) – Width : 20 * xChar – Height : 7 * yChar / 4 – Parent Window : hwnd – Child window ID : (HMENU) I – Instance handle : ((LPCREATSTRUCT) lParam) ->hInstance – Extra parameters : NULL • 인스턴스 핸들을 얻는 방법 3가지 – WM_CREATE시 : • lParam에 CREATESTRUCT형식의 구조체에 대한 포인터가 들어 있 다. – 전역변수를 이용하는 방법 • hInst = hInstance; – GetWindowLong(hwnd,GWL_HINSTANCE) 2. 자식 윈도우가 부모 윈도우에 메시지 보 내기 – 자식 윈도우 컨트롤은 자신의 부모 윈도우에게 WM_COMMAND 메시지를 보낸다. • LOWORD(wParam) • HIWORD(wParam) • lParam : 자식윈도우 ID : 알림코드 : 자식윈도우의 핸들 – 단추 알림코드의 가능한 값들 • • • • • • • • BN_CLICKED BN_PAINT BN_HILITE or BN_PUSHED BN_UNHILITE or BN_UNPUSHED BN_DISABLE BN_DOUBLECLICKED or BN_DBLCLK BN_SETFOCUS BN_KILLFOCUS : : : : : : : : 0 1 2 3 4 5 6 7 – 알림코드 6과 7은 단추 스타일이 BS_NOTIFY를 포함하는 경우 에만 전달된다. 3. 부모 윈도우가 자식윈도우에게 보내는 메 시지 • BM : Button Message – BM_GETCHECK – BM_SETCHECK • 체크박스와 라디오 단추의 체크표시를 설정하기 위해서 보낸다. – BM_GETSTATE – BM_SETSTATE • 한 윈도우를 마우스로 누르거나 Space Bar를 눌렀을 때의 상태를 의미한다. – BM_SETSTYLE • 단추가 만들어진 후 단추의 스타일을 변경할 수 있게 한다. – BM_CLICK – BM_GETIMAGE – BM_SETIMAGE • 자식 윈도우의 ID를 얻기 – Id = GetWindowLong(hwndChild,GWL_ID); – Id = GetDlgCtlID(hwndChild); – ID를 아는 상태에서 자식윈도우의 핸들 얻기 • hwndChild = GetDlgItem(hwndParent,ID); 4. 누르기 단추 – 마우스로 단추를 누르고 떼면 원래의 모양으로 되돌아오고 부모 에게 WM_COMMAND메시지와 함께 알림코드로 BN_CLICKED 를 보낸다. – 단추 윈도우에 BM_SETSTATE메시지를 보냄으로써 단추의 누른 상태를 지정할 수 있다. • SendMessage(hwndButton,BM_SETSTATE,1,0); – 다음의 호출로 단추는 정상으로 돌아 온다. • SendMessage(hwndButton,BM_SETSTATE,0,0); – 누르기 단추의 상태를 알려면 • SendMessage(hwndButton,BM_GETSTATE,0,0); • 단추가 눌려졌으면 TRUE를, 정상 상태이면 FALSE를 반환한다. 5. 체크 상자 – 체크 상자의 일반적인 두 가지 유형 – BS_CHECKBOX • 컨트롤에 BM_SETCHECK메시지를 전달한 후 체크 표시를 설정해 야 한다. • wParam : 체크표시 1 , 제거 0 • BM_GETCHECK메시지를 전달하여 Check상태를 얻을 수 있다. • SendMessage((HWND)lParam,BM_SETCHECK, (WPARAM)!SendMessage((HWND)lParam, BM_GETCHECK,0,0),0); – BS_AUTOCHECKBOX • 자동으로 자신이 Check표시를 토글 한다. • iCheck =(int)SendMessage(hwndButton,BM_GETCHECK,0,0); – 체크되어 있으면 : TRUE – 체크되어 있지 않으면 : FALSE 6. 라디오 단추 – 라디오 단추에서 WM_COMMAND를 받을 때는 wParam이 1인 BM_SETCHECK메시지를 보내 체크를 표시한다. • SendMessage(hwndButton,BM_SETCHECK,1,0); – 같은 그룹에 있는 모든 라디오 단추에 wParam이 0인 BM_SETCHECK메시지를 전달하여 체크 설정을 취소한다. • SendMessage(hwndButton,BM_SETCHECK,0,0); • 단추 텍스트 변경하기 – SetWindowText를 호출하면 텍스트를 변경할 수 있다. • SetWindowText(hwnd,pszString); • iLength = GetWindowText(hwnd,pszBuffer,iMaxLength) – iMaxLength는 복사할 문자의 최대 개수를 지정 • iLength = GetWindowTextLength(hwnd); 7. 보이는 단추와 사용 가능한 단추 – 자식윈도우를 만들 때 WS_VISIBLE을 포함하지 않으면 ShowWindow를 호출할 때까지 자식 윈도우를 표시하지 않는다. – 윈도우를 보이게 한다. • ShowWindow(hwndChild,SW_SHOWNORMAL); – 윈도우를 숨긴다. • ShowWindow(hwndChild,SW_HIDE); – 자식윈도우가 보이는지 보이지 않는지를 알아낸다. • IsWindowVisible(hwndChild) – 자식윈도우를 사용 가능하게 하거나 불가능하게 한다. • EnableWindow(hwndChild,FALSE); • IsWindowEnabled(hwndChild); 8. 단추와 입력 포커스 – WM_KILLFOCUS시에 wParam 매개 변수는 입력 포커스를 받는 윈도우의 핸들이다. case WM_KILLFOCUS: for ( i = 0; i < NUM; i++) { if (hwndChild[I] == (HWND)wParam) { SetFocus(hwnd); break; } } case WM_KILLFOCUS: if (hwnd == GetParetn((HWND)wParam) { SetFocus(hwnd); break; } 8. WM_CTLCOLORBTN메시지 – 이 메시지는 자식 윈도우가 자신의 클라이언트 영역을 표시할 때 단추 컨트롤이 부모 윈도우에게 보내는 메시지이다. • wParam • lParam : 단추의 장치 컨텍스트에 대한 핸들 : 단추의 윈도우 핸들 • SetTextColor를 사용하여 텍스트 색상을 설정한다. • SetBkColor를 사용하여 텍스트 배경을 설정한다. • 자식 윈도우에 브러쉬 핸들을 반환한다. #include <windows.h> #define ID_SMALLER #define ID_LARGER #define BTN_WIDTH #define BTN_HEIGHT 1 2 (8 * cxChar) (4 * cyChar) LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; HINSTANCE hInst ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("OwnDraw") ; MSG msg ; HWND hwnd ; WNDCLASS wndclass ; hInst = hInstance ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = szAppName ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Owner-Draw Button Demo"),WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } void Triangle (HDC hdc, POINT pt[]) { SelectObject (hdc, GetStockObject (BLACK_BRUSH)) ; Polygon (hdc, pt, 3) ; SelectObject (hdc, GetStockObject (WHITE_BRUSH)) ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hwndSmaller, hwndLarger ; static int cxClient, cyClient, cxChar, cyChar ; int cx, cy ; LPDRAWITEMSTRUCT pdis ; POINT pt[3] ; RECT rc ; switch (message) { case WM_CREATE : cxChar = LOWORD (GetDialogBaseUnits ()) ; cyChar = HIWORD (GetDialogBaseUnits ()) ; // Create the owner-draw pushbuttons hwndSmaller = CreateWindow (TEXT ("button"), TEXT (""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, BTN_WIDTH, BTN_HEIGHT,hwnd, (HMENU) ID_SMALLER, hInst, NULL) ; hwndLarger = CreateWindow (TEXT ("button"), TEXT (""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, BTN_WIDTH, BTN_HEIGHT, hwnd, (HMENU) ID_LARGER, hInst, NULL) ; return 0 ; case WM_SIZE : cxClient = LOWORD (lParam) ; cyClient = HIWORD (lParam) ; // Move the buttons to the new center MoveWindow (hwndSmaller,cxClient/2-3*BTN_WIDTH/2, cyClient/2-BTN_HEIGHT/2,BTN_WIDTH, BTN_HEIGHT,TRUE); MoveWindow (hwndLarger,cxClient/2+BTN_WIDTH/2,cyClient/2-BTN_HEIGHT/2,BTN_WIDTH, BTN_HEIGHT,TRUE); return 0 ; case WM_COMMAND : GetWindowRect (hwnd, &rc) ; // Make the window 10% smaller or larger switch (wParam) { case ID_SMALLER : rc.left += cxClient / 20 ; rc.right -= cxClient / 20 ; rc.top += cyClient / 20 ; rc.bottom -= cyClient / 20 ; break ; case ID_LARGER : rc.left -= cxClient / 20 ; rc.right += cxClient / 20 ; rc.top -= cyClient / 20 ; rc.bottom += cyClient / 20 ; break ; } MoveWindow (hwnd, rc.left, rc.top, rc.right - rc.left, return 0 ; rc.bottom - rc.top, TRUE) ; case WM_DRAWITEM : pdis = (LPDRAWITEMSTRUCT) lParam ; // Fill area with white and frame it black FillRect (pdis->hDC, &pdis->rcItem,(HBRUSH) GetStockObject (WHITE_BRUSH)) ; FrameRect (pdis->hDC, &pdis->rcItem,(HBRUSH) GetStockObject (BLACK_BRUSH)) ; // Draw inward and outward black triangles cx = pdis->rcItem.right - pdis->rcItem.left ; cy = pdis->rcItem.bottom - pdis->rcItem.top ; switch (pdis->CtlID) { case ID_SMALLER : pt[0].x = 3 * cx / 8 ; pt[0].y = 1 * cy / 8 ; pt[1].x = 5 * cx / 8 ; pt[1].y = 1 * cy / 8 ; pt[2].x = 4 * cx / 8 ; pt[2].y = 3 * cy / 8 ; Triangle (pdis->hDC, pt) ; pt[0].x = 7 * cx / 8 ; pt[0].y = 3 * cy / 8 ; pt[1].x = 7 * cx / 8 ; pt[1].y = 5 * cy / 8 ; pt[2].x = 5 * cx / 8 ; pt[2].y = 4 * cy / 8 ; Triangle (pdis->hDC, pt) ; pt[0].x = 5 * cx / 8 ; pt[0].y = 7 * cy / 8 ; pt[1].x = 3 * cx / 8 ; pt[1].y = 7 * cy / 8 ; pt[2].x = 4 * cx / 8 ; pt[2].y = 5 * cy / 8 ; Triangle (pdis->hDC, pt) ; pt[0].x = 1 * cx / 8 ; pt[0].y = 5 * cy / 8 ; pt[1].x = 1 * cx / 8 ; pt[1].y = 3 * cy / 8 ; pt[2].x = 3 * cx / 8 ; pt[2].y = 4 * cy / 8 ; Triangle (pdis->hDC, pt) ; break ; case ID_LARGER : pt[0].x = 5 * cx / 8 ; pt[0].y = 3 * cy / 8 ; pt[1].x = 3 * cx / 8 ; pt[1].y = 3 * cy / 8 ; pt[2].x = 4 * cx / 8 ; pt[2].y = 1 * cy / 8 ; Triangle (pdis->hDC, pt) ; pt[0].x = 5 * cx / 8 ; pt[0].y = 5 * cy / 8 ; pt[1].x = 5 * cx / 8 ; pt[1].y = 3 * cy / 8 ; pt[2].x = 7 * cx / 8 ; pt[2].y = 4 * cy / 8 ; Triangle (pdis->hDC, pt) ; pt[0].x = 3 * cx / 8 ; pt[0].y = 5 * cy / 8 ; pt[1].x = 5 * cx / 8 ; pt[1].y = 5 * cy / 8 ; pt[2].x = 4 * cx / 8 ; pt[2].y = 7 * cy / 8 ; Triangle (pdis->hDC, pt) ; pt[0].x = 3 * cx / 8 ; pt[0].y = 3 * cy / 8 ; pt[1].x = 3 * cx / 8 ; pt[1].y = 5 * cy / 8 ; pt[2].x = 1 * cx / 8 ; pt[2].y = 4 * cy / 8 ; Triangle (pdis->hDC, pt) ; break ; } // Invert the rectangle if the button is selected if (pdis->itemState & ODS_SELECTED) InvertRect (pdis->hDC, &pdis->rcItem) ; // Draw a focus rectangle if the button has the focus if (pdis->itemState & ODS_FOCUS) { pdis->rcItem.left += cx / 16 ; pdis->rcItem.top += cy / 16 ; pdis->rcItem.right -= cx / 16 ; pdis->rcItem.bottom -= cy / 16 ; DrawFocusRect (pdis->hDC, &pdis->rcItem) ; } return 0 ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 9. BS_OWNERDRAW 스타일 단추 – 단추는 다시 표시되어야 할 때마다 자신의 부모 윈도우에게 WM_DRAWITEM메시지를 전달한다. – WM_DRAWITEM메시지를 처리하는 동안 lParam에는 DRAWITEMSTRUCT구조체의 포인터를 return한다. • • • • hDC : 단추에 대한 DC rcItem: 단추 크기를 제공한느 RECT구조체 CtlID : 컨트롤 윈도우ID itemState : 단추가 눌린 상태인지 또는 입력 포커스를 가지고 있는 지를 나타낸다.ODS_SELECT,ODS_FOCUS – Progrmming Windows Page 451 참조 10. 정적 클래스 – 마우스나 키보드 입력을 받지 않으며, 부모 윈도우에 WM_COMMAND메시지를 전송하지 않는다. – 정적 자식 윈도우 위로 마우스를 옮기거나 클릭 할 때, 자식 윈도 우는 WM_NCHITTEST메시지를 가로채고, HTTRANSPARENT를 윈도우에 반환한다. – SS_LEFT,SS_RIGHT,SS_CENTER를 포함하여 문자열을 정렬 11. 스크롤 바 클래스 – 스크롤 바 컨트롤은 부모 윈도우에WM_COMMAND를 보내지 않 는 대신, 윈도우 스크롤 바처럼 WM_VSCROLL과WM_HSCROLL 메시지를 보낸다. – 스크롤 바 메시지를 처리할 때, • lParam 매개 변수로 윈도우 스크롤 바 컨트롤과 컨트롤 스크롤 바 사이를 구별할 수 있다. • 컨트롤 스크롤 바 : – lParam 값 : 윈도우의 핸들 – hwndScrollBar = (HWND) lParam; • 윈도우 스크롤 바 : – lParam 값 : 0 • wParam : – nScrollCode = (int)LOWORD(wParam); – nPos = (short int)HIWORD(wParam); 11. 스크롤 바 클래스 Value SB_BOTTOM SB_ENDSCROLL SB_LINEDOWN SB_LINEUP SB_PAGEDOWN SB_PAGEUP SB_THUMBPOSITION SB_THUMBTRACK SB_TOP – – – – Description Scrolls to the lower right Ends scroll Scrolls one line down Scrolls one line up Scrolls one page down Scrolls one page up The user has dragged the scroll box (thumb) and released the mouse button. The nPos parameter indicates the position of the scroll box at the end of the drag operation. The user is dragging the scroll box. This message is sent repeatedly until the user releases the mouse button. The nPos parameter indicates the position that the scroll box has been dragged to. Scrolls to the upper left SetScrollRange(hwndScroll,SB_CTL,iMin,iMax,bRedraw); SetScrollPos(hwndScroll,SB_CTL,iPos,bRedraw); SetScrollInfo(hwndScroll,SB_CTL,&si,bRedraw); 윈도우 스크롤 바는 • 첫번째 매개변수로 메인 윈도우의 핸들, • 두번째 매개변수로 SB_VERT,SB_HORZ를 사용 /*---------------------------------------COLORS1.C -- Colors Using Scroll Bars (c) Charles Petzold, 1998 ----------------------------------------*/ #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; LRESULT CALLBACK ScrollProc (HWND, UINT, WPARAM, LPARAM) ; int idFocus ; WNDPROC OldScroll[3] ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("Colors1") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = CreateSolidBrush (0) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Color Scroll"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static COLORREF crPrim[3] = { RGB (255, 0, 0), RGB (0, 255, 0), RGB (0, 0, 255) } ; static HBRUSH hBrush[3], hBrushStatic ; static HWND hwndScroll[3], hwndLabel[3], hwndValue[3], hwndRect ; static int color[3], cyChar ; static RECT rcColor ; static TCHAR * szColorLabel[] = { TEXT ("Red"), TEXT ("Green"), TEXT ("Blue") } ; HINSTANCE hInstance ; int i, cxClient, cyClient ; TCHAR szBuffer[10] ; switch (message) { case WM_CREATE : hInstance = (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE) ; // Create the white-rectangle window against which the // scroll bars will be positioned. The child window ID is 9. hwndRect = CreateWindow (TEXT ("static"), NULL, WS_CHILD | WS_VISIBLE | SS_WHITERECT, 0, 0, 0, 0, hwnd, (HMENU) 9, hInstance, NULL) ; for (i = 0 ; i < 3 ; i++) { // The three scroll bars have IDs 0, 1, and 2, with // scroll bar ranges from 0 through 255. hwndScroll[i] = CreateWindow (TEXT ("scrollbar"), NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_VERT, 0, 0, 0, 0, hwnd, (HMENU) i, hInstance, NULL) ; SetScrollRange (hwndScroll[i], SB_CTL, 0, 255, FALSE) ; SetScrollPos (hwndScroll[i], SB_CTL, 0, FALSE) ; // The three color-name labels have IDs 3, 4, and 5, // and text strings "Red", "Green", and "Blue". hwndLabel [i] = CreateWindow (TEXT ("static"), szColorLabel[i], WS_CHILD | WS_VISIBLE | SS_CENTER, 0, 0, 0, 0, hwnd, (HMENU) (i + 3), hInstance, NULL) ; // The three color-value text fields have IDs 6, 7, // and 8, and initial text strings of "0". hwndValue [i] = CreateWindow (TEXT ("static"), TEXT ("0"),WS_CHILD | WS_VISIBLE | SS_CENTER, 0, 0, 0, 0,hwnd, (HMENU) (i + 6), hInstance, NULL) ; OldScroll[i] = (WNDPROC) SetWindowLong (hwndScroll[i], GWL_WNDPROC, (LONG) ScrollProc) ; hBrush[i] = CreateSolidBrush (crPrim[i]) ; } hBrushStatic = CreateSolidBrush (GetSysColor (COLOR_BTNHIGHLIGHT)) ; cyChar = HIWORD (GetDialogBaseUnits ()) ; return 0 ; case WM_SIZE : cxClient = LOWORD (lParam) ; cyClient = HIWORD (lParam) ; SetRect (&rcColor, cxClient / 2, 0, cxClient, cyClient) ; MoveWindow (hwndRect, 0, 0, cxClient / 2, cyClient, TRUE) ; for (i = 0 ; i < 3 ; i++) { MoveWindow (hwndScroll[i],(2 * i + 1) * cxClient / 14, 2 * cyChar,cxClient / 14, cyClient - 4 * cyChar, TRUE) ; MoveWindow (hwndLabel[i],(4 * i + 1) * cxClient / 28, cyChar / 2,cxClient / 7, cyChar, TRUE) ; MoveWindow (hwndValue[i],(4 * i + 1) * cxClient / 28, cyClient - 3 * cyChar / 2,cxClient / 7, cyChar, TRUE) ; } SetFocus (hwnd) ; return 0 ; case WM_SETFOCUS : SetFocus (hwndScroll[idFocus]) ; return 0 ; case WM_VSCROLL : i = GetWindowLong ((HWND) lParam, GWL_ID) ; switch (LOWORD (wParam)) { case SB_PAGEDOWN :color[i] += 15 ;break; case SB_LINEDOWN :color[i] = min (255, color[i] + 1) ;break ; case SB_PAGEUP :color[i] -= 15 ;; break; case SB_LINEUP :color[i] = max (0, color[i] - 1) ;break ; case SB_TOP : color[i] = 0 ; break ; case SB_BOTTOM :color[i] = 255 ;break ; case SB_THUMBPOSITION : case SB_THUMBTRACK :color[i] = HIWORD (wParam) ;break ; default :break ; } SetScrollPos (hwndScroll[i], SB_CTL, color[i], TRUE) ; wsprintf (szBuffer, TEXT ("%i"), color[i]) ; SetWindowText (hwndValue[i], szBuffer) ; DeleteObject ((HBRUSH)SetClassLong (hwnd, GCL_HBRBACKGROUND, (LONG) CreateSolidBrush (RGB (color[0], color[1], color[2])))) ; InvalidateRect (hwnd, &rcColor, TRUE) ; return 0 ; case WM_CTLCOLORSCROLLBAR : i = GetWindowLong ((HWND) lParam, GWL_ID) ; return (LRESULT) hBrush[i] ; case WM_CTLCOLORSTATIC : i = GetWindowLong ((HWND) lParam, GWL_ID) ; if (i >= 3 && i <= 8) // static text controls { SetTextColor ((HDC) wParam, crPrim[i % 3]) ; SetBkColor ((HDC) wParam, GetSysColor (COLOR_BTNHIGHLIGHT)); return (LRESULT) hBrushStatic ; } break ; case WM_SYSCOLORCHANGE : DeleteObject (hBrushStatic) ; hBrushStatic = CreateSolidBrush (GetSysColor (COLOR_BTNHIGHLIGHT)) ; return 0 ; case WM_DESTROY : DeleteObject ((HBRUSH) SetClassLong (hwnd, GCL_HBRBACKGROUND, (LONG)GetStockObject (WHITE_BRUSH))) ; for (i = 0 ; i < 3 ; i++) DeleteObject (hBrush[i]) ; DeleteObject (hBrushStatic) ; PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } LRESULT CALLBACK ScrollProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { int id = GetWindowLong (hwnd, GWL_ID) ; switch (message) { case WM_KEYDOWN : if (wParam == VK_TAB) SetFocus (GetDlgItem (GetParent (hwnd),(id + (GetKeyState (VK_SHIFT) < 0 ? 2 : 1)) % 3)) ; break ; case WM_SETFOCUS : idFocus = id ; break ; } return CallWindowProc (OldScroll[id], hwnd, message, wParam, lParam) ; } 12. 윈도우 서브클래싱(SubClassing) – 스크롤 바 컨트롤에 대한 윈도우 프로시저는 Window내부 어느 곳인가에 있다. – GWL_WNDPROC를 사용하여 GetWindowLong을 호출하여 윈 도우 프로시저에 대한 주소를 얻을 수 있다. – SetWindowLong을 이용하여 윈도우 프로시저를 설정할 수도 있 다. – OldScroll[I] = (WNDPROC)SetWindowLong (hwndScroll[I],GWL_WNDPROC,(LONG) ScrollProc)); – WM_CTLCOLORSCROLLBAR • 스크롤 바가 자신이 그려져야 할 때 부모 윈도우에게 이 메시지를 보낸다. • hdcSB = (HDC) wParam; • hwndSB = (HWND) lParam; • 스크롤 바의 배경을 칠할 브러쉬의 핸들을 리턴한다. /*------------------------------------------------------POPPAD1.C -- Popup Editor using child window edit box (c) Charles Petzold, 1998 -------------------------------------------------------*/ #include <windows.h> #define ID_EDIT 1 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); TCHAR szAppName[] = TEXT ("PopPad1") ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, szAppName,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hwndEdit ; switch (message) { case WM_CREATE : hwndEdit = CreateWindow (TEXT ("edit"), NULL,WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL,0, 0, 0, 0, hwnd, (HMENU) ID_EDIT,((LPCREATESTRUCT) lParam) -> hInstance, NULL) ; return 0 ; case WM_SETFOCUS : SetFocus (hwndEdit) ; return 0 ; case WM_SIZE : MoveWindow (hwndEdit, 0, 0, LOWORD (lParam), HIWORD (lParam), TRUE) ; return 0 ; case WM_COMMAND : if (LOWORD (wParam) == ID_EDIT) if (HIWORD (wParam) == EN_ERRSPACE || HIWORD (wParam) == EN_MAXTEXT) MessageBox (hwnd, TEXT ("Edit control out of space."),szAppName, MB_OK | MB_ICONSTOP) ; return 0 ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 13. 에디트 클래스 스타일 – 에디트 컨트롤은 부모 윈도우에 WM_COMMAND메시지를 보낸 다. • LOWORD(wParam) • HIWORD(wParam) • lParam 자식 윈도우 알림 코드 자식 윈도우 핸들 – 기본값으로 에디트 컨트롤은 하나의 줄을 갖는다. • 여러줄 : ES_MULTILINE – 자동적으로 수평 방향으로 스크롤하려면 • ES_AUTOHSCROLL – 입력 포커스를 잃은 때에도 계속 하이라이트되게 하려면 • ES_NOHIDESEL – 인식 코드 • • • • • • • • EN_SETFOCUS : 컨트롤이 입력 포커스를 얻었다. EN_KILLFOCUS : 컨트롤이 입력 포커스를 잃었다. EN_CHANGE : 컨트롤의 내용이 변경될 것이다. EN_UPDATE : 컨트롤의 내용이 변경되었다. EN_ERRSPACE : 컨트롤의 여백이 없다. EN_MAXTEXT : 컨트롤에 삽입할 공간이 없다. EN_HSCROLL : 컨트롤의 수평 스크롤 바가 클릭 되었다. EN_VSCROLL : 컨트롤의 수직 스크롤 바가 클릭 되었다. 14. 에디트 컨트롤에 메시지를 전송하기 – 현재 선택된 것을 자르고,복사하고,지우기 • • • • SendMessage(hwndEdit,WM_CUT,0,0); SendMessage(hwndEdit,WM_COPY,0,0); SendMessage(hwndEdit,WM_CLEAR,0,0); SendMessage(hwndEdit,WM_PASTE,0,0); – 현재 선택의 시작과 끝을 얻을 수 있다. • SendMessage(hwndEdit,EM_GETSEL,(WPARAM) &iStart, (LPARAM) &iEND); – 텍스트를 선택 • SendMEssage(hwndEdit,EM_SETSEL,iStart,iEnd); – 다른 텍스트를 교체 • SendMEssage(hwndEdit,EM_REPLACESEL,0,(LPARAM)szString); – 멀티라인 에디트 컨트롤에서 줄의 수 • iCount = SendMessage(hwndEdit,EM_GETLINECOUNT,0,0); – 줄 길이를 얻을 수 있다. • iLength = SendMessage(hwndEdit,EM_LINELENGTH,iLine,0); – 줄 자체를 버퍼에 복사 • iLength = SendMessage(hwndEdit,EM_GETLINE,iLine, (LPARAM) szBuffer); 15. 목록 상자 스타일 – 윈도우 클래스로는 listbox를 사용 – 기본 목록 상자 스타일을 부모에게 WM_COMMAND를 보내지 않 는다. – 목록 상자 스타일 LBS_NOTIFY를 사용하여, WM_COMMAND메시 지를 부모 윈도우가 받도록 해준다. – 다중 목록 상자를 만들려면 LBS_MULTIPLESEL스타일을 사용한 다. – 새로운 항목이 스크롤 바 목록에 추가 될 때마다 자신을 갱신한다. LBS_NOREDRAW스타일을 포함하여 이것을 방지 – SendMessage를 사용하여 텍스트를 삽입 • SendMessage(hwndlist,LB_ADDSTRING,0,(LPARAM)szString); • SendMessage(hwndlist,LB_INSERTSTRING,iIndex,(LPARAM)szStri ng); • SendMessage(hwndlist,LB_DELETESTRING,iIndex, 0); – 목록을 모두 삭제 • SendMessage(hwndlist,LB_RESETCONTENT,0, 0); – 컨트롤의 다시 그리기 플래그를 끄기 • SendMessage(hwndlist,WM_SETREDRAW,FALSE,0); • SendMessage(hwndlist,WM_SETREDRAW,TRUE,0); 15. 목록 상자 스타일 스타일 설명 LBS_DISABLENOSCR OLL 리스트박스는 항목이 많으면 스크롤 바를 보여주고 스 크롤 할 항목이 없으면 스크롤 바를 숨긴다. 스크롤할 항목이 없어도 스크롤 바를 숨기지 않고 흐린 모양의 스크롤 바를 보여준다. LBS_EXENDEDSEL Shift키와 마우스 또는 특수한 키 조합을 사용하여 복수 개의 항목을 선택할 수 있도록 한다. LBS_HASSTRING 오너 드로우 스타일이 지정된 경우 문자열도 함께 가질 것인가를 지정한다. LBS_MULTIPLESEL 여러 개의 항목을 선택할 수 있도록 한다. LBS_NOTIFY 사용자가 목록 중 하나를 선택했을 때 부모 윈도우로 통지 메시지를 보내도록 한다. LBS_SORT 추가된 항목들을 자동 정렬하도록 한다. LBS_OWNERDRAW 문자열이 아닌 비트맵이나 그림을 넣을 수 있도록 한다. LBS_MULTICOLUMN 수평으로 여러 단을 이루어 항목을 보여주도록 한다. LB_SETCOLUMNWIDTH 메시지로 각 단의 폭을 지정 LBS_STANDARD LBS_NOTIFY|LBS_SORT|LBS_BORDER 15. 목록 상자 스타일 스타일 설명 LB_ADDSTRING 리스트 박스에 항목을 추가한다. lParam으로 추가하고 자 하는 문자열의 번지를 넘겨준다. LB_DELETESTRING 항목을 삭제한다. wParam으로 항목의 번호를 넘겨주 며 남은 항목 수를 리턴한다. LB_GETCURSEL 현재 선택된 항목의 번호를 리턴한다. LB_GETTEXT 지정한 항목의 문자열을 읽는다. wParam에 항목 번 호,lParam에 문자열 버퍼의 번지 LB_DIR 파일 목록을 리스트 박스에 추가한다. LB_FINDSTRING 주어진 검색식에 맞는 첫 번째 항목의 인덱스를 조사한 다. LB_GETCOUNT 총 항목 개수를 조사한다. LB_GETITEMDATA 주어진 항목의 항목 데이터를 조사한다. LB_GETITEMRECT 주어진 항목의 작업영역 좌표를 조사한다. 15. 목록 상자 스타일 스타일 설명 LB_GETSEL 항목의 선택 상태를 조사한다. LB_GETSELCOUNT 다중 선택 리스트 박스에서 선택된 항목의 개수를 조사 LB_GETSELITEMS 다중 선택 리스트 박스에서 선택된 항목의 인덱스를 배 열에 채워준다. LB_GETTEXT 주어진 항목의 문자열을 조사한다. LB_GETTEXTLEN 주어진 항목의 문자열의 길이를 조사한다. LB_RESETCONTENT 모든 항목을 삭제한다. LB_SETITEMDATA 항목 데이터를 대입한다. 15. 목록 상자 스타일 • 항목 선택 및 추출하기 – 항목의 개수를 알아낸다. • iCount = SendMessage(hwndList,LB_GETCOUNT,0,0); – 해당 항목을 선택 • SendMessage(hwndList,LB_SETCURSEL,iIndex,0); – 항목의 첫 문자에 근거하여 항목을 선택 • iIndex = SendMessage(hwndList, LB_SELECTSTRING,iIndex, (LPARAM)szSearchString); • WPARAM으로 제공되는 iIndex는szSearchString과 부합되는 첫 문자 를 가진 항목에 대한 검색이 시작될 때 인덱스값이다. • 부합되는 첫문자가 없으면 LB_ERR를 반환 – 현재 선택의 인덱스를 알아 낸다. • iIndex = SendMessage(hwndList,LB_GETCURSEL,0,0); – 어떤 문자열의 길이 리턴 • iLength = SendMessage(hwndList,LB_GETTEXTLEN, iIndex,0); – 항목을 텍스트 버퍼로 복사할 수 있다. • iLength = SendMessage(hwndList,LB_GETTEXT,iIndex, (LPARAM)szBuffer); – 다중 선택 목록에서는 LB_SETCURSEL, LB_GETCURSEL, LB_SELECTSTRING을 사용할 수 없다. 15. 목록 상자 스타일 – 다중 선택 목록에서 특정 항목의 선택 상태를 설정하기 위해 LB_SETSEL을 사용 • SendMessage(hwndList,LB_SETSEL,wParam,iIndex); • wParam => 취소 : 0, 선택은 0이 아닌 값 – 특정 항목의 선택 상태를 알려면 LB_GETSEL이용 • iSelect=SendMessage(hwndList,LB_GETSEL,iIndex,0); • iSelect : iIndex가 항목이 선택되어 있으면 0이 아닌 값 • 선택되어 있지 않으면 0으로 설정 – 목록 상자는 자신의 부모에게 WM_COMMAND를 보낸다. • LOWORD(wParam) • HIWORD(wPram) • LParam : 자식 윈도우 ID : 알림 코드 : 자식 윈도우 핸들 – 알림 코드 • LBN_ERRSPACE : – 목록 상자 컨트롤이 공간이 부족하다는 것을 가리킨다. • LBN_SELCHANGE – 현재 선택이 변경되었다는 것을 가리킨다. – 사용자가 하이라이트 목록 상자를 통해 이동시키고 Space bar키로 선택 상태를 토글하거나 마우스로 항목을 선택할 때 발생한다. • LBN_DBLCLK – 목록 상자 항목이 마우스로 더블 클릭 되었음을 가리킨다. – 윈도우 스타일이 LBS_NOTIFY일 경우에만 BN_SELCHANGE,LBN_DBLCLK를 받을 수 있다. /*---------------------------------------ENVIRON.C -- Environment List Box (c) Charles Petzold, 1998 ----------------------------------------*/ #include <windows.h> #define ID_LIST 1 #define ID_TEXT 2 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("Environ") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Environment List Box"),WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } void FillListBox (HWND hwndList) { int iLength ; TCHAR * pVarBlock, * pVarBeg, * pVarEnd, * pVarName ; pVarBlock = GetEnvironmentStrings () ; // Get pointer to environment block while (*pVarBlock) { if (*pVarBlock != '=') // Skip variable names beginning with '=' { pVarBeg = pVarBlock ; // Beginning of variable name while (*pVarBlock++ != '=') ; // Scan until '=' pVarEnd = pVarBlock - 1 ; // Points to '=' sign iLength = pVarEnd - pVarBeg ; // Length of variable name // Allocate memory for the variable name and terminating // zero. Copy the variable name and append a zero. pVarName = calloc (iLength + 1, sizeof (TCHAR)) ; CopyMemory (pVarName, pVarBeg, iLength * sizeof (TCHAR)) ; pVarName[iLength] = '\0' ; // Put the variable name in the list box and free memory. SendMessage (hwndList, LB_ADDSTRING, 0, (LPARAM) pVarName) ; free (pVarName) ; } while (*pVarBlock++ != '\0') ; // Scan until terminating zero } FreeEnvironmentStrings (pVarBlock) ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam) { static HWND hwndList, hwndText ; int iIndex, iLength, cxChar, cyChar ; TCHAR * pVarName, * pVarValue ; switch (message) { case WM_CREATE : cxChar = LOWORD (GetDialogBaseUnits ()) ; cyChar = HIWORD (GetDialogBaseUnits ()) ; // Create listbox and static text windows. hwndList = CreateWindow (TEXT ("listbox"), NULL,WS_CHILD | WS_VISIBLE | LBS_STANDARD, cxChar, cyChar * 3,cxChar * 16 + GetSystemMetrics (SM_CXVSCROLL),cyChar * 5, hwnd, (HMENU) ID_LIST,(HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE),NULL) ; hwndText = CreateWindow (TEXT ("static"), NULL,WS_CHILD | WS_VISIBLE | SS_LEFT, cxChar, cyChar, GetSystemMetrics (SM_CXSCREEN), cyChar,hwnd, (HMENU) ID_TEXT, (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE),NULL) ; FillListBox (hwndList) ; return 0 ; case WM_SETFOCUS : SetFocus (hwndList) ; return 0 ; case WM_COMMAND : if (LOWORD (wParam) == ID_LIST && HIWORD (wParam) == LBN_SELCHANGE) { iIndex = SendMessage (hwndList, LB_GETCURSEL, 0, 0) ; iLength = SendMessage (hwndList, LB_GETTEXTLEN, iIndex, 0) + 1 ; pVarName = calloc (iLength, sizeof (TCHAR)) ; SendMessage (hwndList, LB_GETTEXT, iIndex, (LPARAM) pVarName) ; iLength = GetEnvironmentVariable (pVarName, NULL, 0) ; pVarValue = calloc (iLength, sizeof (TCHAR)) ; GetEnvironmentVariable (pVarName, pVarValue, iLength) ; SetWindowText (hwndText, pVarValue) ; free (pVarName) ; free (pVarValue) ; } return 0 ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 16. 파일 나열하기 – LB_DIR • 목록 상자를 파일 디렉토리 리스트와 옵션으로 서브 디렉토리와 유효 한 디스크 드라이브로 채운다. • SendMessage(hwndList,LB_DIR,iAttr,(LPARAM)szFileSpec); • iAttr – – – – – – – – – 파일 속성 코드이다. DDL_READWRITE : 일반 파일 DDL_READONLY : 읽기 전용 DDL_HIDDEN : 숨김 DDL_SYSTEM : 시스템 DDL_DIRECTORY : 하위 디렉토리 DDL_ARCHIVE : 보관 bit가 설정된 파일 DDL_DRIVES : 드라이브 문자 포함 DDL_EXECLUSIVE : 독점 검색 전용 • lParam은 “*.*”같은 파일 지정 문자열에 대한 포인터 /*--------------------------------------------HEAD.C -- Displays beginning (head) of file (c) Charles Petzold, 1998 ---------------------------------------------*/ #include <windows.h> #define ID_LIST 1 #define ID_TEXT 2 #define MAXREAD 8192 #define DIRATTR (DDL_READWRITE | DDL_READONLY | DDL_HIDDEN | DDL_SYSTEM | DDL_DIRECTORY | DDL_ARCHIVE | DDL_DRIVES) #define DTFLAGS (DT_WORDBREAK | DT_EXPANDTABS | DT_NOCLIP | DT_NOPREFIX) LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; LRESULT CALLBACK ListProc (HWND, UINT, WPARAM, LPARAM) ; WNDPROC OldList ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("head") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("head"), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static BOOL bValidFile ; static BYTE buffer[MAXREAD] ; static HWND hwndList, hwndText ; static RECT rect ; static TCHAR szFile[MAX_PATH + 1] ; HANDLE hFile ; HDC hdc ; int i, cxChar, cyChar ; PAINTSTRUCT ps ; TCHAR szBuffer[MAX_PATH + 1] ; switch (message) { case WM_CREATE : cxChar = LOWORD (GetDialogBaseUnits ()) ; cyChar = HIWORD (GetDialogBaseUnits ()) ; rect.left = 20 * cxChar ; rect.top = 3 * cyChar ; hwndList = CreateWindow (TEXT ("listbox"), NULL,WS_CHILDWINDOW | WS_VISIBLE | LBS_STANDARD, cxChar, cyChar * 3,cxChar * 13 + GetSystemMetrics (SM_CXVSCROLL),cyChar * 10,hwnd, (HMENU) ID_LIST,(HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE),NULL) ; GetCurrentDirectory (MAX_PATH + 1, szBuffer) ; hwndText = CreateWindow (TEXT ("static"), szBuffer,WS_CHILDWINDOW | WS_VISIBLE | SS_LEFT, cxChar, cyChar, cxChar * MAX_PATH, cyChar,hwnd, (HMENU) ID_TEXT, (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE),NULL) ; OldList = (WNDPROC) SetWindowLong (hwndList, GWL_WNDPROC,(LPARAM) ListProc) ; SendMessage (hwndList, LB_DIR, DIRATTR, (LPARAM) TEXT ("*.*")) ; return 0 ; case WM_SIZE : rect.right = LOWORD (lParam) ; rect.bottom = HIWORD (lParam) ; return 0 ; case WM_SETFOCUS : SetFocus (hwndList) ; return 0 ; case WM_COMMAND : if (LOWORD (wParam) == ID_LIST && HIWORD (wParam) == LBN_DBLCLK) { if (LB_ERR == (i = SendMessage (hwndList, LB_GETCURSEL, 0, 0))) break ; SendMessage (hwndList, LB_GETTEXT, i, (LPARAM) szBuffer) ; if (INVALID_HANDLE_VALUE != (hFile = CreateFile (szBuffer, GENERIC_READ, FILE_SHARE_READ, NULL,OPEN_EXISTING, 0, NULL))) { CloseHandle (hFile) ; bValidFile = TRUE ; lstrcpy (szFile, szBuffer) ; GetCurrentDirectory (MAX_PATH + 1, szBuffer) ; if (szBuffer [lstrlen (szBuffer) - 1] != '\\') lstrcat (szBuffer, TEXT ("\\")) ; SetWindowText (hwndText, lstrcat (szBuffer, szFile)) ; } else { bValidFile = FALSE ; szBuffer [lstrlen (szBuffer) - 1] = '\0' ; // If setting the directory doesn't work, maybe it's // a drive change, so try that. if (!SetCurrentDirectory (szBuffer + 1)) { szBuffer [3] = ':' ; szBuffer [4] = '\0' ; SetCurrentDirectory (szBuffer + 2) ; } // Get the new directory name and fill the list box. GetCurrentDirectory (MAX_PATH + 1, szBuffer) ; SetWindowText (hwndText, szBuffer) ; SendMessage (hwndList, LB_RESETCONTENT, 0, 0) ; SendMessage (hwndList, LB_DIR, DIRATTR,(LPARAM) TEXT ("*.*")) ; } InvalidateRect (hwnd, NULL, TRUE) ; } return 0 ; case WM_PAINT : if (!bValidFile) break ; if (INVALID_HANDLE_VALUE == (hFile = CreateFile (szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL))) { bValidFile = FALSE ; break ; } ReadFile (hFile, buffer, MAXREAD, &i, NULL) ; CloseHandle (hFile) ; hdc = BeginPaint (hwnd, &ps) ; SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ; SetTextColor (hdc, GetSysColor (COLOR_BTNTEXT)) ; SetBkColor (hdc, GetSysColor (COLOR_BTNFACE)) ; // Assume the file is ASCII DrawTextA (hdc, buffer, i, &rect, DTFLAGS) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } LRESULT CALLBACK ListProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { if (message == WM_KEYDOWN && wParam == VK_RETURN) SendMessage (GetParent (hwnd), WM_COMMAND, MAKELONG (1, LBN_DBLCLK), (LPARAM) hwnd) ; return CallWindowProc (OldList, hwnd, message, wParam, lParam) ; } 17. OWNERDRAW – 오너 드로우에 사용되는 메시지에는 두 가지가 있다. – WM_MEASUREITEM • 이 메시지는 리스트 박스의 항목이 그려지기 전에 항목의 크기를 부모 윈도우에게 물어보기 위해 사용한다. typedef struct MEASUREITEMSTRUCT { UINT CtlType; UINT CtlID; UINT itemID; UINT itemWidth; UINT itemHeight; ULONG_PTR itemData; } MEASUREITEMSTRUCT 17. OWNERDRAW CtlType 컨트롤의 타입, ODT_BUTTON, ODT_COMBOBOX, ODT_LISTBOX,ODT_LISTVIEW, ODT_MENU, ODT_STATIC CtlID 컨트롤의 ID itemID 메뉴 항목의 ID, 또는 리스트 박스나 콤보 박스의 항목 인덱스 itemWidth 항목의 폭을 지정하는 멤버. 부모 윈도우는 이 멤버에 항목의 폭 을 대입해 주어야 한다. itemHeight 항목의 높이를 지정하는 멤버. 부모 윈도우는 이 멤버에 항목의 높이를 대입해 주어야 한다. itemData 메뉴나 리스트 박스의 각 항목에 저장된 항목 데이터이다. LB_HASSTRINGS스타일을 가진 경우 이 값은 LB_SETITEMDATA에 의해 기억된 값이지만 그렇지 않은 경우에 는 LB_ADDSTRING메시지의 lParam을 통해 전달된 값이다. – 이 메시지를 처리했으면 반드시 TRUE를 리턴해 주어야 한다. 17. OWNERDRAW – WM_DRAWITEM • 이 메시지의 wParam으로는 컨트롤의 ID가 전달되며, lParam에는 DRAWITEMSTRUCT의 포인터가 전달된다. typedef struct tagDRAWITEMSTRUCT { UINT CtlType; UINT CtlID; UINT itemID; UINT itemAction; UINT itemState; HWND hwndItem; HDC hDC; RECT rcItem; ULONG_PTR itemData; } DRAWITEMSTRUCT; 17. OWNERDRAW CtlType 컨트롤의 타입, ODT_BUTTON, ODT_COMBOBOX, ODT_LISTBOX,ODT_LISTVIEW, ODT_MENU, ODT_STATIC CtlID 컨트롤의 ID itemID 메뉴 항목의 ID, 또는 리스트 박스나 콤보 박스의 항목 인덱스 리스트 박스나 콤보 박스가 비어 있을 때는 -1이 전달된다. itemAction 어떤 처리가 필요한지를 지정한다. ODA_DRAWENTIRE(전체 항 목을 다 그려야 한다.), ODA_FOCUS(포커스를 그려야 한다.), ODA_SELECT(선택이 변경되었다.)의 값을 가지게 된다. itemState 항목의 현재 상태를 나타낸다. hwndItem 컨트롤의 윈도우 핸들이다. hDC 그리기에 사용할 DC이다. rcItem 항목이 그려져야 할 사각형이다. itemData 메뉴나 리스트 박스의 각 항목에 기억된 항목 데이터이다. 17. OWNERDRAW ODS_CHECKED 항목이 체크되었다. ( 메뉴에서만 사용 가능 ) ODS_COMBOBOX EDIT 콤보 박스의 에디트에서 그리기가 발생했다. ODS_DEFAULT 디폴트 항목이다. ODS_DISABLE 항목이 디스에이블되었다. ODS_FOCUS 항목이 포커스를 가졌다. ODS_GRAYED 항목이 사용금지되었다. (메뉴에서만 사용 가능) ODS_SELECTED 항목이 선택되었다. 9. 메뉴와 기타 자원 1. 자원 – 아이콘,커서,메뉴,대화상자는 모두 Windows의 자원이다. – 자원은 데이터로 생각할 수 있으며, 프로그램의 EXE파일에 저장 된다. – 실행 가능한 프로그램의 데이터 영역에는 존재하지 않는다. • 자원은 프로그램 소스코드에 정의된 변수를 이용하여 즉시 액세스될 수 없다. • Windows가 자원을 메모리에 올려 사용할 수 있게 하는 함수를 제공 한다. ( LoadIcon, LoadCursor) – 아이콘을 동적으로 변경 • SetClassLong(hwnd,GCL_HICON,LoadIcon(hInstance, MAKEINTRESOURCE(IDI_LTICON))); – 아이콘 표시 • DrawIcon(hdc,x,y,GetClassLong(hwnd,GCL_HICON)); case WM_LBUTTONDOWN : SetClassLong(hwnd,GCL_HICON,(long)LoadIcon(hInstance, MAKEINTRESOURCE(IDI_NEWICON))); return 0; #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { TCHAR szAppName[] = TEXT ("IconDemo") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON)) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Icon Demo"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HICON hIcon ; static int cxIcon, cyIcon, cxClient, cyClient ; HDC hdc ; HINSTANCE hInstance ; PAINTSTRUCT ps ; int x, y ; switch (message) { case WM_CREATE : hInstance = ((LPCREATESTRUCT) lParam)->hInstance ; hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON)) ; cxIcon = GetSystemMetrics (SM_CXICON) ; cyIcon = GetSystemMetrics (SM_CYICON) ; return 0 ; case WM_SIZE : cxClient = LOWORD (lParam) ; cyClient = HIWORD (lParam) ; return 0 ; case WM_PAINT : hdc = BeginPaint (hwnd, &ps) ; for (y = 0 ; y < cyClient ; y += cyIcon) for (x = 0 ; x < cxClient ; x += cxIcon) DrawIcon (hdc, x, y, hIcon) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 2. 사용자 정의 커서 사용하기 – Wndclass.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR)); – SetClassLong(hwndChild,GCL_HCURSOR,LoadCursor(hInstanc e, MAKEINTRESOURCE(IDC_CURSOR)); – SetCursor(hCursor); • 문자열 사용 – LoadString(hInstance,id,szBuffer,iMaxLength); • 프로그램에 메뉴 참조하기 – 사용자가 메뉴 항목을 선택할 때 WM_COMMAND메시지를 보낸다. – Wndclass.lpszMenuName =szAppName; – CreateWindow의 9번째 인자가 NULL인 경우는 윈도우 클래스에 기반을 둔 메뉴를 사용한다. – hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(ID_MENU)); – SetMenu(hwnd,hMenu) • 동적으로 메뉴를 바꿀 수 있다 3. 메뉴와 메시지 – WM_INITMENU • • • • wParam : 메인 메뉴의 핸들 lParam : 0 항목이 선택되기 전에 발생한다. 메뉴를 변경하기 위해서 사용 – WM_MENUSELECT • LOWORD(wParam) : 선택된 항목 – 메뉴 ID나 팝업 메뉴 인덱스 • HIWORD(wPram) : 선택 플래그 – MF_GRAYED,MF_DISABLED,MF_CHECKED,MF_BITMAP,MF_POPUP, MF_HELP,MF_SYSMENU,MF_MOUSESELECT의 조합 • lParam : 선택된 항목을 포함하는 메뉴에 대한 핸들 • 대부분 DefWindowProc에 전달 – Windows는 팝업 메뉴를 표시할 준비가 되었을 때, WM_INITMENUPOPUP메시지를 윈도우 프로시저에 보낸다. • • • • wParam : 팝업 메뉴 핸들 LOWORD(lParam) : 팝업 인덱스 HIWORD(lParam) : 시스템 메뉴 : 1, 그 이외 0 항목을 표시하기 전에 활성, 비활성 상태로 표시하기 위해서 중요하다. – WM_COMMAND • LOWORD(wParam) : 메뉴 ID • HIWORD(wParam) : 0 • lParam : 0 /*----------------------------------------MENUDEMO.C -- Menu Demonstration (c) Charles Petzold, 1998 -----------------------------------------*/ #include <windows.h> #include "resource.h" #define ID_TIMER 1 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; TCHAR szAppName[] = TEXT ("MenuDemo") ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = szAppName ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Menu Demonstration"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static int idColor [5] = { WHITE_BRUSH, LTGRAY_BRUSH, GRAY_BRUSH,DKGRAY_BRUSH, BLACK_BRUSH } ; static int iSelection = IDM_BKGND_WHITE ; HMENU hMenu ; switch (message) { case WM_COMMAND: hMenu = GetMenu (hwnd) ; switch (LOWORD (wParam)) { case IDM_FILE_NEW: case IDM_FILE_OPEN: case IDM_FILE_SAVE: case IDM_FILE_SAVE_AS: MessageBeep (0) ; return 0 ; case IDM_APP_EXIT: SendMessage (hwnd, WM_CLOSE, 0, 0) ; return 0 ; case IDM_EDIT_UNDO: case IDM_EDIT_CUT: case IDM_EDIT_COPY: case IDM_EDIT_PASTE: case IDM_EDIT_CLEAR: MessageBeep (0) ; return 0 ; case IDM_BKGND_WHITE: // Note: Logic below case IDM_BKGND_LTGRAY: // assumes that IDM_WHITE case IDM_BKGND_GRAY: // through IDM_BLACK are case IDM_BKGND_DKGRAY: // consecutive numbers in case IDM_BKGND_BLACK: // the order shown here. CheckMenuItem (hMenu, iSelection, MF_UNCHECKED) ; iSelection = LOWORD (wParam) ; CheckMenuItem (hMenu, iSelection, MF_CHECKED) ; SetClassLong (hwnd, GCL_HBRBACKGROUND, (LONG)GetStockObject(idColor [LOWORD (wParam) - IDM_BKGND_WHITE])) ; InvalidateRect (hwnd, NULL, TRUE) ; return 0 ; case IDM_TIMER_START: if (SetTimer (hwnd, ID_TIMER, 1000, NULL)) { EnableMenuItem (hMenu, IDM_TIMER_START, MF_GRAYED) ; EnableMenuItem (hMenu, IDM_TIMER_STOP, MF_ENABLED) ; } return 0 ; case IDM_TIMER_STOP: KillTimer (hwnd, ID_TIMER) ; EnableMenuItem (hMenu, IDM_TIMER_START, MF_ENABLED) ; EnableMenuItem (hMenu, IDM_TIMER_STOP, MF_GRAYED) ; return 0 ; case IDM_APP_HELP: MessageBox (hwnd, TEXT ("Help not yet implemented!"), szAppName, MB_ICONEXCLAMATION | MB_OK) ; return 0 ; case IDM_APP_ABOUT: MessageBox (hwnd, TEXT ("Menu Demonstration Program\n"), TEXT ("(c) Charles Petzold, 1998"),szAppName, MB_ICONINFORMATION | MB_OK) ; return 0 ; } break ; case WM_TIMER: MessageBeep (0) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } MENUDEMO MENU DISCARDABLE BEGIN POPUP "&File" BEGIN MENUITEM "&New", MENUITEM "&Open", MENUITEM "&Save", MENUITEM "Save &As...", MENUITEM SEPARATOR MENUITEM "E&xit", END POPUP "&Edit" BEGIN MENUITEM "&Undo", MENUITEM SEPARATOR MENUITEM "C&ut", MENUITEM "&Copy", MENUITEM "&Paste", MENUITEM "De&lete", END POPUP "&Background" BEGIN MENUITEM "&White", MENUITEM "&Light Gray", MENUITEM "&Gray", MENUITEM "&Dark Gray", MENUITEM "&Black", END POPUP "&Timer" BEGIN MENUITEM "&Start", MENUITEM "S&top", END POPUP "&Help" BEGIN MENUITEM "&Help...", MENUITEM "&About MenuDemo...", END END ID_MENUITEM40020 IDM_FILE_OPEN IDM_FILE_SAVE IDM_FILE_SAVE_AS IDM_APP_EXIT IDM_EDIT_UNDO IDM_EDIT_CUT IDM_EDIT_COPY IDM_EDIT_PASTE IDM_EDIT_CLEAR IDM_BKGND_WHITE, CHECKED IDM_BKGND_LTGRAY IDM_BKGND_GRAY IDM_BKGND_DKGRAY IDM_BKGND_BLACK IDM_TIMER_START IDM_TIMER_STOP, GRAYED IDM_APP_HELP IDM_APP_ABOUT 3. 메뉴와 메시지 – 메뉴의 핸들 얻기 : hMenu = GetMenu(hwnd); – 체크하지 않기 : • CheckMenuItem(hMenu,iSelection,MF_UNCHECKED); – 체크하기 • iSelection = wParam; • CheckMenuItem(hMenu,iSelection,MF_CHECKED); – Disable & Grayed Menu만들기 • EnableMenuItem(hMenu,IDM_TIMER_START,MF_GRAYED); – Disable Menu만들기 • EnableMenuItem(hMenu,IDM_TIMER_START, MF_DISABLED); – Enable Menu만들기 • EnableMenuItem(hMenu,IDM_TIMER_START, MF_ENABLED); • TrackPopupMenu – WM_CREATE: • hMenu = LoadMenu(hInst,szAppName); • hMenu = GetSubMenu(hMenu,0); – WM_RBUTTON: • • • • Point.x = LOWORD(lParam); Point.y = HIWORD(lParam); ClientToScreen(hwnd,&Point); TrackPopupMenu(hMenu,TPM_RIGHTBUTTON,Point.x,Point.y,0,h wnd,NULL); 4. 시스템 메뉴 사용하기 – 시스템 메뉴의 핸들을 얻는다. • hMenu = GetSystemMenu(hwnd,FALSE); • FALSE : 시스템 메뉴를 수정 • TRUE : 추가된 항목을 제거 – AppendMenu • 메뉴의 끝에 새 항목을 추가한다. – DeleteMenu • 메뉴에서 기존의 항목을 떼어 내고 완전히 없앤다. – InsertMenu • 메뉴에 새 항목을 집어 넣는다. – ModifyMenu • 기존의 메뉴항목을 수정한다. – RemoveMenu • 메뉴에서 기존의 항목을 떼어낸다. – DrawMenuBar(hwnd) • 상위 메뉴 항목을 변경했을 때, 그 변화는 Windows가 메뉴를 그릴 때 까지 나타나지 않는다. 다시 메뉴를 그리는 명령 – hMenuPopup = GetSubMenu(hMenu,iPosition); • 팝업 메뉴에 대한 핸들을 리턴 5. 기타 메뉴 명령 – 상위나 팝업 메뉴에 있는 항목의 수를 리턴 • iCount = GetMenuItem(hMenu); – 팝업 메뉴에 있는 항목의 메뉴ID를 리턴 • Id = GetMenuID(hMenuPopup,iPosition); – 체크 설정 및 취소 • CheckMenuItem(hMenu,id,iCheck); • Id를 Position으로 사용하려면 – CheckMenuItem(hMenu,iPosition,MF_CHECKED|MF_BYPOSITION); – iCharCount = GetMenuString(hMneu,id,pString, iMaxCount, iFlag); • iFlag : – MF_BYCOMMAND : id가 메뉴 ID – MF_BYPOSITION : id가 인덱스 위치 – iFlags = GetMenuState(hMenu,id,iFlag) • iFlag : – MF_BYCOMMAND : id가 메뉴 ID – MF_BYPOSITION : id가 인덱스 위치 • 리턴 값 : – MF_DISABLED, MF_GRAYED, MF_CHECKED, MF_MENUBREAK, MF_MENUBARBREAK, MF_SEPARATOR – SetMenu(hwnd,hMenuMain); • 메뉴를 변경한다. 6. 키보드 액셀러레이터 – WM_COMMAND메시지를 발생시키는 키의 조합이다. – 액셀러레이터 테이블 로드 HANDLE hAccel; hAccel = LoadAccelerators(hInstance,TEXT("MyAccelerators")); while(GetMessage(&msg,NULL,0,0)) { if (!TranslateAccelerator(hwnd,hAccel,&msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } /*----------------------------------------------------POPPAD2.C -- Popup Editor Version 2 (includes menu) (c) Charles Petzold, 1998 -----------------------------------------------------*/ #include <windows.h> #include "resource.h" #define ID_EDIT 1 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); TCHAR szAppName[] = TEXT ("PopPad2") ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { HACCEL hAccel ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (hInstance, szAppName) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = szAppName ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, szAppName,WS_OVERLAPPEDWINDOW,GetSystemMetrics (SM_CXSCREEN) / 4, GetSystemMetrics (SM_CYSCREEN) / 4, GetSystemMetrics (SM_CXSCREEN) / 2, GetSystemMetrics (SM_CYSCREEN) / 2, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; hAccel = LoadAccelerators (hInstance, szAppName) ; while (GetMessage (&msg, NULL, 0, 0)) { if (!TranslateAccelerator (hwnd, hAccel, &msg)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } } return msg.wParam ; AskConfirmation (HWND hwnd) { return MessageBox (hwnd, TEXT ("Really want to close PopPad2?"),szAppName, MB_YESNO | MB_ICONQUESTION) ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hwndEdit ; int iSelect, iEnable ; switch (message) { case WM_CREATE: hwndEdit = CreateWindow (TEXT ("edit"), NULL,WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0, 0, 0, 0, hwnd, (HMENU) ID_EDIT,((LPCREATESTRUCT) lParam)->hInstance, NULL) ; return 0 ; case WM_SETFOCUS: SetFocus (hwndEdit) ; return 0 ; case WM_SIZE: MoveWindow (hwndEdit, 0, 0, LOWORD (lParam), HIWORD (lParam), TRUE) ; return 0 ; case WM_INITMENUPOPUP: if (lParam == 1) { EnableMenuItem ((HMENU) wParam, IDM_EDIT_UNDO,SendMessage (hwndEdit, EM_CANUNDO, 0, 0) MF_ENABLED : MF_GRAYED) ; EnableMenuItem ((HMENU) wParam, IDM_EDIT_PASTE,IsClipboardFormatAvailable (CF_TEXT) ? MF_ENABLED : MF_GRAYED) ; iSelect = SendMessage (hwndEdit, EM_GETSEL, 0, 0) ; if (HIWORD (iSelect) == LOWORD (iSelect)) iEnable = MF_GRAYED ; else iEnable = MF_ENABLED ; EnableMenuItem ((HMENU) wParam, IDM_EDIT_CUT, iEnable) ; EnableMenuItem ((HMENU) wParam, IDM_EDIT_COPY, iEnable) ; EnableMenuItem ((HMENU) wParam, IDM_EDIT_CLEAR, iEnable) ; return 0 ; } break ; case WM_COMMAND: if (lParam) { if (LOWORD (lParam) == ID_EDIT && (HIWORD (wParam) == EN_ERRSPACE || HIWORD (wParam) == EN_MAXTEXT)) MessageBox (hwnd, TEXT ("Edit control out of space."),szAppName, MB_OK | MB_ICONSTOP) ; return 0 ; } else switch (LOWORD (wParam)) { case IDM_FILE_NEW: case IDM_FILE_OPEN: case IDM_FILE_SAVE: case IDM_FILE_SAVE_AS: case IDM_FILE_PRINT: MessageBeep (0) ; return 0 ; case IDM_APP_EXIT: SendMessage (hwnd, WM_CLOSE, 0, 0) ; return 0 ; case IDM_EDIT_UNDO: SendMessage (hwndEdit, WM_UNDO, 0, 0) ; return 0 ; case IDM_EDIT_CUT: SendMessage (hwndEdit, WM_CUT, 0, 0) ; return 0 ; case IDM_EDIT_COPY: SendMessage (hwndEdit, WM_COPY, 0, 0) ; return 0 ; case IDM_EDIT_PASTE: SendMessage (hwndEdit, WM_PASTE, 0, 0) ; return 0 ; case IDM_EDIT_CLEAR: SendMessage (hwndEdit, WM_CLEAR, 0, 0) ; return 0 ; case IDM_EDIT_SELECT_ALL: SendMessage (hwndEdit, EM_SETSEL, 0, -1) ; return 0 ; case IDM_HELP_HELP: MessageBox (hwnd, TEXT ("Help not yet implemented!"),szAppName, MB_OK | MB_ICONEXCLAMATION) ; return 0 ; case IDM_APP_ABOUT: MessageBox (hwnd, TEXT ("POPPAD2 (c) Charles Petzold, 1998"),szAppName, MB_OK | MB_ICONINFORMATION return 0 ; } case WM_CLOSE: if (IDYES == AskConfirmation (hwnd)) DestroyWindow (hwnd) ; return 0 ; case WM_QUERYENDSESSION: if (IDYES == AskConfirmation (hwnd)) return 1 ; else return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 10. 대화상자 1. 다이얼로그 박스 • 대화상자 – 다양한 자식 윈도우 컨트롤을 가진 팝업 윈도우 형식을 갖는다. – 메시지는 대화상자 프로시저에서 처리된다. – 대화상자 프로시저 • • • • • 대화상자가 만들어질 때 자식 윈도우 컨트롤을 초기화 자식 윈도우 컨트롤로부터의 메시지를 처리 대화상자를 종료. 키보드와 마우스 입력도 처리하지 않는다. WM_PAINT도 처리하지 않는다. • 모달 – – – – 사용자가 대화상자와 또 다른 윈도우 사이를 전환할 수 없다. 가장 일반적 확인이나 취소 버튼을 이용하여 대화상자를 종료 DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1),hwn d,DlgProc); • • • • hInstance : Instance의 Handle MAKEINTRESOURCE(IDD_DIALOG1) : 대화상자의 이름 hwnd : 대화상자의 부모 DlgProc : 대화상자 프로시저 함수 1. 다이얼로그 박스 • 파라미터 전달 – INT_PTR DialogBoxParam( HINSTANCE hInstance, LPCTSTR lPTemplateName, HWND hWndParent, DLGPROC lpDialogFunc, LPARAM dwInitParam); • 함수의 원형은 DialogBox와 거의 유사하되 제일 뒤에 dwInitParam이 라는 인수가 하나 더 추가되었다. • 이 값은 대화상자를 생성할 때 WM_INITDIALOG의 lParam으로 전달 된다. • 아이들 메시지 – 모달 대화상자가 떠 있는 동안 부모 윈도우는 사용 금지되어 아무 런 입력도 받지 못한다. – 사용 금지된 윈도우도 메시지를 처리할 수 있는데 백그라운드 작 업이 필요하다면 WM_ENTERIDLE메시지를 사용할 수 있다. – 이 메시지는 모달 대화상자나 메뉴 윈도우가 떠 잇는 동안 부모 윈도우에게 주기적으로 보내진다. – 모달 대화상자는 자신에게 전달된 메시지를 처리하다가 더 이상 처리할 메시지가 없을 때, 부모 윈도우에게 이 메시지를 전달한다. /*-----------------------------------------ABOUT1.C -- About Box Demo Program No. 1 (c) Charles Petzold, 1998 ------------------------------------------*/ #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("About1") ; MSG msg ; HWND hwnd ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (hInstance, szAppName) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = szAppName ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("About Box Demo Program"),WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HINSTANCE hInstance ; switch (message) { case WM_CREATE : hInstance = ((LPCREATESTRUCT) lParam)->hInstance ; return 0 ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDM_APP_ABOUT : DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ; break ; } return 0 ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG : return TRUE ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDOK : case IDCANCEL : EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } 1. 다이얼로그 박스 윈도우 프로시저와 대화상자 프로시저 LRESULT를 반환 BOOL을 반환 메시지 처리하지 않으면 DefWindowProc호출 메시지 처리 O : TRUE 메시지 처리 X : FALSE 초기화 : WM_CREATE 초기화 : WM_INITDIALOG /*-----------------------------------------ABOUT3.C -- About Box Demo Program No. 3 (c) Charles Petzold, 1998 ------------------------------------------*/ #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ; LRESULT CALLBACK EllipPushWndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("About3") ; MSG msg ; HWND hwnd ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (hInstance, szAppName) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = szAppName ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = EllipPushWndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = NULL ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = TEXT ("EllipPush") ; RegisterClass (&wndclass) ; hwnd = CreateWindow (szAppName, TEXT ("About Box Demo Program"),WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HINSTANCE hInstance ; switch (message) { case WM_CREATE : hInstance = ((LPCREATESTRUCT) lParam)->hInstance ; return 0 ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDM_APP_ABOUT : DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ; return 0 ; } break ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG : return TRUE ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDOK : EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; LRESULT CALLBACK EllipPushWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { TCHAR szText[40] ; HBRUSH hBrush ; HDC hdc ; PAINTSTRUCT ps ; RECT rect ; switch (message) { case WM_PAINT : GetClientRect (hwnd, &rect) ; GetWindowText (hwnd, szText, sizeof (szText)) ; hdc = BeginPaint (hwnd, &ps) ; hBrush = CreateSolidBrush (GetSysColor (COLOR_WINDOW)) ; hBrush = (HBRUSH) SelectObject (hdc, hBrush) ; SetBkColor (hdc, GetSysColor (COLOR_WINDOW)) ; SetTextColor (hdc, GetSysColor (COLOR_WINDOWTEXT)) ; Ellipse (hdc, rect.left, rect.top, rect.right, rect.bottom) ; DrawText (hdc, szText, -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; DeleteObject (SelectObject (hdc, hBrush)) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_KEYUP : if (wParam != VK_SPACE) break ; // fall through case WM_LBUTTONUP : SendMessage (GetParent (hwnd), WM_COMMAND,GetWindowLong (hwnd, GWL_ID), (LPARAM) hwnd) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 2. 모달리스 대화상자 – – – – DialogBox() CreateDialog() 종료후 리턴 바로 리턴 Visible 속성을 Check하여야 한다. 메시지는 프로그램의 메시지 큐를 통해서 들어온다. 메시지 루프 루틴을 변경해야 한다. IsDialogMessage(hDlgModeless, & msg) • 모달리스 메시지이면 대화상자 윈도우 프로시저에 메시지를 전달, Return TRUE – EndDialog대신 DestroyWindow를 사용. /*-----------------------------------------------COLORS2.C -- Version using Modeless Dialog Box (c) Charles Petzold, 1998 ------------------------------------------------*/ #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; BOOL CALLBACK ColorScrDlg (HWND, UINT, WPARAM, LPARAM) ; HWND hDlgModeless ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("Colors2") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = CreateSolidBrush (0L) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Color Scroll"),WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; hDlgModeless = CreateDialog (hInstance, TEXT ("ColorScrDlg"), hwnd, ColorScrDlg) ; while (GetMessage (&msg, NULL, 0, 0)) { if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_DESTROY : DeleteObject ((HGDIOBJ) SetClassLong (hwnd, GCL_HBRBACKGROUND,(LONG) GetStockObject (WHITE_BRUSH))); PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } BOOL CALLBACK ColorScrDlg (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static int iColor[3] ; HWND hwndParent, hCtrl ; int iCtrlID, iIndex ; switch (message) { case WM_INITDIALOG : for (iCtrlID = 10 ; iCtrlID < 13 ; iCtrlID++) { hCtrl = GetDlgItem (hDlg, iCtrlID) ; SetScrollRange (hCtrl, SB_CTL, 0, 255, FALSE) ; SetScrollPos (hCtrl, SB_CTL, 0, FALSE) ; } return TRUE ; case WM_VSCROLL : hCtrl = (HWND) lParam ; iCtrlID = GetWindowLong (hCtrl, GWL_ID) ; iIndex = iCtrlID - 10 ; hwndParent = GetParent (hDlg) ; switch (LOWORD (wParam)) { case SB_PAGEDOWN : iColor[iIndex] += 15 ; // fall through case SB_LINEDOWN : iColor[iIndex] = min (255, iColor[iIndex] + 1) ; break ; case SB_PAGEUP : iColor[iIndex] -= 15 ; // fall through case SB_LINEUP : iColor[iIndex] = max (0, iColor[iIndex] - 1) ; break ; case SB_TOP : iColor[iIndex] = 0 ; break ; case SB_BOTTOM : iColor[iIndex] = 255 ; break ; case SB_THUMBPOSITION : case SB_THUMBTRACK : iColor[iIndex] = HIWORD (wParam) ; break ; default : return FALSE ; } SetScrollPos (hCtrl, SB_CTL, iColor[iIndex], TRUE) ; SetDlgItemInt (hDlg, iCtrlID + 3, iColor[iIndex], FALSE) ; DeleteObject ((HGDIOBJ) SetClassLong (hwndParent, GCL_HBRBACKGROUND, (LONG) CreateSolidBrush (RGB (iColor[0], iColor[1], iColor[2])))) ; InvalidateRect (hwndParent, NULL, TRUE) ; return TRUE ; } return FALSE ; } BOOL GetOpenFileName( LPOPENFILENAME ); typedef struct tagOFN { DWORD lStructSize; HWND hwndOwner; HINSTANCE hInstance; LPCTSTR lpstrFilter; LPTSTR lpstrCustomFilter; DWORD nMaxCustFilter; DWORD nFilterIndex; LPTSTR lpstrFile; DWORD nMaxFile; LPTSTR lpstrFileTitle; DWORD nMaxFileTitle; LPCTSTR lpstrInitialDir; LPCTSTR lpstrTitle; DWORD Flags; WORD nFileOffset; WORD nFileExtension; LPCTSTR lpstrDefExt; DWORD lCustData; LPOFNHOOKPROC lpfnHook; LPCTSTR lpTemplateName; } OPENFILENAME; 3. 공용대화상자 • FileOpen, FileSave Dialog – BOOL GetOpenFileName( LPOPENFILENAME lpofn ); – BOOL GetSaveFileName( LPOPENFILENAME lpofn ); – OPENFILENAME • lStructSize : OPENFILENAME 구조체의 크기 • hwndOwner : 대화상자의 소유자를 지정 • hInstance : 별도의 대화상자 템플릿을 사용할 경우 리소스를 가진 인스턴스 핸들을 지정 • lpstrFilter : 파일 형식 콤보 박스에 나타낼 필터들이며 널 문자를 기 준으로 “파일형식\0필터”와 같이 기술한다. • nFilterIndex : 파일 형식 콤보 박스에서 사용할 필터의 인덱스를 지 정한다. 1이면 첫 번째 Filter, 2면 두 번째 필터 • lpstrFile : 파일 이름 에디트에 처음 나타낼 파일명을 지정. 사용자 가 최종적으로 선택한 파일의 이름(완전경로)을 리턴하는 용도로도 사용된다. • nMaxFile : lpstrFile멤버의 길이. 최소 256 • lpstrFileTitle : 파일의 이름을 돌려받기 위한 버퍼를 제공 (이름만 리턴) 3. 공용대화상자 • • • • lpstrInitialDir : 파일 찾기를 시작할 디렉토리를 지정한다. lpstrTitle : 대화상자의 캡션을 지정한다. Flags : 대화상자의 모양과 동작을 지정하는 옵션. nFileOffset : lpstrFile버퍼 내의 파일명 오프셋을 리턴한다. lpstrFile버퍼에서 이 오프셋만큼 더하면 경로명을 제외한 파일명만 얻을 수 있다. – Flags • OFN_ALLOWMULTISELECT : 복수 개의 파일을 선택할 수 있도록 한다. 이 플래그를 OFN_EXPLORER플래그 없이 사용하면 구형 대 화상자가 열린다. • OFN_ENABLESIZING : 크기 조정이 가능하도록 한다. • OFN_FILEMUSTEXIST : 사용자는 존재하는 파일만 입력해 넣을 수 있으며 없는 파일을 입력한 경우 경고 메시지를 보여준다. • OFN_FORCESHOWHIDDEN : 시스템 파일과 숨겨진 파일을 보여준 다. LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: return 0; case WM_LBUTTONDOWN: { char temp[256]; strcpy(temp,"123.txt"); OPENFILENAME ofn; ZeroMemory(&ofn,sizeof(ofn)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hwnd; ofn.lpstrFilter = "모든 파일(*.*)\0*.*\0텍스트 파일(*.txt)\0*.txt\0\0\0"; ofn.lpstrFile = temp; ofn.nFilterIndex = 2; ofn.nMaxFile = 256; ofn.Flags = OFN_EXPLORER | OFN_ALLOWMULTISELECT | OFN_ENABLESIZING ; if (GetOpenFileName(&ofn) != NULL) { MessageBox(hwnd,temp,"파일선택",MB_OK); } } return 0; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 3. 공용대화상자 – 복수 개의 파일 선택 • 폴더 경로 \0 첫 번째 파일명 \0 두 번째 파일명 \0\0 3. 공용대화상자 BOOL ChooseColor( LPCHOOSECOLOR ); typedef struct { DWORD lStructSize; HWND hwndOwner; HWND hInstance; COLORREF rgbResult; COLORREF * lpCustColors; DWORD Flags; LPARAM lCustData; LPCCHOOKPROC lpfnHook; LPCTSTR lpTemplateName; } CHOOSECOLOR; rgbResult specifies the user's color selection lpCustColors Pointer to an array of 16 COLORREF values that contain red, green, blue (RGB) values for the custom color boxes in the dialog box 3. 공용대화상자 – Flags • • • • CC_ANYCOLOR : 기본 색상중 모든 색상을 보여주도록 한다. CC_FULLOPEN : 대화상자를 확장형으로 보여준다. CC_PREVENTFULLOPEN : 확장형으로 열리지 못하도록 한다. CC_SOLDCOLOR : 디더링되지 않은 색상만 보여준다. 3. 공용대화상자 static COLORREF s_CustColors[16]; static COLORREF s_color = RGB(255, 255, 255); for (i = 0; i < 16; i++) { s_CustColors[i] = RGB(255, 255, 255); } cc.lStructSize = sizeof(CHOOSECOLOR); cc.hwndOwner = hwnd; cc.hInstance = NULL; cc.rgbResult = s_color; cc.lpCustColors = s_CustColors; cc.Flags = CC_RGBINIT; cc.lCustData = 0; cc.lpfnHook = NULL; cc.lpTemplateName = NULL; if (ChooseColor(&cc)) { s_color = cc.rgbResult; InvalidateRect(hwnd, NULL, TRUE); } 3. 공용대화상자 – BOOL ChooseFont( LPCHOOSEFONT lpcf ); typedef struct { DWORD lStructSize; HWND hwndOwner; HDC hDC; LPLOGFONT lpLogFont; INT iPointSize; DWORD Flags; COLORREF rgbColors; LPARAM lCustData; LPCFHOOKPROC lpfnHook; LPCTSTR lpTemplateName; HINSTANCE hInstance; LPTSTR lpszStyle; WORD nFontType; INT nSizeMin; INT nSizeMax; } CHOOSEFONT, *LPCHOOSEFONT; 3. 공용대화상자 lpStructSize 구조체의 크기 hwndOwner 대화상자의 소유자를 지정한다. hDC Flags에 CF_PRINTERFONTS가 설정되어 있는 경우에 프린터 DC를 지정한다. lpLogFont 사용자가 선택한 글꼴 정보를 리턴 할 LOGFONT 구조체 CF_INITTOLOGFONTSTRUCT 플래그가 설정되어 있으면 이 구조체의 정보대로 폰트 선택 대화상자가 초기화 된다. iPointSize 선택한 폰트의 포인트 크기를 1/10포인트 단위로 리턴 Flags 대화상자 초기화와 사용자의 선택값을 리턴하는 플래그들이다. rgbColors CF_EFFECTS플래그가 설정된 경우 사용자가 선택한 글꼴 색 상을 리턴한다. lpszStyle 글꼴 스타일 콤보 박스 초기화에 사용될 스타일 값이며 사용자 가 선택한 스타일을 리턴한다. 3. 공용대화상자 CF_APPLY 적용 버튼을 표시하도록 한다. 이 플래그를 사용할 경우 APPLY버튼을 누를 때 보내지는 WM_COMMAND메시지를 처 리할 훅 프로시저를 제공해 주어야 한다. CF_SCREENF 시스템에 설치된 화면 폰트만 보여준다. ONTS CF_PRINTER FONTS hDC 멤버가 지정한 프린터의 폰트만 보여준다. CF_EFFECTS 관통선, 밑줄 등의 글꼴 스타일과 색상을 선택할 수 있도록 한 다. 11. 공통 컨트롤 1. 공통 컨트롤의 초기화 – 공통 컨트롤은 DLL에 의해 제공되는 것이기 때문에 이 DLL이 로 드되어 있지 않으면 윈도우 클래스가 정의되지 않으며 컨트롤도 생성할 수 없다. • 1. CommCtrl.h를 반드시 포함시킨다. – 공통 컨트롤에 대한 윈도우 클래스명, 스타일, 메시지 등에 대한 매크로 상수가 정의되어 있다. • 2. 프로그램 선두에 InitCommonControls()함수를 호출해 주어야 한다. – 이 함수는 공통 컨트롤을 제공하는 DLL인 COMCTL32.DLL이 제대로 로드되었는지를 체크해 주고 만약 로드 되어 있지 않으면 로드한다. • 3. ComCtl32.lib를 링크 시킨다. • BOOL InitCommonControlsEx( LPINITCOMMONCONTROLSEX lpInitCtrls); typedef struct tagINITCOMMONCONTROLSEX { DWORD dwSize; DWORD dwICC; } INITCOMMONCONTROLSEX, *LPINITCOMMONCONTROLSEX; 1. 공통 컨트롤의 초기화 플래그 컨트롤 ICC_ANIMATE_CLASS 애니메이트 컨트롤 ICC_BAR_CLASSES 툴바, 상태바, 트랙바, 툴팁 ICC_COLL_CLASSES 리바 ICC_DATE_CLASSES 날짜 선택 컨트롤 ICC_INTERNET_CLASSES IP 컨트롤 ICC_LISTVIEW_CLASSES 리스트 뷰, 헤더 컨트롤 ICC_PAGESCROLLER_CLASS 페이지컨트롤 ICC_PROGRESS_CLASS 프로그래스 바 ICC_TAB_CLASSES 탭, 툴팁 컨트롤 ICC_TREEVIEW_CLASSES 트리 뷰, 툴팁 컨트롤 ICC_UPDOWN_CLASS 업 다운 컨트롤 ICC_USEREX_CLASSES 확장 콤보 박스 ICC_WIN95_CLASSES 애니메이트, 헤더, 핫키, 리스트 뷰, 프로그 래스, 상태란, 탭, 툴 팁, 툴 바, 트랙 바, 업 다운 컨트롤 2. Progress 컨트롤 – 윈도우 클래스 : PROGRESS_CLASS – 1. PBM_SETRANGE메시지로 범위를 설정한다. – 2. PBM_SETPOS메시지로 위치 값을 설정한다. 메시지 설명 PBM_SETRANGE 범위를 설정한다. wParam은 사용하지 않는다. LOWORD(lParam)으로 최소값, HIWORD(lParam)으로 최 대값을 주면 된다. 범위는 0 – 100 PBM_SETPOS wParam으로 위치를 설정한다. PBM_DELTAPOS wParam이 지정하는 만큼 위치를 증감시킨다. 새 위치는 현재 위치 + wParam이 된다. PBM_SETSTEP 단계 증가값을 wParam으로 지정한다. 이 값은 PBM_STEPIP메시지에서 사용한다. PBM_STEPIT 단계 증가 값만큼 위치를 이동시킨다. PBM_SETRANGE 32 wParam으로 최소값, lParam으로 최대값을 지정한다. 65535이상의 범위를 가지는 프로그래스를 만들 때 사용 한다. 2. Progress 컨트롤 메시지 설명 PBM_GETRANGE 현재 설정된 범위를 얻는다. PBM_GETPOS 현재 설정된 위치를 얻는다. BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hProgress; switch (message) { case WM_INITDIALOG : hProgress = GetDlgItem(hDlg,IDC_PROGRESS1); SendMessage(hProgress,PBM_SETRANGE,0,MAKELPARAM(0,100)); SendMessage(hProgress,PBM_SETPOS,0,0); return TRUE ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_BUTTON1: { for(int i = 0;i <= 100;i++) { SendMessage(hProgress,PBM_SETPOS,i,0); Sleep(50); } } return TRUE; case IDOK : case IDCANCEL : EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } 3. 트랙 바 TBS_HORZ 수평 트랙 바를 만든다 TBS_VERT 수직 트랙 바를 만든다. TBS_AUTOTICKS 범위내의 모든 값에 틱 마크를 배치한다. TBS_NOTICKS 틱 마크를 출력하지 않는다. TBS_BOTTOM 수평일 경우 바닥에 틱 마크를 출력한다. TBS_TOP 수평일 경우 위쪽에 틱 마크를 출력한다. TBS_RIGHT 수직일 경우 오른쪽에 틱 마크를 출력한다. TBS_LEFT 수직일 경우 왼쪽에 틱 마크를 출력한다. TBS_BOTH 틱 마크를 양쪽에 출력한다. TBS_ENABLESEL RANGE 선택 영역을 표시한다. 이 스타일이 지정되었을 경우 선택 영역의 시작점과 끝점의 틱은 삼각형 모양이 되며 선택된 영역이 밝게 강조된다. TBS_FIXEDLENG TH 썸의 길이를 변경할 수 있도록 한다. TBS_NOTHUMB 썸을 가지지 않는다. TBS_TOOLTIPS 툴 팁을 사용한다. 3. 트랙 바 TBM_GETPOS 트랙바의 현재 위치를 얻는다. TBM_GETPOSMIN 범위의 최소값을 얻는다. TBM_GETPOSMAX 범위의 최대값을 얻는다. TBM_GETTIC wParam으로 지정한 인덱스의 틱 위치를 얻는다. TBM_SETTIC lParam으로 지정한 인덱스에 틱 마크를 배치한다. TBM_SETPOS lParam으로 트랙바의 위치를 변경한다.wParam이 TRUE이면 위치 변경 후 트랙바를 다시 그리고, FALSE 이면 다시 그리지 않는다. TBM_SETRANGE 트랙바의 범위를 설정한다. LOWORD(lParam)에 최소 값, HIWORD(lParam)에 최대값을 준다. TBM_CLEARTICS 틱 마크를 제거한다. – 사용자가 트랙 바를 조작하면 WM_HSCROLL, WM_VSCROLL 메 시지를 부모 윈도우로 보내며 LOWORD(wParam)으로 통지 메시 지를 보낸다. 3. 트랙 바 TB_LINEUP 라인 증가 TB_LINEDOWN 라인 감소 TB_PAGEUP 페이지 증가 TB_PAGEDOWN 페이지 감소 TB_THUMBPOSITON 드래그 종료 TB_THUMBTRACK 드래그 중 TB_TOP 최상위 위치로 이동 TB_BOTTOM 최하위 위치로 이동 TB_ENDTRACK 조작 종료 BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hProgress; static HWND hEdit; static HWND hTrackBar; switch (message) { case WM_INITDIALOG : hProgress = GetDlgItem(hDlg,IDC_PROGRESS1); SendMessage(hProgress,PBM_SETRANGE,0,MAKELPARAM(0,100)); SendMessage(hProgress,PBM_SETPOS,0,0); hTrackBar = GetDlgItem(hDlg,IDC_SLIDER1); SendMessage(hTrackBar,TBM_SETRANGE,0,MAKELPARAM(0,100)); SendMessage(hTrackBar,TBM_SETPOS,0,0); hEdit = GetDlgItem(hDlg,IDC_EDIT1); return TRUE ; case WM_HSCROLL: { int iReturnCtrl = GetDlgCtrlID((HWND)lParam); switch (iReturnCtrl) { case IDC_SLIDER1: { int iPos; iPos = SendMessage(hTrackBar,TBM_GETPOS,NULL,NULL); char temp[256]; wsprintf(temp,"%d",iPos); SetWindowText(hEdit,temp); } break; } } return TRUE; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_BUTTON1: { for(int i = 0;i <= 100;i++) { SendMessage(hProgress,PBM_SETPOS,i,0); Sleep(50); } } return TRUE; case IDOK : case IDCANCEL : EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } 4. 업다운 UDS_HORZ 업 다운 버튼을 위아래로 배치한다. UDS_WRAP 1 – 100까지 일 때 100에서 1증가하면 0이된다. 1에서 1감소하면 100이 된다. UDS_ARROWKEYS 커서 티를 사용하여 위치 값을 변경할 수 있도록 한다. UDS_SETBUDDYINT 업다움 컨트롤의 값이 변경되면 버디 윈도우의 텍스트 를 변경하도록 한다. 값이 변경될 때마다 위치 값을 문 자열로 바꾼 후 버디 윈도우로 WM_SETTEXT메시지를 보내준다. UDS_NOTHOUSANDS 매 세자리 마다 콤마를 삽입해 주는 것을 막는다. UDS_AUTOBUDDY 이 컨트롤보다 Z순서가 바로 앞인 컨트롤을 자동으로 버디 윈도우로 지정한다. UDS_ALIGNRIGHT 버디 윈도우의 오른쪽에 배치한다. UDS_ALIGNLIGHT 버디 윈도우의 왼쪽에 배치한다. 4. 업다운 UDM_SETRANGE 범위를 지정한다. LOWORD(lParam)에 최대 값을 HIWORD(lParam)에 최소값을 지정한다. 디폴트 범위는 최소값 100, 최대값 0이며 범위가 반대 로 되어 있다. UDM_SETPOS LOWORD(lParam)으로 위치를 설정한다. UDM_SETBUDDY 버디 윈도우를 설정한다. wParam에 버디로 지정할 윈 도우 핸들을 넘겨준다. UDM_SETACCELL 업다운 컨트롤에 가속 기능을 부여한다. lParam에 UDACCEL구조체 배열의 번지를 넘겨주고 wParam에 배열의 크기를 넘겨준다. typedef struct { UINT nSec; UINT nInc; } UDACCEL, *LPUDACCEL; nSec : 가속할 초 단위 시간 nlnc : nSec초 이후에 적용될 가속값이다. 5초 후에는 10씩 증가, 10초 후에는 20씩 증가 BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hProgress; static HWND hEdit,hEdit2; static HWND hTrackBar; static HWND hSpin; switch (message) { case WM_INITDIALOG : { hProgress = GetDlgItem(hDlg,IDC_PROGRESS1); SendMessage(hProgress,PBM_SETRANGE,0,MAKELPARAM(0,100)); SendMessage(hProgress,PBM_SETPOS,0,0); hTrackBar = GetDlgItem(hDlg,IDC_SLIDER1); SendMessage(hTrackBar,TBM_SETRANGE,0,MAKELPARAM(0,100)); SendMessage(hTrackBar,TBM_SETPOS,0,0); hEdit = GetDlgItem(hDlg,IDC_EDIT1); hEdit2 = GetDlgItem(hDlg,IDC_EDIT2); hSpin = GetDlgItem(hDlg,IDC_SPIN1); SendMessage(hSpin,UDM_SETRANGE,0,MAKELPARAM(1000,0)); SendMessage(hSpin,UDM_SETBUDDY,(WPARAM)hEdit2,NULL); static UDACCEL udAccel[3] = {{0,1},{5,10},{8,20}}; SendMessage(hSpin,UDM_SETACCEL,(WPARAM)3,(LPARAM)udAccel); } return TRUE ; case WM_HSCROLL: { int iReturnCtrl = GetDlgCtrlID((HWND)lParam); switch (iReturnCtrl) { case IDC_SLIDER1: { int iPos; iPos = SendMessage(hTrackBar,TBM_GETPOS,NULL,NULL); char temp[256]; wsprintf(temp,"%d",iPos); SetWindowText(hEdit,temp); } break; } } return TRUE; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_BUTTON1: { for(int i = 0;i <= 100;i++) { SendMessage(hProgress,PBM_SETPOS,i,0); Sleep(50); } } return TRUE; case IDOK : case IDCANCEL : EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } 5. 애니메이트 – 다음에 맞는 AVI파일만 재생할 수 있다. • 비디오 스트림이 꼭 하나만 있어야 한다. • 사운드 스트림은 없거나 꼭 하나만 있어야 한다. • 압축이 되어 있지 않거나 압축되어 있어도 RLE8포맷이어야 한다. ACS_CENTER 애니메이트 윈도우의 크기를 유지하며 중앙에서 AVI 파일을 재생한다. 이 스타일이 주어지지 않으면 크기 가 AVI파일 크기에 맞추어 진다. ACS_TRANSPARENT 투명 배경색을 사용하도록 한다. ACS_AUTOPLAY AVI 클립을 열자마자 바로 재생하도록 한다. ACM_OPEN AVI 클립을 연다. lParam에 열고자 하는 AVI파일명을 주거나 아니면 AVI리소스의 ID를 LOWORD(lParam) 에 대입해 주면 된다. ACM_PLAY AVI 클립을 재생한다. wParam으로 재생 횟수를 지정 하되 –이면 무한 반복 재생, LOWORD(lParam)에 재 생 시작 위치를 지정하며, HIWORD(lParam)에 재생 끝 위치를 지정한다. 0, -1 (처음부터 끝까지) ACM_STOP 재생을 중지한다. switch (message) { case WM_INITDIALOG : { hProgress = GetDlgItem(hDlg,IDC_PROGRESS1); SendMessage(hProgress,PBM_SETRANGE,0,MAKELPARAM(0,100)); SendMessage(hProgress,PBM_SETPOS,0,0); hTrackBar = GetDlgItem(hDlg,IDC_SLIDER1); SendMessage(hTrackBar,TBM_SETRANGE,0,MAKELPARAM(0,100)); SendMessage(hTrackBar,TBM_SETPOS,0,0); hEdit = GetDlgItem(hDlg,IDC_EDIT1); hEdit2 = GetDlgItem(hDlg,IDC_EDIT2); hSpin = GetDlgItem(hDlg,IDC_SPIN1); SendMessage(hSpin,UDM_SETRANGE,0,MAKELPARAM(1000,0)); SendMessage(hSpin,UDM_SETBUDDY,(WPARAM)hEdit2,NULL); static UDACCEL udAccel[3] = {{0,1},{5,10},{8,20}}; SendMessage(hSpin,UDM_SETACCEL,(WPARAM)3,(LPARAM)udAccel); HWND hAnimate = GetDlgItem(hDlg,IDC_ANIMATE1); SendMessage(hAnimate,ACM_OPEN,NULL,MAKELPARAM(IDR_AVI1,0)); SendMessage(hAnimate,ACM_PLAY,(WPARAM)-1,MAKELPARAM(0,-1)); } return TRUE ; 6. 핫키 – 핫 키는 운영체제가 지원하는 키보드 입력 방법 중 하나이다. – 다른 키 입력에 비해 우선 순위가 높게 매겨져 잇다. – 어떤 작업을 하고 있던 중이라도 핫키 입력이 가능하며 보통 작 업 중단이나 특별한 신호를 보내기 위해 사용된다. – BOOL RegisterHotKey(HWND hWnd, int id, UINT fsModifiers, UINT vk); – BOOL UnregisterHotKey( HWND hWnd, int id); • id : 핫키의 고유 번호 • fsModifiers : MOD_ALT, MOD_CONTROL, MOD_SHIFT • vk : 가상 키코드 – 등록된 핫키가 눌려지면 시스템은 핫키를 등록한 윈도우로 WM_HOTKEY메시지를 보내준다. RegisterHotKey(hDlg,1,MOD_ALT|MOD_CONTROL,'X'); } return TRUE ; case WM_HOTKEY: MessageBox(hDlg,"핫키를 눌렀다.","핫키",MB_OK); return TRUE; 6. 핫키 RegisterHotKey(hDlg,1,MOD_ALT|MOD_CONTROL,'X'); } return TRUE ; case WM_HOTKEY: MessageBox(hDlg,"핫키를 눌렀다.","핫키",MB_OK); return TRUE; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_BUTTON1: { for(int i = 0;i <= 100;i++) { SendMessage(hProgress,PBM_SETPOS,i,0); Sleep(50); } } return TRUE; case IDOK : case IDCANCEL : UnregisterHotKey(hDlg,1); EndDialog (hDlg, 0) ; return TRUE ; } break ; 7. 날짜 선택 컨트롤 DTS_LONGDATEFORMAT 긴 날짜 포맷으로 보여준다. DTS_SHORTDATEFORMAT 짧은 날짜 포맷으로 보여준다. DTS_SHORTDATECENTURY 짧은 날짜로 보여주되 연도는 4자리로 표기한다. FORMAT DTS_TIMEFORMAT 날짜 대신 시간을 보여준다. DTS_APPCANPARSE 사용자가 입력한 값을 분석해 보고 값에 따라 특정한 동작을 할 수 있도록 통지 메시지를 보 내준다. DTS_RIGHTALIGN 달력 컨트롤은 DTP의 왼쪽에 펼쳐지는데 이 스 타일을 지정하면 오른쪽에 펼쳐진다. DTS_SHOWNONE 선택된 날짜가 없음을 지정하는 체크 박스를 포 함한다. DTS_UPDOWN 달력 컨트롤을 사용하지 않고 업다운 컨트롤로 값을 편집하도록 한다. 7. 날짜 선택 컨트롤 – DWORD DateTime_GetSystemtime(HWND hwndDP, LPSYSTEMTIME pSysTime) • 사용자가 선택한 날짜를 조사한다. – BOOL DateTime_SetSystemtime(HWND hwndDT, DWORD flag, LPSYSTEMTIME lpSysTime); • flag : GDT_VALID hDTP = GetDlgItem(hDlg,IDC_DATETIMEPICKER1); } return TRUE ; case WM_HOTKEY: { SYSTEMTIME st; DateTime_GetSystemtime(hDTP,&st); char temp[256]; wsprintf(temp,"%d년 %d월 %d일",st.wYear,st.wMonth,st.wDay); MessageBox(hDlg,temp,"선택한 날짜",MB_OK); } return TRUE; 8. 상태란 – HWND CreateStatusWindow(LONG style, LPCTSTR lpszText, HWND hwndParent, UINT wID); • • • • • 상태란을 만드는 함수이다. style : WS_CHILD | WS_VISIBLE lpszText : 상태란에 나타날 초기 문자열 hwndParent : 부모 윈도우의 핸들 wID : 상태란의 ID이다. SB_GETTEXT wParam으로 지정한 파트의 텍스트를 구한다.lParam으로 텍 스트를 돌려받기 위한 버퍼 번지를 넘겨준다. SB_SETTEXT wParam으로 지정한 파트의 텍스트를 설정한다. lParam으로 문자영을 넘겨준다. SB_GETTEXTLENG wParam으로 지정한 파트의 문자열 길이를 구한다. 리턴값 TH 의 하위 워드를 읽으면 길이를 구할 수 있다. SB_SETPARTS 파트를 설정한다. wParam으로 파트의 개수를 주며, lParam 으로 각 파트의 오른쪽 끝 좌표를 가진 정수형 배열을 준다. SB_GETRECT wParam으로 지정한 파트의 영역을 구한다. hStatus = CreateStatusWindow(WS_CHILD|WS_VISIBLE, "Status Line", hDlg, 0); int PartArray[3] = {100,300,-1}; SendMessage(hStatus,SB_SETPARTS,(WPARAM)3,(LPARAM)PartArray); } return TRUE ; case WM_MOUSEMOVE: { char temp[256]; wsprintf(temp,"x => %d. y => %d",LOWORD(lParam),HIWORD(lParam)); SendMessage(hStatus,SB_SETTEXT,(WPARAM)1,(LPARAM)temp); } return TRUE; 9. 프로퍼티 시트 – int PropertySheet( LPCPROPSHEETHEADER lppsph ); • 프로퍼티 시트를 만든다. typedef struct _PROPSHEETPAGE { DWORD dwSize; DWORD dwFlags; HINSTANCE hInstance; union { LPCSTR pszTemplate; LPCDLGTEMPLATE pResource; }; union { HICON hIcon; LPCSTR pszIcon; }; LPCSTR pszTitle; DLGPROC pfnDlgProc; LPARAM lParam; LPFNPSPCALLBACK pfnCallback; UINT * pcRefParent; #if (_WIN32_IE >= 0x0500) LPCTSTR pszHeaderTitle; LPCTSTR pszHeaderSubTitle; #endif #if (_WIN32_WINNT >= 0x0501) HANDLE hActCtx; #endif } PROPSHEETPAGE, *LPPROPSHEETPAGE; 9. 프로퍼티 시트 dwSize 구조체의 크기 dwFlags 페이지의 속성과 유효한 멤버를 지정 hInstance 대화상자 템플리트, 아이콘, 문자열 리소스를 가진 인스턴스 핸들 pszTemplate 대화상자 템플리트이다. pResource dwFlags에 PSP_DLGINDIRECT플래그가 설정되어 있을 때만 사용된다. hIcon 페이지 탭에 출력될 아이콘의 핸들. dwFlags에 PSP_USEHICON이 없으면 무시된다. pszIcon 페이지 탭에 출력될 아이콘 리소스이며 아이콘의 ID이거나 아 이콘 리소스의 이름 문자열이다. dwFlags에 PSP_USEHICON 이 없으면 무시된다 pszTitle 페이지 탭에 출력할 문자열을 지정한다. dwFlags에 PSP_USETITLE플래그가 있어야먄 이 멤버가 사용되며 그렇지 않을 경우 대화상자 템플릿의 타이틀이 사용된다. pfnDlgProc 대화상자 프로시저의 포인터이며 대화상자의 메시지를 처리한 다. EndDialog를 호출해서는 안 된다. 9. 프로퍼티 시트 PSP_DEFAULT 아무 플래그도 지정하지 않는다. PSP_DLGINDIRECT pResource 멤버가 지정하는 메모리의 대화상자 템플릿 으로 부터 페이지를 만든다. PSP_HASHELP 이 페이지가 활성화될 때 도움말 버튼을 보여준다. PSP_USEHICON hIcon멤버가 지정하는 아이콘을 탭에 출력한다. PSP_USEICONID pszIcon멤버가 지정하는 리소스에서 아이콘을 읽어 탭 에 출력한다. PSP_USETITLE 대화상자 템플리트에 지정된 문자열 대신 pszTitle멤버 의 문자열을 탭 페이지에 출력한다. 9. 프로퍼티 시트 – 시트 구조체 • PROPSHEETPAGE 구조체는 프로퍼티 시트에 포함된 개별 페이지에 대한 구조체이다. typedef struct _PROPSHEETHEADER { DWORD dwSize; DWORD dwFlags; HWND hwndParent; HINSTANCE hInstance; union { HICON hIcon; LPCTSTR pszIcon; }; LPCTSTR pszCaption; UINT nPages; union { UINT nStartPage; LPCTSTR pStartPage; }; union { LPCPROPSHEETPAGE ppsp; HPROPSHEETPAGE *phpage; }; PFNPROPSHEETCALLBACK pfnCallback; #if (_WIN32_IE >= 0x0500) union { HBITMAP hbmWatermark; LPCTSTR pszbmWatermark; }; HPALETTE hplWatermark; union { HBITMAP hbmHeader; LPCSTR pszbmHeader; }; #endif } PROPSHEETHEADER, *LPPROPSHEETHEADER; 9. 프로퍼티 시트 dwSize 이 구조체의 크기 dwFlags 구조체 중 어떤 멤버가 사용될 것인가를 지정한다. hwndParent 부모 윈도우의 핸들 hInstance 아이콘과 문자열 리소스를 가진 인스턴스 핸들 hIcon 아이콘 핸들 pszIcon 아이콘 리소스 pszCaption 프로퍼티 시트의 타이틀 문자열을 지정한다. nPages 프로퍼티 페이지의 개수 nStartPage 프로퍼티 시트가 만들어질 때 처음 나타날 시작 페이지 pStartPage 프로퍼티 시트가 만들어질 때 처음 나타날 시작 페이지 의 이름 ppsp 프로퍼티 시트에 포함될 개별 페이지를 정의한 PROPSHEETPAGE 구조체의 배열 phpage 각 페이지의 핸들을 담은 배열의 포인터. 개별 페이지는 CreatePropertySheetPage함수로 만들 수 있다. 9. 프로퍼티 시트 PSH_DEFAULT 아무 플래그도 지정하지 않는다. PSH_HASHELP 도움말 버튼을 만든다. PSH_MODELLESS 모델리스형의 프로퍼티 시트를 만든다. PSH_NOAPPLYNOW 적용 버튼을 제거한다. PSH_PROPSHEETP AGE ppsp멤버를 사용한다. 즉 이 구조체 배열의 정보를 참 조하여 각 페이지를 생성시킨다. PSH_PROPTITLE 프로퍼티 시트의 타이틀 뒤에 “등록정보”를 덧붙인다. PSH_USEHICON hIcon멤버가 지정하는 아이콘을 사용한다. PSH_USEICONID pszIcon 멤버가 지정하는 리소스의 아이콘을 사용한다. PSH_USESTARTPA GE nStartPage가 지정하는 시작 페이지 대신 pStartPage 멤버가 지정하는 페이지를 사용한다. PSH_WIZARD 마법사 형식의 프로퍼티 시트를 만든다. #include <windows.h> #include "resource.h" #include <commctrl.h> HINSTANCE hInst; BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { hInst = hInstance; DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1),NULL,DlgProc); return 0; } BOOL CALLBACK DialogProc1 (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); BOOL CALLBACK DialogProc2 (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG : return TRUE ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_BUTTON1: { PROPSHEETPAGE psp[2]; PROPSHEETHEADER psh; psp[0].dwSize = sizeof(PROPSHEETPAGE); psp[0].dwFlags = PSP_DEFAULT; psp[0].hInstance = hInst; psp[0].pszTemplate = MAKEINTRESOURCE(IDD_DIALOG2); psp[0].pfnDlgProc=(DLGPROC)DialogProc1; psp[0].lParam = 0; psp[1].dwSize = sizeof(PROPSHEETPAGE); psp[1].dwFlags = PSP_DEFAULT; psp[1].hInstance = hInst; psp[1].pszTemplate = MAKEINTRESOURCE(IDD_DIALOG3); psp[1].pfnDlgProc=(DLGPROC)DialogProc2; psp[1].lParam = 0; psh.dwSize = sizeof(PROPSHEETHEADER); psh.dwFlags = PSH_PROPSHEETPAGE | PSH_NOAPPLYNOW; psh.hwndParent = hDlg; psh.pszCaption = "프로퍼티 페이지"; psh.nPages = 2; psh.nStartPage = 0; psh.ppsp = psp; PropertySheet(&psh); } return TRUE; case IDOK : case IDCANCEL : EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } BOOL CALLBACK DialogProc1 (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { return FALSE ; } BOOL CALLBACK DialogProc2 (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { return FALSE ; } 9. 프로퍼티 시트 PSN_APPLY 적용 버튼을 눌렀다. PSN_HELP 도움말 버튼을 눌렀다. PSN_KILLACTIVE 다른 페이지를 선택하거나 대화상자가 닫히기 직전이 다. PSN_QUERYCANCEL 취소버튼을 눌렀을 때 확인한다. PSN_RESET 취소 버튼을 눌렀다 PSN_SETACTIVE 이 페이지가 활성화되지 직전이다. PSN_WIZBACK BACK 버튼을 눌렀다. PSN_WIZFINISH 종료 버튼을 눌렀다. PSN_WIZNEXT 다음 버튼을 눌렀다. 9. 프로퍼티 시트 – 프로퍼티 시트의 메시지 PSM_ADDPAGE 제일 끝에 페이지를 추가한다. CreatePropertySheetPage함수로 페이지를 만든 후 그 핸들을 lParam으로 전달한다. PSM_APPLY 적용 버튼을 누른 것과 같은 효과를 낸다. PSM_CANCELTOCL OSE 취소 버튼을 사용 금지시키고 확인 버튼의 캡션을 닫기 로 바꾼다. PSM_CHANGED 페이지의 정보가 변경되었음을 알린다.프로퍼티 시트 는 이 메시지를 받으면 적용 버튼을 활성화시킨다. PSM_GETCURRENT PAGEHWND 현재 페이지의 윈도우 핸들을 구해 리턴해 준다. PSM_PRESSBUTTO N wParam으로 지정한 버튼을 누른 것과 같은 효과를 낸 다. wParam으로 전달되는 값은 PSBTN_*인데 *는 APPLYNOW, BACK, CANCEL, FINISH, HELP, NEXT,OK중 하나이다. PSM_SETCURSEL 지정한 페이지를 활성화시킨다. wParam으로 제거할 페이지의 인덱스를, lParam으로 제거할 페이지의 핸들 을 전달해 준다. (둘 중 하나나 둘 다 ) 핸들 우선 10. 이미지 리스트 – HIMAGELIST ImageList_LoadBitmap( HINSTANCE hi, LPCSTR lpbmp, int cx, int cGrow, COLORREF crMask); • 지정한 비트맵 리소스를 읽어와 이미지 리스트를 만들고 그 핸들을 리턴 해 준다. • lpbmp : 비트맵 리소스의 이름 • cx : 각 이미지의 폭 • cGrow : 이미지 리스트는 이미지가 추가될 때마다 메모리를 재할당 하여 추가된 이미지를 저장한다. 한꺼번에 할당할 메모리 양을 지정 한다. • crMask : 투명색으로 사용할 색상을 지정 – HIMAGELIST ImageList_LoadImage( HINSTANCE hi, LPCSTR lpbmp, int cx, int cGrow, COLORREF crMask, UINT uType, UINT uFlags); • 이 함수는 비트맵뿐만 아니라 아이콘과 커서까지도 읽을 수 있다. • uType : IMAGE_BITMAP, IMAGE_CURSOR, IMAGE_ICON • uFlags : – ImageList_Destroy() • 이미지 리스트를 파괴한다. 10. 이미지 리스트 LR_DEFAULTCOLOR 디스플레이의 색상 포맷을 사용한다. LR_LOADDEFAULTSI ZE cx가 0일 경우 아이콘과 커서의 크기를 시스템 메트 릭스 크기로 사용한다. cx가 0이고 이 플래그가 설정 되어 있지 않으면 리소스에 정의된 크기가 사용된다. LR_LOADFROMFILE 파일로부터 이미지를 읽어오며 이때 lpbmp인수는 파 일명을 지정한다. – BOOL ImageList_Draw( HIMAGELIST himl , int i, HDC hdcDst, int y, UINT fStyle); • • • • • • • 이미지 리스트의 이미지를 화면에 출력한다. himl : 이미지 리스트의 핸들 i : 이미지 번호 hdcDst : 출력 대상 DC x,y : 좌표 fStyle : IDL_NORMAL : 배경색상으로 이미지를 출력한다. IDL_TRANSPARENT : 마스크를 사용하여 투명한 이미지를 그린다. 10. 이미지 리스트 – BOOL ImageList_SetOverlayImage( HIMAGELIST himl, int iImage, int iOverlay); • image 번째의 이미지를 iOverlay 번째의 오버레이 이미지로 지정한 다. HIMAGELIST IL = ImageList_LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP1), 32, 1, RGB(0,0,255)); ImageList_SetOverlayImage(IL,1,1); ImageList_Draw(IL,0,hdc, 50,50, ILD_NORMAL); ImageList_Draw(IL,0,hdc, 50,50, ILD_NORMAL|INDEXTOOVERLAYMASK(1)); LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps ; RECT rect ; static HINSTANCE hInst; static HIMAGELIST IL; switch (message) { case WM_CREATE: hInst = ((LPCREATESTRUCT)lParam)->hInstance; IL = ImageList_LoadBitmap(hInst,MAKEINTRESOURCE(IDB_BITMAP1),16,1,RGB(192,192,192)); ImageList_SetOverlayImage(IL,1,1); return 0 ; case WM_LBUTTONDOWN: hdc = GetDC(hwnd); ImageList_Draw(IL,0,hdc,50,50,ILD_NORMAL); ImageList_Draw(IL,0,hdc,100,100,ILD_NORMAL|INDEXTOOVERLAYMASK(1)); ReleaseDC(hwnd,hdc); return 0; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; DrawText (hdc, "Hello, Windows 98!", -1, &rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 11. 리스트 뷰 LVS_ICON 큰 아이콘 모양 LVS_SMALLICON 작은 아이콘 모양 LVS_LIST 리스트 모양 LVS_REPORT 레포트 모양 LVS_AUTOARRANGE 아이콘들이 자동으로 정렬된다 LVS_SINGLESEL 리스트 뷰 컨트롤은 디폴트로 여러 개의 항목을 선 택할 수 있다. LVS_SHAREIAMGELISTS 리스트 뷰 컨트롤은 파괴될 때 자신에게 등록된 이 미지 리스트를 같이 파괴시키며 사용자가 이미지 리 스트를 직접 파괴시켜주지 않아도 된다. 이 스타일 을 지정하면 이미지 리스트를 사용이 끝난 후에 사 용자가 직접 파괴해 주어야 한다. LVS_NOLABELWRAP 문자열이 나타나는데 문자열이 길 경우에는 두 줄로 나누어 출력된다. 이 옵션을 선택하면 문자열이 두 줄로 나타나지 않는다. LV_OWNERDRAWFIXED 리스트 뷰 컨트롤의 부모 윈도우가 항목을 직접 그 리도록 한다.WM_DRAWITEM메시지를 부모 윈도우 로 보낸다. 11. 리스트 뷰 LVM_INSERTCOLUMN 새 컬럼을 추가한다. LV_COLUMN에 추가하고자 하 는 컬럼의 정보를 기입하고 wParam에 컬럼 인덱스, lParam에 LV_COLUMN구조체의 포인터를 전달. LVM_INSERTITEM 새 항목을 추가한다. LV_ITEM 구조체에 추가하고자 하는 항목의 정보를 기입하고 lParam으로 그 포인 터를 전달한다. LVM_SETIMAGELIST 리스트 뷰 컨트롤과 연결될 이미지 리스트를 지정한 다. lParam으로 이미지 리스트의 핸들을 전달. wParam으로 이미지 리스트의 종류를 지정한다. 이미지 리스트의 종류는 LVSIL_NORMAL, LVSIL_SMALL, LVSIL_STATE중 하나이다. LVM_SETITEM 항목의 속성을 변경한다. lParam으로 LV_ITEM 구조 체의 포인터를 전달한다. LVM_DELETEITEM 항목을 삭제한다. wParam에 삭제할 항목의 인덱스 를 전달해 준다. LVM_DELETEALLITEMS 모든 항목을 삭제한다. LVM_GETITEM 항목의 속성을 조사한다. LVM_GETNEXTITEM 조건에 맞는 항목을 조사한다. 11. 리스트 뷰 LVN_BEGINDRAG 왼쪽 마우스 버튼으로 항목을 드래그하기 시작할 때 발생한다. LVN_BEGINLABELEDIT 레이블을 편집할 때 발생한다. LVN_BEGINRDRAG 오른쪽 마우스 버튼으로 항목을 드래그하기 시작할 때 발생한다. LVN_COLUMNCLICK 헤어 컨트롤이 클릭될 때 발생한다. LVN_DELETEALLITEM 리스트 뷰 컨트롤의 모든 항목이 삭제될 때 발생한다. LVN_DELETEITEM 한 항목이 삭제될 때 발생한다. LVN_ENDLABELEDIT 레이블 편집이 완료되었을 때 발생한다. LVN_GETDISPINFO 항목의 출력이나 정렬을 위한 정보를 부모 윈도우에게 요청한다. LVN_INSERTITEM 새로운 항목이 삽입될 때 발생한다. LVN_ITEMCHANGED 사용자가 다른 항목을 선택했을 때 발생한다. LVN_ITEMCHANGING 사용자가 다른 항목을 선택하려고 할 때 발생한다. LVN_KEYDOWN 리스트 뷰 컨트롤이 포커스를 가지고 있는 상태에서 키보드 입력이 있을 때 발생 BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HIMAGELIST hIL; static HWND hListView; switch (message) { case WM_INITDIALOG : { hIL = ImageList_LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1), 16, 1, RGB(192,192,192)); hListView = GetDlgItem(hDlg,IDC_LIST1); SendMessage(hListView,LVM_SETIMAGELIST,(WPARAM)LVSIL_SMALL,(LPARAM)hIL); SendMessage(hListView,LVM_SETIMAGELIST,(WPARAM)LVSIL_NORMAL,(LPARAM)hIL); LVCOLUMN col; col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; col.fmt = LVCFMT_LEFT; col.cx = 100; col.pszText = "이름"; col.iSubItem = 0; SendMessage(hListView,LVM_INSERTCOLUMN,0,(LPARAM)&col); col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; col.fmt = LVCFMT_LEFT; col.cx = 100; col.pszText = "주소"; col.iSubItem = 1; SendMessage(hListView,LVM_INSERTCOLUMN,1,(LPARAM)&col); col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; col.fmt = LVCFMT_LEFT; col.cx = 100; col.pszText = "전화번호"; col.iSubItem = 2; SendMessage(hListView,LVM_INSERTCOLUMN,2,(LPARAM)&col); LVITEM li; li.mask = LVIF_TEXT | LVIF_IMAGE; li.state = 0; li.stateMask = 0; li.iImage = 0; li.iSubItem = 0; li.iItem = 0; li.pszText = "홍길동"; SendMessage(hListView, LVM_INSERTITEM, 0 , (LPARAM)&li); li.iSubItem = 1; li.pszText = "서울"; SendMessage(hListView, LVM_SETITEM, 0 , (LPARAM)&li); li.iSubItem = 2; li.pszText = "123-1234"; SendMessage(hListView, LVM_SETITEM, 0 , (LPARAM)&li); li.iImage = 1; li.iSubItem = 0; li.iItem = 1; li.pszText = "김길동"; SendMessage(hListView, LVM_INSERTITEM, 0 , (LPARAM)&li); li.iSubItem = 1; li.pszText = "강원도"; SendMessage(hListView, LVM_SETITEM, 0 , (LPARAM)&li); li.iSubItem = 2; li.pszText = "234-2345"; SendMessage(hListView, LVM_SETITEM, 0 , (LPARAM)&li); } return TRUE ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDOK : case IDCANCEL : EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } 11. 리스트 뷰 typedef struct _LVCOLUMN { UINT mask; int fmt; int cx; LPTSTR pszText; int cchTextMax; int iSubItem; #if (_WIN32_IE >= 0x0300) int iImage; int iOrder; #endif } LVCOLUMN, *LPLVCOLUMN; LVCF_FMT fmt 멤버에 있는 값을 사용한다. LVCF_SUBITEM iSubItem 멤버에 있는 값을 사용한다. LVCF_TEXT pszText 멤버에 있는 값을 사용한다. LVCF_WIDTH cx 멤버에 있는 값을 사용한다. LVCF_IMAGE image 멤버에 있는 값을 사용한다. LVCF_ORDER iOrder 멤버에 있는 값을 사용한다. 11. 리스트 뷰 LVCFMT_LEFT 문자열을 왼쪽으로 정렬한다. LVCFMT_RIGHT 문자열을 오른쪽으로 정렬한다. LVCFMT_CENTER 문자열을 중앙으로 정렬한다. LVCFMT_BITMAP_ON_ RIGHT 비트맵을 문자열의 오른쪽으로 배치한다. LVCFMT_COL_HAS_IM AGE 헤더에 이미지가 나타난다. LVCFMT_IMAGE 이미지 리스트의 이미지를 출력한다. pszText 헤더에 나타날 문자열을 지정 cchTextMax pszText멤버의 크기를 지정. 헤더를 만들 때는 사용 하지 않는다. cx 헤더의 폭을 픽셀 단위로 지정 iSubItem 리스트 뷰 컨트롤의 몇 번째 세부 항목에 대한 제목인 가를 지정한다. iImage 컬럼에 나타날 이미지의 인덱스 iOrder 컬럼의 순서를 지정 11. 리스트 뷰 • 선택 항목 조사 – 리스트 뷰는 항목에 변경이 발생하기 전에 LVN_ITEMCHANGING 통지 메시지를 보내주고 변경이 발생한 후에 LVN_ITEMCHANGED 통지 메시지를 보내준다. – 이 두 메시지의 lPara에는 어떠한 변경이 발생했는지에 대한 정보 인 NMLISTVIEW 구조체의 포인터가 전달된다. typedef struct tagNMLISTVIEW { NMHDR hdr; int iItem; int iSubItem; UINT uNewState; UINT uOldState; UINT uChanged; POINT ptAction; LPARAM lParam; } NMLISTVIEW, *LPNMLISTVIEW; typedef struct tagNMHDR { HWND hwndFrom; UINT idFrom; UINT code; } NMHDR; case WM_NOTIFY: { LPNMHDR LPNMLISTVIEW nlv; hdr = (LPNMHDR)lParam; nlv = (LPNMLISTVIEW)lParam; hdr; if (hdr->hwndFrom == hListView) { switch(hdr->code) { case LVN_ITEMCHANGED: if(nlv->uChanged == LVIF_STATE && nlv->uNewState == (LVIS_SELECTED|LVIS_FOCUSED)) { char szName[255]; char szAddr[255]; char szTelNo[255]; ListView_GetItemText(hListView, nlv->iItem,0,szName,255); ListView_GetItemText(hListView, nlv->iItem,1,szAddr,255); ListView_GetItemText(hListView, nlv->iItem,2,szTelNo,255); char temp[256]; wsprintf(temp,"이름 : %s,주소 : %s,전화번호 : %s",szName, szAddr,szTelNo); MessageBox(hDlg,temp,"",MB_OK); } } } } case WM_NOTIFY: { LPNMHDR hdr; LPNMLISTVIEW nlv; hdr = (LPNMHDR)lParam; nlv = (LPNMLISTVIEW)lParam; if (hdr->hwndFrom == hListView) { switch(hdr->code) { case NM_DBLCLK: { LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE) lParam; if (nlv->iItem != -1) { char temp[256]; ListView_GetItemText(hListView,lpnmitem->iItem,lpnmitem->iSubItem,temp,256); MessageBox(NULL,temp,"",MB_OK); } } } } } 12. 트리 뷰 – 트리 뷰 컨트롤 스타일 • TVS_HASBUTTONS – 자식 항목을 가진 부모 항목 옆에 +, - 버튼을 보여 준다. • TVS_HASLINES – 항목간의 계층구조를 좀 더 명확히 보여주기 위해 점선으로 항목간을 연 결하여 표시하도록 한다. • TVS_LINESATROOT – 루트 항목끼리 선으로 연결한다. 단 이 스타일은 TVS_HASLINES속성이 선택되어 있을 때만 효력을 발휘한다. • TVS_EDITLABES – 사용자가 항목의 텍스트를 직접 수정할 수 있도록 해준다. • TVS_DISABLEDRAGDROP – 스타일의 이름대로 항목을 드래그하지 못하도록 한다. • TVS_SHOWSELALWAYS – 트리 뷰가 포커스를 가지지 않은 상태에서 선택된 항목이 반전된 상태로 남아 있도록 한다. • TVS_CHECKBOXES – 항목 옆에 체크 박스를 보여준다. 12. 트리 뷰 – 트리 뷰 컨트롤 메시지 TVM_CREATEDRAGIMAGE 이미지 리스트를 만들고 lParam으로 지정한 항목 의 드래그 비트맵을 만들어 이미지 리스트에 포함 시킨다. TVM_DELETEITEM lParam으로 지정한 항목을 삭제한다. lParam이 TVI_ROOT이면 트리의 모든 항목이 삭제된다. TVM_EDITLABLE lParam으로 지정한 항목을 즉시 편집한다. 이 메 시지 후에 곧바로 TVN_BEGINLABELEDIT통지 메 시지가 전달된다. TVM_EDITLABLENOW 편집중인 항목의 편집을 중단하도록 한다. wParam이 TRUE이면 편집 동작은 취소되며, FALSE이면 편집동작을 저장한 후 끝낸다. TVM_ENSUREVISIBLE lParam으로 지정한 항목이 화면에 보이도록 한다. TVM_EXPAND lParam으로 지정된 항목을 확장하거나 축소한다. wParam은 다음 중 하나이다. TVE_COLLAPSE : 축소한다. TVE_COLLAPSERESET : 축소함과 동시에 차일드 를 파괴한다. 12. 트리 뷰 TVE_EXPAND : 확장한다. TVE_TOGGLE : 확장되어 있으면 축소하고 축소되 어 있으면 확장한다. TVM_GETCOUNT 항목의 개수를 구한다. TVM_GETEDITCONTROL 항목 편집에 사용되는 에디트 컨트롤의 핸들을 구 한다. TVM_GETIMAGELIST 트리 컨트롤과 연결된 이미지 리스트의 핸들을 구 한다. wParam에는 TVSIL_NORMAL, TVSIL_STATE중 하나를 지정한다. TVM_GETITEM 특정 항목을 찾는다.찾고자 하는 항목의 정보를 TVITEM구조체의 hItem멤버에 대입한 후 이구조체 를 lParam으로 넘겨주면 구조체에 항목의 정보를 채워준다. TVM_GETITEMRECT 항목이 차지하고 있는 사각영역을 구한다. lParam으로 RECT구조체를 넘겨주고, wParam이 TRUE이면 문자열 영역에 대해서만 사각영역이 구 해지며, FALSE이면 선을 포함한 영역까지 다 구해 준다. 12. 트리 뷰 TVM_GETNEXTITEM lParam으로 지정한 항목과 wParam의 관계를 가 지는 항목을 구한다. wParam : TVGN_CHILD, TVGN_NEXT TVM_HITTEST 특정한 한 점이 트리 컨트롤의 어디쯤인가를 조사 한다. TVM_INSERTITEM lParam으로 전달되는 TV_INSERTSTRUCT구조체 의 정보를 참조하여 항목을 추가한다. TVM_SELECTITEM lParam으로 지정한 항목을 선택하거나 보이게 한 다. TVM_SETIMAGELIST 트리 컨트롤과 연결될 이미지 리스트를 지정한다. lParam으로 이미지 리스트의 핸들, wParam으로 TVSIL_NORMAL, TVSIL_STATE TVM_SETITEM 항목의 속성을 설정한다. TVITEM구조체를 작성한 후 lParam으로 전달한다. 12. 트리 뷰 – 트리 뷰 컨트롤 통지 메시지 TVN_BEGINDRAG 왼쪽 마우스 버튼으로 항목을 드래그하기 시작할 때 발생한다. TVN_BEGINLABELEDIT 레이블을 편집할 때 발생한다. TVN_DELTEITEM 한 항목이 삭제될 때 발생한다. TVN_ENDLABELEDIT 레이블 편집이 완료되었을 때 발생한다. TVN_GETDISPINFO 항목의 출력이나 정렬을 위한 정보를 부모 윈도우에게 요청한다. TVN_ITEMEXPANDED 트리의 확장 또는 축소되었을 때 발생한다. TVN_KEYDOWN 트리 컨트롤이 포커스를 가지고 있는 상태에서 키보드 입력이 있을 때 발생한다. TVN_SELCHANGED 사용자가 다른 항목을 선택했을 때 발생한다. TVN_SELCHANGING 사용자가 다른 항목을 선택하려고 할 때 발생한다. TVN_SETDISPINFO 부모 윈도우가 정력이나 출력을 위해 유지하고 있는 항목의 정보가 갱신되어야 함을 알린다. 12. 트리 뷰 – TVINSERTSTRUCT typedef struct tagTVINSERTSTRUCT { HTREEITEM hParent; HTREEITEM hInsertAfter; #if (_WIN32_IE >= 0x0400) union { TVITEMEX itemex; TVITEM item; } DUMMYUNIONNAME; #else TVITEM item; #endif } TVINSERTSTRUCT, *LPTVINSERTSTRUCT; 12. 트리 뷰 – TVITEM typedef struct tagTVITEM { UINT HTREEITEM UINT UINT LPTSTR int int int int LPARAM } TVITEM, *LPTVITEM; mask; hItem; state; stateMask; pszText; cchTextMax; iImage; iSelectedImage; cChildren; lParam; – mask : • TVIF_CHILDREN : cChildren 멤버 • TVIF_HANDLE : hItem 멤버 • TVIF_IMAGE : iImage 멤버 12. 트리 뷰 • • • • TVIF_PARAM : lParam 멤버 TVIF_SELECTEDIMAGE : iSelectedImage 멤버 TVIF_STATE : state, stateMask 멤버 TVIF_TEXT : pszText, cchTextMax멤버 – state, stateMask • • • • • • • • TVIS_BOLD : 두꺼운 문자 TVIS_CUT : 잘라내기 된 상태이며 흐리게 나타난다. TVIS_DROPHILITED : 드래그 앤 드롭의 타겟이 되어 잇는 상태 TVIS_EXPENDED : 현재 항목이 확장되어 있는 상태이며 세부 항목들 이 보이는 상태 TVIS_EXPANEDEONCE : 현재 항목이 최소한 한 번 이상 확장된 적이 있는 상태 TVIS_EXPANDPARTIAL : 항목이 부분적으로 확장되어 있다. 세부 항 목이 보이기는 하지만 전부 다 보이는 상태는 아니다. TVIS_OVERLAYMASK : 항목이 그려질 때 오버레이 이미지가 포함되 어 있다. TVIS_SELECTED : 선택되어 있는 상태이며 일반적으로 파란색으로 반전된다. 12. 트리 뷰 – pszText • 항목의 실제 내용이 되는 문자열 – cchTextMax • pszText의 길이를 지정한다. – iImage • 항목의 왼쪽에 나타날 이미지의 번호를 지정한다. – iSelectedImage • 항목이 선택되었을 때 나타날 이미지의 번호를 지정한다. – cChildren • 이 항목이 세부 항목을 가지고 있는지를 조사한다. BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HIMAGELIST hIL; static HWND hTreeView; switch (message) { case WM_INITDIALOG : { hIL = ImageList_LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1), 16, 1, RGB(192,192,192)); hTreeView = GetDlgItem(hDlg,IDC_TREE1); SendMessage(hTreeView,TVM_SETIMAGELIST,(WPARAM)TVSIL_NORMAL,(LPARAM)hIL); TVINSERTSTRUCT tvi; tvi.hParent = 0; tvi.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; tvi.item.iImage = 0; tvi.item.iSelectedImage = 4; tvi.item.pszText = "친구"; HTREEITEM hRoot = (HTREEITEM)SendMessage(hTreeView,TVM_INSERTITEM,0,(LPARAM)&tvi); tvi.hParent = hRoot; tvi.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; tvi.item.iImage = 0; tvi.item.iSelectedImage = 4; tvi.item.pszText = "홍길동"; SendMessage(hTreeView,TVM_INSERTITEM,0,(LPARAM)&tvi); tvi.hParent = hRoot; tvi.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; tvi.item.iImage = 0; tvi.item.iSelectedImage = 4; tvi.item.pszText = "김길동"; SendMessage(hTreeView,TVM_INSERTITEM,0,(LPARAM)&tvi); } return TRUE ; case WM_NOTIFY: { } case WM_COMMAND : switch (LOWORD (wParam)) { case IDOK : case IDCANCEL : ImageList_Destroy(hIL); EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } 12. 트리 뷰 • 선택 – 트리 뷰는 사용자가 다른 항목을 선택하면 항목이 바뀌기 전에 TVN_SELCHANGING을 항목이 바뀐 후에 TVN_SELCHANGED 통 지 메시지를 보내준다. – TVN_SELCHANGED메시지를 받았을 때 사용자가 선택한 항목을 조사하여 특정 처리를 수행하면 된다. – 이 메시지는 lParam으로 NMLISTVIEW구조체를 보내준다. typedef struct tagNMLISTVIEW { NMHDR hdr; int iItem; int iSubItem; UINT uNewState; UINT uOldState; UINT uChanged; POINT ptAction; LPARAM lParam; } NMLISTVIEW, *LPNMLISTVIEW; 12. 트리 뷰 • action : 선택 변경이 키보드 (TVC_BYKEYBOARD)로부터 발생했는지 마우스(TVC_BYMOUSE)로부터 발생했는지를 알려준다. • itemOld : 이전에 선택되어 있던 항목에 대한 정보 • itemNew : 새로 선택된 항목에 대한 정보 • ptDrag : 통지 메시지가 발생할 때의 마우스 좌표 – BOOL TreeView_GetItem( HWND hwndTV, LPTVITEMEX pitem); – BOOL TreeView_SetItem( HWND hwndTV, LPTVITEMEX pitem); 12. 트리 뷰 case WM_NOTIFY: { LPNMHDR hdr; LPNMTREEVIEW ntv; hdr = (LPNMHDR)lParam; ntv = (LPNMTREEVIEW)lParam; TVITEMEX TvEx; char temp[256]; if (hdr->hwndFrom == hTreeView) { switch(hdr->code) { case TVN_SELCHANGED: TvEx.mask = TVIF_IMAGE | TVIF_TEXT; TvEx.hItem = ntv->itemNew.hItem; TvEx.pszText = temp; TvEx.cchTextMax = 256; TreeView_GetItem(hTreeView,&TvEx); MessageBox(hDlg,temp,"",MB_OK); } } } case WM_NOTIFY: { LPNMHDR hdr; LPNMTREEVIEW ntv; hdr = (LPNMHDR)lParam; ntv = (LPNMTREEVIEW)lParam; TVITEMEX TvEx; char temp[256]; if (hdr->hwndFrom == hTreeView) { switch(hdr->code) { case NM_DBLCLK: HTREEITEM hItem = TreeView_GetSelection(hTreeView); if (hItem == NULL) { MessageBox(hDlg,"선택된 항목이 없습니다.",NULL,MB_OK); } else { TvEx.mask = TVIF_TEXT; TvEx.pszText = temp; TvEx.cchTextMax = 256; TvEx.hItem = hItem; TreeView_GetItem(hTreeView,&TvEx); MessageBox(hDlg,temp,"",MB_OK); } } } } 12. 트리 뷰 – 임의의 시점에 어떤 항목을 조사 • HTREEITEM TreeView_GetSelection(HWND hwndTV); – 특정항목을 강제로 선택 • BOOL TreeView_Select(HWND hwndTV, HTREEITEM hitem, UINT flag); 12. 비트맵 1. 비트맵의 종류 • DDB – 윈도우가 지원하는 비트맵 포맷은 두 가지 종류가 있다. • 1. DDB (Device Dependent Bitmap) – 출력장치에 의존된다. – 이미지의 크기와 색상에 관한 기본적인 정보와 이미지 데이터로만 구성 되어 있다. – 다양한 해상도의 장치에 광범위하게 사용되지 못하며 만들어진 장치 외 의 다른 장치에서 출력하면 제대로 출력되지 못한다. • 2. DIB (Device Independent Bitmap) – 비트맵은 장치에 독립적이기 때문에 제 모양대로 출력될 수 있다. – DIB는 DDB에 비해 색상 테이블과 해상도 정보 등의 추가 정보를 가지므 로 장치에 종속되지 않으며 활용 용도가 훨씬 광범위 하다. – 확장자가 bmp를 가지는 비트맵 파일들은 모두 DIB 포맷으로 저장된 파 일이며 리소스 에디터에서 만들어주는 비트맵들도 모두 DIB이다. – dc에서 선택할 수 있는 비트맵은 DDB이다. – DIB는 직접 DC에 선택될 수 없기 때문에 프로그램에서 곧바로 사 용하기가 어렵다. 1. 비트맵의 종류 – wind32에서 HBITMAP으로 지칭되는 비트맵 오브젝트는 DDB를 말한다. – DDB만이 DC에 선택될 수 있다. – 리소스 에디터에서 만들어지는 비트맵 리소스들은 모두 DIB이지 만 이 리소스는 LoadBitmap함수에 의해 읽혀지면서 현재 비디오 모드와 호환되는 DDB로 변경된다. typedef struct tagBITMAP { LONG bmType; LONG bmWidth; LONG bmHeight; LONG bmWidthBytes; WORD bmPlanes; WORD bmBitsPixel; LPVOID bmBits; } BITMAP, *PBITMAP; (DDB포맷) 1. 비트맵의 종류 – BITMAP구조체 • bmType : 비트맵의 타입을 지정 ( 0으로 고정) • bmWidth, bmHeight : 비트맵의 폭과 높이를 픽셀 단위로 지정 • bmWidthBytes : 한 줄의 바이트 수, 비트맵은 WORD단위로 정렬되 기 때문에 반드시 짝수여야만 한다. • bmPlanes : 색상수면의 수 (보통 1) • bmBitsPixel : 한 픽셀을 표현하기 위해 필요한 비트 수 ( 1:흑백, 4:16색, 8:256색, 256이면 트루컬러 비트맵) • bmBits 멤버는 비트맵의 실제 데이터, 즉 비트맵의 이미지 모양을 가 지는 레스터 데이터에 대한 포인터. – HBITMAP CreateBitmap(int nWidth, int nHeight, UINT cPlanes, UNIT cBitsPerPel, CONST VOID * lpvBits); – HBITMAP CreateBitmapIndirect( CONST BITMAP * lpbm); LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc,hMemDC; PAINTSTRUCT ps ; RECT rect ; BYTE Bits[] = {0xc3,0xff,0xbd,0xff,0x66,0xff,0x5a,0xff,0x5a,0xff,0x66,0xff,0xbd,0xff,0xc3,0xff} HBITMAP hBitmap, hOldBitmap; switch (message) { case WM_CREATE: case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; hMemDC = CreateCompatibleDC(hdc); hBitmap = CreateBitmap(8,8,1,1,Bits); hOldBitmap = (HBITMAP)SelectObject(hMemDC,hBitmap); BitBlt(hdc,2,2,8,8,hMemDC,0,0,SRCCOPY); SelectObject(hMemDC,hOldBitmap); DeleteDC(hMemDC); DeleteObject(hBitmap); EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 1. 비트맵의 종류 – ROP 모드 BLACKNESS 0 무조건 검정색으로 칠한다. DSTINVERT ~D 화면 색을 반전시킨다. MERGECOPY S&P 브러시와 비트맵 색을 AND연산한다. MERGEPAINT ~S|D 비트맵을 반전한 후 화면 색과 OR연산을 한다. NOTSRCCOPY ~S 비트맵을 반전시킨다. NOTSRCERASE ~(S|D) 화면 색과 비트맵 색을 OR연산한 후 반전시킨다. PATCOPY P 현재 선택된 브러시로 칠한다. PATINVERT P^D 브러시와 화면 색을 XOR연산한다. PATPAINT P|~(S|D) NOTSRCERASE의 결과를 브러시와 OR연산한다. SRCAND S&D 비트맵과 화면 색을 AND연산한다. SRCCOPY S 비트맵을 그래도 화면에 출력한다. SRCERASE S&~D 비트맵과 화면의 반전 색을 AND연산한다. SRCINVERT S^D 비트맵과 화면을 XOR연산한다. SRCPAINT S|D 비트맵과 화면을 OR연산한다. WHITENESS 1 무조건 흰색으로 칠한다. LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc,hMemDC; PAINTSTRUCT ps ; RECT rect ; HBITMAP hBitmap, hOldBitmap; static HINSTANCE hInst; switch (message) { case WM_CREATE: hInst = ((LPCREATESTRUCT)lParam)->hInstance; return 0; case WM_PAINT: { hdc = BeginPaint (hwnd, &ps) ; hMemDC = CreateCompatibleDC(hdc); hBitmap = LoadBitmap(hInst,MAKEINTRESOURCE(IDB_BITMAP1)); hOldBitmap = (HBITMAP)SelectObject(hMemDC,hBitmap); HBRUSH hBrush = (HBRUSH)GetStockObject(BLACK_BRUSH); HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc,hBrush); Ellipse(hdc,100,100,200,200); SelectObject(hdc,hOldBrush); BITMAP bm; GetObject(hBitmap,sizeof(BITMAP),&bm); BitBlt(hdc,100,100,bm.bmWidth,bm.bmHeight,hMemDC,0,0,SRCPAINT); SelectObject(hMemDC,hOldBitmap); DeleteDC(hMemDC); DeleteObject(hBitmap); EndPaint (hwnd, &ps) ; } return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 1. 비트맵의 종류 – 확대 및 축소 BOOL StretchBlt( HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, DWORD dwRop ); // // // // // // // // // // // handle to destination DC x-coord of destination upper-left corner y-coord of destination upper-left corner width of destination rectangle height of destination rectangle handle to source DC x-coord of source upper-left corner y-coord of source upper-left corner width of source rectangle height of source rectangle raster operation code LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc,hMemDC; PAINTSTRUCT ps ; HBITMAP hBitmap, hOldBitmap; static HINSTANCE hInst; switch (message) { case WM_CREATE: hInst = ((LPCREATESTRUCT)lParam)->hInstance; return 0; case WM_PAINT: { hdc = BeginPaint (hwnd, &ps) ; hMemDC = CreateCompatibleDC(hdc); hBitmap = LoadBitmap(hInst,MAKEINTRESOURCE(IDB_BITMAP1)); hOldBitmap = (HBITMAP)SelectObject(hMemDC,hBitmap); BITMAP bm; GetObject(hBitmap,sizeof(BITMAP),&bm); StretchBlt(hdc,100,100,bm.bmWidth*2,bm.bmHeight*2,hMemDC,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY); SelectObject(hMemDC,hOldBitmap); DeleteDC(hMemDC); DeleteObject(hBitmap); EndPaint (hwnd, &ps) ; } return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 1. 비트맵의 종류 – BOOL TransparentBlt(… , UINT crTransparent); • 마지막 인수로 지정한 컬러를 투명하게 처리한다. • DIB – DIB는 다양한 장치에 사용할 수 있도록 하기 위해 비트맵 출력에 대한 상세 정보를 포함하고 있다. BITMAPFILEHEADER 구조체 BITMAPINFOHEADER 구조체 RGBQUAD 구조체 배열 비트 정보 1. 비트맵의 종류 – BITMAPFILEHEADER 구조체 typedef struct tagBITMAPFILEHEADER { WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; } BITMAPFILEHEADER, *PBITMAPFILEHEADER; bfType 파일의 형태를 지정하되 반드시 BM이어야 한다. (0x42,0x4d) bfSize 비트맵 파일의 크기를 바이트 단위로 지정한다. bfReserved1 예약. 0으로 설정한다. bfReserved2 예약. 0으로 설정한다. bfOffBits 이 구조체와 실제 비트맵 데이터와의 오프셋 값 이 값은 BITMAPFILEHEADER의 크기 + BITMAPINFOHEADER 의 크기 + RGBQUAD구조체 배열의 크기이다. 1. 비트맵의 종류 – 이 구조체는 DIB가 디스크의 파일로 저장될 때만 사용되며 비트맵 을 출력할 때는 사용되지 않는다. – 파일로 저장된 BMP파일에만 이 구조체가 있고 메모리상의 DIB에 는 이 구조체가 없다. typedef struct tagBITMAPINFOHEADER{ DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER, *PBITMAPINFOHEADER; biSize 이 구조체의 크기. 이 구조체의 시작 번지에 biSize를 더하면 색상 테이블의 위치를 구할 수 있다. biWidth 비트맵의 가로 픽셀 수 biHeight 비트맵의 세로 픽셀 수. 이 값이 양수이면 DIB는 아래에서 위로 구 성되며, 원점은 아래 왼쪽이 된다. 이 값이 음수이면 DIB는 위에서 아래로 구성되며 원점은 위왼쪽이 된다. biPlanes 플레인 개수를 나타내는 멤버이되 이 값은 1로 고정되어 있다. biBitCount 한 픽셀이 몇 개의 비트로 이루어지는가를 나타내며 이 값에 따라 픽셀이 가질 수 있는 색상수가 결정된다. biCompre ssion 압축 방법을 지정한다. 아래에서 위로 비트맵일 경우만 압축이 가 능하며 위에서 아래로 비트맵은 압축할 수 없다. 이 값이 BI_RGB 이면 압축되지 않은 비트맵이며, bi_RLE8이면 8비트 압축, BI_RLE4이면 4비트 압축 방법으로 압축되어 있는 것이다. biSizeIma ge 이미지의 크기를 바이트 단위로 나타내며 BI_RGB(압축되지 않음) 에서는 0이다. biXPelsPe rMeter 미터당 가로 픽셀수, 즉 가로 해상도를 지정한다. biYPelsPe rMeter 세로 해상도 1. 비트맵의 종류 biClrUsed 색상테이블의 색상 중 실제로 비트맵에서 사용되는 색상수를 나타 낸다. 이 값이 0이면 비트맵은 사용 가능한 모든 색상을 다 사용한 다. 이 값이 0이 아닐 경우 RGBQUAD구조체 배열의 크기는 이 멤 버 값 만큼이 된다. biClrImpor 비트맵을 출력하는데 필수적인 색상수를 나타내며 이 값이 0이면 tant 모든 색상이 다 사용되어야 한다. int SetDIBitsToDevice( HDC hdc, // handle to DC int XDest, // x-coord of destination upper-left corner int YDest, // y-coord of destination upper-left corner DWORD dwWidth, // source rectangle width DWORD dwHeight, // source rectangle height int XSrc, // x-coord of source lower-left corner int YSrc, // y-coord of source lower-left corner UINT uStartScan, // first scan line in array UINT cScanLines, // number of scan lines CONST VOID *lpvBits, // array of DIB bits CONST BITMAPINFO *lpbmi, // bitmap information UINT fuColorUse // RGB or palette indexes ); LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc,hMemDC; PAINTSTRUCT ps ; HBITMAP hBitmap, hOldBitmap; static BITMAPFILEHEADER * fh; static BITMAPINFOHEADER * ih; static int bx; static int by; static BYTE * pRaster; static HINSTANCE hInst; switch (message) { case WM_CREATE: hInst = ((LPCREATESTRUCT)lParam)->hInstance; return 0; case WM_LBUTTONDOWN: { OPENFILENAME OFN; char lpstrFile[256]=""; memset(&OFN,0,sizeof(OPENFILENAME)); OFN.lStructSize = sizeof(OPENFILENAME); OFN.hwndOwner = hwnd; OFN.lpstrFile = lpstrFile; OFN.lpstrFilter="Bitmap File(*.bmp)\0*.bmp\0"; OFN.nMaxFile = 256; if (GetOpenFileName(&OFN) != 0) { HANDLE hFile; DWORD FileSize, dwRead; hFile = CreateFile(lpstrFile,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if (hFile == INVALID_HANDLE_VALUE) { return 0; } FileSize = GetFileSize(hFile,NULL); if (fh) free(fh); fh = (BITMAPFILEHEADER *)malloc(FileSize); ReadFile(hFile,fh,FileSize,&dwRead,NULL); CloseHandle(hFile); pRaster = (PBYTE)fh + fh->bfOffBits; ih = (BITMAPINFOHEADER *)((PBYTE)fh + sizeof(BITMAPFILEHEADER)); bx = ih->biWidth; by = ih->biHeight; InvalidateRect(hwnd,NULL,TRUE); } } return 0; case WM_PAINT: { hdc = BeginPaint (hwnd, &ps) ; if (fh) { SetDIBitsToDevice(hdc,0,0,bx,by,0,0,0,by,pRaster,(BITMAPINFO *)ih, DIB_RGB_COLORS); } EndPaint (hwnd, &ps) ; } return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 1. 비트맵의 종류 • DIB로 변환 – DDB는 주로 프로그램 실행 중에 CreateBitmap나 CreateCompatibleBitmap함수로 만들어 진다. – 이 포맷을 파일로 저장하려면 DIB로 변환해 주어야 한다. 13. 가상 메모리 1. 메모리 할당 • Win32에서 추가된 가상 메모리 할당 함수들 – 1. 메모리를 예약 상태로 할당할 수 있다. • 예약이란 물리적인 메모리를 소비하지 않으면서 주소 공간만을 미리 할당해 놓는 방법을 말한다. – 2. 할당한 메모리의 액세스 권한을 지정할 수 있다. • 가상 메모리 함수로 할당한 메모리는 읽기 전용, 액세스 금지 속성을 가질 수 있어 실수로 인한 데이터 파괴를 막을 수 있다. – LPVOID VirtualAlloc(LPVOID lpAddress, DWORD dwSize, DWORD flAllocationType, DWORD flProtect); • lpAddress : 할당하고자 하는 메모리 번지를 지정하되 NULL이면 시 스템이 알아서 할당 번지를 지정해 준다. • dwSize : 할당하고자 하는 메모리의 양을 바이트 단위로 지정한다. • flAllocationType : 할당 방법 ( MEM_RESERVE, MEM_COMMIT) • flProtect : 할당한 페이지의 액세스 타입을 지정하며 보통 PAGE_READWRITE로 지정한다. – LPVOID VirtualFree (LPVOID lpAddress, DWORD dwSize, 1. 메모리 할당 – LPVOID VirtualFree (LPVOID lpAddress, DWORD dwSize, DWORD dwFreeType); • lpAddress : 해제하고자 하는 메모리의 선두 번지 • dwSize : 해제하고자 하는 메모리의 크기 • dwFreeType : MEM_DECOMMIT (확정된 페이지를 확정 해제) MEM_RELEASE(예약된 페이지를 예약 해제 한다.) 2. 예약과 확정 – win32 프로세스가 가지는 4G의 가상 메모리는 “페이지”라는 단위 로 구성된다. – 인텔 계열의 CPU에서는 한 페이지의 크기는 4K바이트이다. – 윈도우는 페이지 단위로 가상 메모리를 관리한다. • 할당하거나 해제하는 단위가 페이지 단위이다. – 가상 메모리를 구성하는 각 페이지는 다음 세 가지 상태 중 하나의 상태로 존재한다. • 1. 자유영역 (Free) : 사용되지 않는 자유 영역이다. • 2. 예약 (Reserved) : 장래 사용을 위해 예약만 되어 있는 페이지이며 물리적인 메모리가 할당되어 있지 않다. • 3. 확정(Committed) : 물리적 메모리가 할당되어 잇는 상태이며 바로 사용할 수 있다. – 할당 영역의 크기는 반드시 페이지 단위의 배수가 된다. • 10K의 크기만큼 할당을 요청했다면 실제로 할당되는 영역의 크기는 12K가 될 것이다. 3. 보호 속성 – VirtualAlloc의 네 번째 인수 fProtect는 할당하고자 하는 메모리의 액세스 타입을 지정한다. • • • • • • PAGE_READONLY : 읽기만 가능하도록 한다. PAGE_READWRITE : 읽기 쓰기를 가능하도록 한다. PAGE_EXECUTE : 실행만 가능하도록 한다. PAGE_EXECUTE_READ : 실행 및 일기기만 가능하도록 한다. PAGE_EXECUTE_READWRITE : 실행,읽기,쓰기를 가능하도록 한다. PAGE_GUARD : 보호페이지로 지정한다. 이 페이지에 읽기, 쓰기를 시도하면 STATUS_GUARD_PAGE예외가 발생하며 보호 페이지 상태 가 해제된다. 메모리의 끝을 표시하는 용도로 주로 사용된다. • PAGE_NOACCESS: 어떤 액세스도 하지 못하도록 한다. • PAGE_NOCACHE : 캐시를 금지시킨다. 4. 메모리 맵 파일 – 메모리 맵 파일은 하드 디스크에 존재하는 파일의 내용을 프로세 스의 주소 공간에 연결하는 기법이다. • 파일을 메모리처럼 사용하는 기법 – 가상 주소 공간에 파일을 맵한 후 그 포인터를 사용하면 파일의 내 용을 마치 메모리 다루듯이 똑같이 사용할 수 있다. – 파일을 열고 닫고 파일 포인터를 옮기고 버퍼를 유지하는 복잡한 처리를 할 필요 없이 마치 메모리에 데이터를 읽고 쓰듯이 파일을 조작할 수 있다. LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps ; switch (message) { case WM_CREATE: return 0; case WM_LBUTTONDOWN: { hdc = GetDC(hwnd); HANDLE hFile = CreateFile("123.txt",GENERIC_READ,0,NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL); if (hFile == INVALID_HANDLE_VALUE) { MessageBox(hwnd,"파일이 없습니다.","에러",MB_OK); } else { HANDLE hFMap = CreateFileMapping(hFile,NULL,PAGE_READONLY,0,0,NULL); char * pData = (char *)MapViewOfFile(hFMap,FILE_MAP_READ,0,0,0); RECT rect; GetClientRect(hwnd,&rect); DrawText(hdc,pData,GetFileSize(hFile,NULL),&rect,DT_EXPANDTABS); UnmapViewOfFile(hFMap); CloseHandle(hFile); } ReleaseDC(hwnd,hdc); } return 0; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 4. 메모리 맵 파일 HANDLE CreateFileMapping ( HANDLE LPSECURITY_ATTRIBUTES DWORD DWORD DWORD LPCTSTR hFile, lpAttributes, flProtect, dwMaximumSizeHigh, dwMaximumSizeLow, lpName ); – hFile : 대상 파일의 핸들 (INVALID_HANDLE_VALUE) – lpAttribute : NULL – flProtect : • PAGE_READONLY : 읽기 전용의 파일 맵핑 오브젝트를 만든다. 이렇 게 만들어진 메모리 맵 파일에 쓰기를 해서는 안 된다. • PAGE_READWRITE : 읽고 쓸 수 있는 파일 맵핑 오브젝트를 만든다. • PAGE_WRITECOPY : 쓰기를 수행하는 시점에서 별도의 복사본이 생 성된다. – dwMaximumSizeHigh, dwMaximumSizeLow : 이 인수들이 모두 0이면 hFile에서 지정한 파일의 크기가 그대로 사용된다. 4. 메모리 맵 파일 – 파일 맵핑의 크기는 hFile이 INVALID_HANDLE_VALUE일 경우, 즉 하드디스크의 파일이 아닌 페이징 파일에 메모리 맵 파일을 생성 할 경우에 사용된다. – 파일 맵핑 오브젝트를 만든 후에는 이 오브젝트를 프로세스의 주 소 공간에 맵해야 한다. – 주소 공간에 맵한 후 그 주소 공간을 사용한다. – 주소 공간에 맵된 파일의 일부분을 파일 뷰라고 한다. LPVOID MapViewOfFile( HANDLE DWORD DWORD DWORD SIZE_T ); hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh, dwFileOffsetLow, dwNumberOfBytesToMap 4. 메모리 맵 파일 – hFileMappingObject : • 주소 공간에 맵하려는 파일 오브젝트의 핸들 – dwDesiredAccess : • • • • FILE_MAP_WRITE : 읽고 쓸 수 있다. FILE_MAP_READ : 읽을 수 있다. FILE_MAP_ALL_ACCESS : 읽을 수도 있고 쓸 수도 있다. FILE_MAP_COPY : 읽고 쓸 수 있다. 쓰기 시도가 발생하면 데이터의 복사본을 만든 후 쓴다. – dwFileOffsetHigh, dwFileOffsetLow • 맵핑을 시작할 오프셋 위치를 나타내는 64비트 정수를 지정한다. • 이 값이 0이면 파일의 선두부터 맵핑이 된다. • 오프셋은 시스템의 할당 단위(64K)의 배수여야 한다. – dwNumberOfBytesToMap • 맵핑할 뷰의 크기 • 0이면 파일 전체가 맵핑된다. – UnmapViewOfFile(LPCVOID lpBaseAddress); 5. 메모리 공유 – 파일 맵핑의 크기는 hFile이 INVALID_HANDLE_VALUE일 경우, 즉 하드디스크의 파일이 아닌 페이징 파일에 메모리 맵 파일을 생성 할 경우에 사용된다. 14. 프로세스 1. 프로세스와 스레드 – 프로세스는 실행중인 프로그램의 한 인스턴스이다. – 운영체제는 실행된 프로그램을 프로세스 단위로 관리한다. – 프로세스는 각각 4GB의 주소 공간과 파일, 메모리, 스레드 등의 객체들을 소유하며 프로세스가 종료될 때 프로세스가 소유한 자원 은 운영체제에 의해 파괴된다. – 프로세스는 실행과 동시에 스레드를 하나 만들고 스레드를 호출함 으로써 스레드에게 모든 작업을 맡긴다. – 프로세스는 최소한 한 개 이상의 스레드를 가진다. – 프로세스와 동시에 만들어지는 스레드를 주 스레드(Primary Thread)라고 한다. – 하나의 프로세스가 여러 개의 스레드를 만들 수 있다. 1. 프로세스와 스레드 BOOL CreateProcess( LPCTSTR LPTSTR LPSECURITY_ATTRIBUTES LPSECURITY_ATTRIBUTES BOOL DWORD LPVOID LPCTSTR LPSTARTUPINFO LPPROCESS_INFORMATION lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation ); – lpApplicationName : • 실행하고자 프로그램의 이름을 준다. • 완전 경로를 주거나 파일명만 지정한 경우는 현재 디렉토리에서 파일 을 찾으며 검색 경로는 사용하지 않는다. • 이 인수를 NULL로 주고 두 번째 인수에 실행 파일명을 줄 수도 있다. 1. 프로세스와 스레드 – lpCommandLine • 명령행 인수를 지정한다. • 첫 번째 인수가 NULL일 경우 실행 파일명을 가질 수도 있으며 실행 파일명과 명령행 인수를 동시에 지정하는 것도 가능하다. – lpStartupInfo • 새로 만든 프로세스의 메인 윈도우가 어떻게 초기화될지를 지정하는 구조체이다. • 이 구조체의 cb멤버에는 구조체의 크기가 반드시 대입되어야 한다. – lpProcessInformation • 생성된 프로세스의 정보를 대입 받기 위한 구조체이며 생략할 수 없다. case WM_LBUTTONDOWN: { STARTUPINFO si; memset(&si,0,sizeof(STARTUPINFO)); PROCESS_INFORMATION pi; CreateProcess(NULL,"Notepad.exe",NULL,NULL,FALSE,NULL,NULL,NULL,&si,&pi); } return 0; 1. 프로세스와 스레드 typedef struct _STARTUPINFO { DWORD cb; LPTSTR lpReserved; LPTSTR lpDesktop; LPTSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } STARTUPINFO,*LPSTARTUPINFO; 1. 프로세스와 스레드 – cb : sizeof(STARTUPINFO)값을 대입한다. – dwFlags : 어떤 속성을 지정할 것인가에 따라 플래그를 설정한다. STARTF_USEPOSITON dwX,dwY 멤버가 지정하는 위치에 메인 윈도 우를 배치한다. STARTF_USESHOWWINDOW wShowWindow멤버가 지정하는 방식대로 메 인 윈도우를 보여준다. STARTF_USESIZE dwXSize, dwYSize멤버가 지정하는 크기대로 메인 윈도우를 배치한다. typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION,*LPPROCESS_INFORMATION; 1. 프로세스와 스레드 – lpProcessAttributes – lpThreadAttributes • 프로세스와 주 스레드의 보안 속성을 지정한다. – bInheritHandles • 새로 생성되는 프로세스가 페이런트로부터 핸들을 상속받을 수 있는 지를 지정한다. – dwCreationFlags • 새로 생성되는 프로세스의 우선 순위 클래스와 프로세스 생성 옵션을 지정한다. • REALTIME_PRIORITY_CLASS : 최상위 우선권 • HIGH_PRIORITY_CLASS : 상위 우선권 • ABOVE_PRIORITY_CLASS : 상위 우선권 • NORMAL_PRIORITY_CLASS : 보통 우선권 • BELOW_PRIORITY_CLASS : 하위 우선권 • IDLE_PRIORITY_CLASS : 최하위 우선권 – lpEnvironment • 새 프로세스의 환경 블록을 지정하는 포인터. 이 값이 NULL이면 페이 런트의 환경 블록을 사용하며 보통 NULL이다. 1. 프로세스와 스레드 – lpCurrentDirectory • 새 프로세스의 작업 디렉토리를 지정한다. • NULL일 경우 페이런트의 현재 디렉토리가 새 프로세스의 작업디렉토 리가 된다. – DWORD WaitForInputIdle( HANDLE hProcess, DWORD dwMilliseconds); • 이 함수는 hProcess가 지정하는 프로세스가 사용자의 입력을 대기할 수 있을 때까지, 즉 초기화가 완료될 때까지 기다려준다. – LPTSTR GetCommandLine(VOID); • 이 함수는 현재 프로세스의 명령행 인수를 조사해 리턴해 준다. – CommandLineToArgvW() • 함수를 사용하여 토큰별로 분리 할 수도 있다. 1. 프로세스와 스레드 – VOID ExitProcess( UINT uExitCode ); • 이 함수가 호출되면 프로세스는 정리작업에 들어가 즉각 종료된다. • 1. 프로세스와 연결된 모든 DLL을 종료시키기 위해 각 DLL의 DllMain 함수가 호출되며 DLL들은 스스로 정리 작업을 한다. • 2. 모든 열려진 핸들을 닫는다. • 3. 실행중인 모든 스레드는 종료한다. • 4. 프로세스 커널 객체와 스레드 객체는 신호상태가 되며 이 객체를 기다리는 다른 프로세스는 대기상태를 해제할 수 있다. • 5. 프로세스의 종료코드는 STILL_ACTIVE와 ExitProcess가 지정한 종 료값이 된다. – BOOL TerminateProcess( HANDLE hProcess, UINT uExitCode); • 이 함수는 ExitProcess에 비해 종료 대상이 되는 프로세스의 핸들을 인수로 가지므로 다른 프로세스를 강제로 종료시킬 수도 있다. • 이 함수는 ExitProcess보다 훨씬 더 위험하다. • TerminateProcess함수가 호출될 때 ExitProcess와 동일한 정리작업 이 수행되나 단 연결된 DLL에게 종료사실이 통지되지 않는다. • 어쩔 수 없이 강제로 종료해야 할 경우에만 사용한다. 2. 프로세스 핸들 – 커널 객체는 프로세스 한정적이다. • 커널 객체를 만드는 프로세스만이 자신의 핸들로 해당 객체를 액세스 할 수 있다는 뜻이다. – 핸들은 프로세스 내에서 해당 객체를 액세스할 때 사용하는 한정 적인 값이며 이 핸들을 사용하여 객체를 마음대로 조작할 수 있다. – ID는 시스템 전역적인 값이며 다른 프로세스 ID와 절대 중복되지 않는다. – 프로세스끼리 ID를 전달해 줌으로써 목적이 되는 프로세스 핸들을 다시 오픈할 수 있다. – HANDLE GetCurrentProcess(VOID); – HANDLE GetCurrentProcessId(VOID); LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps ; static HINSTANCE hInst; static STARTUPINFO si; static PROCESS_INFORMATION pi; switch (message) { case WM_CREATE: hInst = ((LPCREATESTRUCT)lParam)->hInstance; return 0; case WM_LBUTTONDOWN: { memset(&si,0,sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USEPOSITION | STARTF_USESIZE; si.dwX = 200; si.dwY = 200; si.dwXSize = 100; si.dwYSize = 100; CreateProcess(NULL,"Notepad.exe",NULL,NULL,FALSE,NULL,NULL,NULL,&si,&pi); } return 0; case WM_RBUTTONDOWN: { HWND hProcessWnd = FindWindow(NULL,"Process"); MessageBox(NULL,"윈도우를 찾았습니다.","",MB_OK); PostMessage(hProcessWnd,WM_MYMSG,(WPARAM)pi.dwProcessId,NULL); } return 0; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps ; static HINSTANCE hInst; static DWORD pID; static HANDLE hProc; switch (message) { case WM_CREATE: hInst = ((LPCREATESTRUCT)lParam)->hInstance; return 0; case WM_MYMSG: { pID = (DWORD)wParam; hProc = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pID); char temp[256]; wsprintf(temp,"Process ID : %x, Process Handle : %x",pID,hProc); MessageBox(NULL,temp,"",MB_OK); } return 0; case WM_LBUTTONDOWN: { DWORD ExitCode; GetExitCodeProcess(hProc,&ExitCode); if (ExitCode != STILL_ACTIVE) { MessageBox(NULL,"프로세스 핸들이 유효하지 않습니다.","",MB_OK); } else { TerminateProcess(hProc,NULL); pID = NULL; } } return 0; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; 3. Thread – Project/Settings c/c++탭의 Code Generation에서 Use run-time Library옵션을 선택 3. Thread uintptr_t _beginthreadex( void * security, unsigned stack_size, unsigned ( __stdcall *start_address )( void * ), void * arglist, unsigned initflag, unsigned * thrdaddr ); – – – – – security : SECURITY_ATTRIBUTES 구조체의 주소, 대부분 NULL stack_size : 0을 입력하면 기본 스텍사이즈를 사용한다. unsigned ( __stdcall *start_address )( void * ) arglist : 스레드 함수로 넘어가는 변수 initflag : CREATE_SUSPENDED면 스레드만 만들고 실행은 하지 않는다. – thrdaddr : 스레드 ID typedef struct threadParam { HWND hwnd; BOOL bCont; }THREAD_PARAM; unsigned int WINAPI MyThreadFunc(LPVOID lpParameter) { THREAD_PARAM * pTp = (THREAD_PARAM *)lpParameter; HWND hwnd = pTp->hwnd; while(pTp->bCont) { HBRUSH hBrush = CreateSolidBrush(RGB(rand()%256,rand()%256,rand()%256)); RECT ClietRect,rect; GetClientRect(hwnd,&ClietRect); SetRect(&rect,rand()%ClietRect.right,rand()%ClietRect.bottom,rand()%ClietRect.right,rand()%ClietRect.bottom); HDC hdc = GetDC(hwnd); HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc,hBrush); Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom); SelectObject(hdc,hOldBrush); ReleaseDC(hwnd,hdc); Sleep(100); } return 0; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps ; static unsigned int ThreadID; static HANDLE hThread; static THREAD_PARAM tp; switch (message) { case WM_COMMAND: switch(LOWORD(wParam)) { case IDM_THREAD_START: if (hThread == NULL) { tp.hwnd = hwnd; tp.bCont = TRUE; hThread = (HANDLE)_beginthreadex(NULL,NULL,MyThreadFunc,(void *)&tp,NULL,&ThreadID); } else { DWORD ExitCode; GetExitCodeThread(hThread,&ExitCode); if (ExitCode == STILL_ACTIVE ) MessageBox(NULL,"현재 스레드는 살아있습니다.","",MB_OK); else MessageBox(NULL,"스레드는 죽었습니다.","",MB_OK); } break; case IDM_THREAD_QUIT: tp.bCont = FALSE; break; case IDM_THERAD_SUSPEND: SuspendThread(hThread); break; case IDM_THREAD_RESUME: ResumeThread(hThread); break; } return 0; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } CRITICAL_SECTION cs; int XPos; unsigned int WINAPI MyThreadFunc1(LPVOID lpParameter) { THREAD_PARAM * pTp = (THREAD_PARAM *)lpParameter; HWND hwnd = pTp->hwnd; while(pTp->bCont) { HBRUSH hBrush = CreateSolidBrush(RGB(rand()%256,rand()%256,rand()%256)); RECT ClietRect,rect; GetClientRect(hwnd,&ClietRect); SetRect(&rect,rand()%ClietRect.right,rand()%ClietRect.bottom,rand()%ClietRect.right,rand()%ClietRect.bottom); HDC hdc = GetDC(hwnd); EnterCriticalSection(&cs); XPos = 100; Sleep(10); TextOut(hdc,XPos,0,"1번 스레드 펑션",15); LeaveCriticalSection(&cs); HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc,hBrush); Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom); SelectObject(hdc,hOldBrush); ReleaseDC(hwnd,hdc); Sleep(120); } return 0; } unsigned int WINAPI MyThreadFunc2(LPVOID lpParameter) { THREAD_PARAM * pTp = (THREAD_PARAM *)lpParameter; HWND hwnd = pTp->hwnd; while(pTp->bCont) { HBRUSH hBrush = CreateSolidBrush(RGB(rand()%256,rand()%256,rand()%256)); RECT ClietRect,rect; GetClientRect(hwnd,&ClietRect); SetRect(&rect,rand()%ClietRect.right,rand()%ClietRect.bottom,rand()%ClietRect.right,rand()%ClietRect.bottom); HDC hdc = GetDC(hwnd); EnterCriticalSection(&cs); XPos = 400; Sleep(15); TextOut(hdc,XPos,0,"2번 스레드 펑션",15); LeaveCriticalSection(&cs); HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc,hBrush); Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom); SelectObject(hdc,hOldBrush); ReleaseDC(hwnd,hdc); Sleep(130); } return 0; } 3. 동기화 • 크리티컬 섹션 – void InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); – void DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); • 둘 다 CRITICAL_SECTON형의 포인터를 인수로 요구한다. – void EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); – void LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); • 보호될 코드를 다음과 같이 두 함수로 감싸준다. EnterCriticalSection(&cs); //이 사이에서 공유 자원을 안전하게 액세스한다. LeaveCriticalSection(&cs); 3. 동기화 case WM_CREATE: InitializeCriticalSection(&cs); return 0; case WM_DESTROY: DeleteCriticalSection(&cs); PostQuitMessage (0) ; return 0 ; – 교착 상태 EnterCriticalSection(&cs1); EnterCriticalSection(&cs2); //이 사이에서 공유 자원을 안전하게 액세스한다. LeaveCriticalSection(&cs2); LeaveCriticalSection(&cs1); EnterCriticalSection(&cs2); EnterCriticalSection(&cs1); //이 사이에서 공유 자원을 안전하게 액세스한다. LeaveCriticalSection(&cs1); LeaveCriticalSection(&cs2); 3. 동기화 • 동기화 객체 – 동기화에 사용되는 객체이다. – 프로세스,스레드처럼 커널 객체이며 프로세스 한정적인 핸들을 가 진다. – 동기화 객체는 크리티컬 섹션보다 느리기는 하지만 여러 프로그램 에서 동시에 동기화가 가능하다. – 신호 상태 : 스레드의 실행을 허가하는 상태. 신호상태의 동기화 객체를 가진 스레드는 계속 실행할 수 있다. – 비 신호 상태 : 스레드의 실행을 허가하지 않는 상태이며 신호 상 태가 될 때까지 스레드는 블록 된다. – DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds ); • dwMilliseconds : 1/1000초 단위로 지정한다. INFINITE로 지정하면 무한정 기다린다. 3. 동기화 • WAIT_OBJECT_0 : hHandle 객체가 신호 상태가 되었다. • WAIT_TIMEOUT : 타임 아웃 시간이 경과하였다. • WAIT_ABANDONED : 포기된 뮤텍스 • 뮤텍스 – 크리티컬 섹션과 유사하다. – 이름을 가질 수 있고 프로세스간에서도 사용이 가능하다. – HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName ); • lpMutexAttributes : 보통 NULL • bInitialOwner : 뮤텍스를 생성함과 동시에 소유할 것인지를 지정한 다. TRUE이면 소유하며, 뮤텍스가 비 신호상태로 생성됨으로써 다른 스레드는 이 뮤텍스를 소요할 수 없게 된다. – 생성한 뮤텍스를 파괴할 때는 CloseHandle함수를 이용한다. – BOOL ReleaseMutex( HANDLE hMutex ); • 뮤텍스 소유를 해제한다. #include <windows.h> #include "resource.h" BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ; return TRUE; } BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hEdit1,hEdit2; static HANDLE hFileMapping; static char * pMapView; static HANDLE hMutex; switch (message) { case WM_INITDIALOG : hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,1042,"MappingMutexSample"); pMapView = (char *)MapViewOfFile(hFileMapping,FILE_MAP_ALL_ACCESS,0,0,1024); hEdit1 = GetDlgItem(hDlg,IDC_EDIT1); hEdit2 = GetDlgItem(hDlg,IDC_EDIT2); hMutex = CreateMutex(NULL,FALSE,"MyMutexSample"); return TRUE ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_BUTTON1: { WaitForSingleObject(hMutex,INFINITE); char temp[256]; GetWindowText(hEdit1,temp,256); SetWindowText(hEdit2,temp); strcpy(pMapView,temp); ReleaseMutex(hMutex); } return TRUE; case IDC_BUTTON2: { WaitForSingleObject(hMutex,INFINITE); SetWindowText(hEdit2,pMapView); ReleaseMutex(hMutex); } return TRUE; case IDOK : case IDCANCEL : EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } 3. 동기화 • 이벤트 – 어떤 사건이 일어났음을 알려주는 동기화 객체이다. – 크리티컬 섹션, 뮤텍스, 세마포어는 주로 공유 자원을 보호하기 위 해 사용되는 데 비해 이벤트는 그보다는 스레드간의 작업 순서나 시기를 조정하기 위해 사용한다. – 자동 리셋 • 대기 상태가 종료되면 자동으로 비신호상태가 된다. – 수동 리셋 • 스레드가 비신호상태로 만들어 줄 때까지 신호상태를 유지한다. HANDLE CreateEvent( LPSECURITY_ATTRIBUTES BOOL BOOL LPCTSTR lpName ); lpEventAttributes, bManualReset, bInitialState, 3. 동기화 • bManualReset : 이 이벤트가 수동 리셋 이벤트인지 자동 리셋 이벤트 인지를 지정한다. – TRUE : 수종 리셋 이벤트, FALSE : 자동 리셋 이벤트 • bInitialState : 이벤트 생성과 동시에 신호상태로 만들어 이벤트를 기 다리는 스레드가 곧바로 실행하도록 한다. – BOOL SetEvent( HANDLE hEvent ); – BOOL ResetEvent( HANDLE hEvent ); – BOOL PulseEvent(HANDLE hEvent ); #include <windows.h> #include "resource.h" #include <process.h> BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ; return TRUE; } typedef struct param { HWND hEdit2; BOOL bCont; HANDLE hEvent; char * pView; }PARAM; unsigned __stdcall MyThreadProc( void * pArguments ) { PARAM * pParam = (PARAM *)pArguments; while(pParam->bCont) { WaitForSingleObject(pParam->hEvent,INFINITE); SetWindowText(pParam->hEdit2,pParam->pView); } _endthreadex( 0 ); return 0; } BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hEdit1,hEdit2; static HANDLE hFileMapping; static char * pMapView; static HANDLE hMutex; static HANDLE hThread; static PARAM param; static HANDLE hEvent; switch (message) { case WM_INITDIALOG : hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,1042,"MappingMutexSample"); pMapView = (char *)MapViewOfFile(hFileMapping,FILE_MAP_ALL_ACCESS,0,0,1024); hEdit1 = GetDlgItem(hDlg,IDC_EDIT1); hEdit2 = GetDlgItem(hDlg,IDC_EDIT2); hMutex = CreateMutex(NULL,FALSE,"MyMutexSample"); hEvent = CreateEvent(NULL,TRUE,FALSE,"MyMutexSampleEvent"); param.bCont = TRUE; param.hEdit2 = hEdit2; param.hEvent = hEvent; param.pView = pMapView; hThread = (HANDLE)_beginthreadex(NULL,NULL,MyThreadProc,&param,NULL,NULL); return TRUE ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_BUTTON1: { WaitForSingleObject(hMutex,INFINITE); char temp[256]; GetWindowText(hEdit1,temp,256); strcpy(pMapView,temp); PulseEvent(hEvent); ReleaseMutex(hMutex); } return TRUE; case IDOK : case IDCANCEL : EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } 15. 파일 입출력 1. 파일 입출력 BOOL ReadFile( HANDLE LPVOID DWORD LPDWORD LPOVERLAPPED • • • • • hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped ); hFile : 데이터를 읽을 대상 파일의 핸들 lpBuffer : 읽은 데이터를 저장할 버퍼 nNumberOfBytesToRead : 읽고자 하는 양 lpNumberOfBytesRead : 실질적으로 읽은 바이트 양 lpOverlapped : 비동기 입출력을 할 때 사용한다. – BOOL CloseHandle( HANDLE hObject ); • 파일 핸들도 메모리를 차지하므로 다 사용하고 난 후에 해제해 주어야 한다. 1. 파일 입출력 HANDLE CreateFile( LPCTSTR DWORD DWORD LPSECURITY_ATTRIBUTES DWORD DWORD HANDLE lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile ); – lpFileName : 열거나 만들고자 하는 파일의 완전경로 – dwDesiredAccess : 파일의 액세스 타입, 즉 파일로부터 데이터를 읽을 것인지 쓸 것인지를 지정한다. • 파일로부터 데이터를 읽기만 하면 GENERIC_READ • 쓰기만 하면 GENERIC_WRITE • 읽기 쓰기 : GENERIC_READ | GENERIC_WRITE – hTemplateFile : 만들고자 하는 파일의 추가 속성을 지원하는 템플 리트 파일의 핸들을 지정 . 일반적으로 NULL지정 1. 파일 입출력 – dwShareMode : 열려진 파일의 공유 모드를 지정한다. • 이 인수가 0이면 파일은 공유되지 않으며 이미 열려진 파일을 또 열 수 없게 된다. • FILE_SHARE_DELETE : 삭제 액세스에 대해서만 파일을 열 수 있다. • FILE_SHARE_READ : 읽기 모드로 열 때만 파일을 열 수 있다. • FILE_SHARE_WRITE : 쓰기 모드로 열 때만 파일을 열 수 있다. – lpSecurityAttributes : 리턴된 핸들을 차일드 프로세스로 상속할 것인지를 지정하는 SECURITY_ATTRIBUTES 구조체의 핸들 – dwCreationDisposition : 만들고자 하는 파일이 이미 있을 경우, 또는 열고자 하는 파일이 없을 경우의 처리를 지정한다. • CREATE_NEW : 새로운 파일을 만들되 만약 지정한 파일이 이미 있으 면 만들지 않는다. • CREATE_ALWAYS : 새로운 파일을 만들되 만약 지정한 파일이 이미 있으면 기존 파일을 삭제하고 다시 만든다. • OPEN_EXISTING : 이미 있는 파일을 열되 만약 파일이 없으면 에러 코드를 되돌린다. • OPEN_ALWAYS : 파일을 열되 만약 없으면 새로 만든다. • TRUNCATE_EXISTING : 파일을 열되 파일 크기를 0으로 만든다. 1. 파일 입출력 – dwFlagsAndAttributes FILE_ATTRIBUTE_ARCHIVE 아카이브 속성 FILE_ATTRIBUTE_HIDDEN 히든 파일 FILE_ATTRIBUTE_NORMAL 아무 속성도 가지지 않은 보통 파일 FILE_ATTRIBUTE_READONLY 읽기 전용 FILE_ATTRIBUTE_SYSTEM 운영체제가 배타적으로 사용하는 파일 FILE_ATTRIBUTE_TEMPORARY 임시 저장소에 저장되는 파일. 이 속성의 파 일은 디스크에 저장되지 않고 메모리에 저 장되므로 입출력 속도가 빠르다. 사용 후에 는 반드시 지워주어야 한다. FILE_FLAG_WRITE_THROUGH 데이터 출력시 캐시를 통해 곧바로 디스크 로 출력하도록 한다. 플러쉬가 더 빨라진다. FILE_FLAG_OVERLAPPED 비동기 입출력을 행한다. FILE_FLAG_RANDOM_ACCESS 임의 접근 파일임을 알린다. FILE_FLAG_SEQUENTIAL_SCAN 순차 접근 파일임을 알린다. 이상의 두 플래 그는 시스템이 캐시를 최적화하는데 도움을 줄 뿐이다. (임의 접근 가능) 1. 파일 입출력 – 리턴 값 : 파일의 핸들 • 어떤 이유로 파일 열기에 실패하면 INVALID_HANDLE_VALUE를 리 턴한다. BOOL WriteFile( HANDLE LPCVOID DWORD LPDWORD LPOVERLAPPED – – – – – hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped ); hFile : 대상 파일의 핸들 lpBuffer : 데이터가 들어 있는 버퍼 nNumberOfBytesToWrite : 쓰고자 하는 바이트 수 lpNumberOfBytesWritten : 실제로 쓰여진 바이트 수 lpOverlapped : 비동기 입출력을 할 때 사용한다. 2. 비동기 입출력 – ReadFile이나 WriteFile같은 입출력 함수는 입출력이 완전히 끝 날 때까지 리턴하지 않는다. – 비동기 입출력을 하려면 CreateFile함수로 파일을 열 때 FILE_FLAG_OVERLAPPED플래그를 주고 ReadFile, WriteFile함 수의 마지막 인수에 OVERLAPPED구조체의 포인터를 전달해 준 다. – 입출력 함수는 입출력이 시작되는 즉시 리턴하여 다른 작업을 계 속할 수 있도록 해준다. – 입출력 함수느 데이터를 완전히 다 입출력하지 않았다는 의미로 FALSE를 리턴하며 GetLastError함수로 에러 코드를 점검해보면 ERROR_IO_PENDING이 리턴된다. BOOL GetOverlappedResult( HANDLE LPOVERLAPPED LPDWORD BOOL hFile, lpOverlapped, lpNumberOfBytesTransferred, bWait ); – 다음 함수로 입출력 진행 상태를 언제든지 확인할 수 있다. – lpNumberOfBytesTransferred : 현재까지 입출력한 바이트 수 – bWait : 입출력이 완료될 때까지 대기할 것인가를 지정 #include <windows.h> #include "resource.h" #include <process.h> #include <commctrl.h> BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ; return TRUE; } typedef struct param { HWND hButton; HWND hProgress; }PARAM; void DispErrorMessage() { DWORD ErrorCode = GetLastError(); char errMsg[1024]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,ErrorCode,0,errMsg,1204,NULL); MessageBox(NULL,errMsg,"",MB_OK); } BOOL GetFileName(char temp[]) { strcpy(temp,"123.txt"); OPENFILENAME ofn; ZeroMemory(&ofn,sizeof(ofn)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = NULL; ofn.lpstrFilter = "모든 파일(*.*)\0*.*\0텍스트 파일(*.txt)\0*.txt\0\0\0"; ofn.lpstrFile = temp; ofn.nFilterIndex = 2; ofn.nMaxFile = 256; ofn.Flags = OFN_EXPLORER | OFN_ALLOWMULTISELECT | OFN_ENABLESIZING ; if (GetOpenFileName(&ofn) == NULL) return TRUE; else return FALSE; } unsigned __stdcall MyThreadProc( void * pArguments ) { PARAM * pParam = (PARAM *)pArguments; EnableWindow(pParam->hButton,FALSE); char temp[256]; if (GetFileName(temp)) { return FALSE; } HANDLE hReadFile = CreateFile(temp,GENERIC_READ,NULL,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL); if (hReadFile == INVALID_HANDLE_VALUE) { DispErrorMessage(); return FALSE; } unsigned long dFileSize = 0; unsigned long dFileSizeHigh = 0; dFileSize = GetFileSize(hReadFile,&dFileSizeHigh); BYTE * pData = (BYTE *)malloc(dFileSize); BYTE * pBuffer = (BYTE *)malloc(1024*1024); SendMessage(pParam->hProgress,PBM_SETRANGE32,0,dFileSize); SendMessage(pParam->hProgress,PBM_SETPOS,0,0); DWORD ReadLen = 0; OVERLAPPED ov; memset(&ov,0,sizeof(OVERLAPPED)); ov.Offset = 0; ov.OffsetHigh = 0 ; ov.hEvent = NULL; int nTotalRead = 0; do { ReadLen = 0; if (ReadFile(hReadFile,pBuffer,1024*1024,&ReadLen,&ov) == FALSE) { DispErrorMessage(); } WaitForSingleObject(hReadFile,INFINITE); GetOverlappedResult(hReadFile,&ov,&ReadLen,TRUE); SendMessage(pParam->hProgress,PBM_SETPOS,nTotalRead,0); CopyMemory(pData+nTotalRead,pBuffer,ReadLen); nTotalRead += ReadLen; ov.Offset += ReadLen; }while( dFileSize != nTotalRead ); EnableWindow(pParam->hButton,TRUE); CloseHandle(hReadFile); //free(pData); free(pBuffer); DWORD dWriteLen; memset(&ov,0,sizeof(OVERLAPPED)); WriteFile(hWriteFile,pData,dFileSize,&dWriteLen,&ov); SendMessage(pParam->hProgress,PBM_SETPOS,0,0); DWORD nTotalWrite = 0; do{ DWORD WriteLen = 0; GetOverlappedResult(hWriteFile,&ov,&WriteLen,FALSE); nTotalWrite += WriteLen; SendMessage(pParam->hProgress,PBM_SETPOS,nTotalWrite,0); }while(dFileSize != nTotalWrite); CloseHandle(hWriteFile); free(pData); _endthreadex( 0 ); return 0; } BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hProgress,hButton; static HANDLE hThread; static PARAM param; switch (message) { case WM_INITDIALOG : hProgress = GetDlgItem(hDlg,IDC_PROGRESS1); hButton = GetDlgItem(hDlg,IDC_BUTTON1); return TRUE ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_BUTTON1: { param.hButton = hButton; param.hProgress = hProgress; hThread = (HANDLE)_beginthreadex(NULL,NULL,MyThreadProc,&param,NULL,NULL); } return TRUE; case IDOK : case IDCANCEL : EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } 3. 파일 관리 BOOL CopyFile( LPCTSTR LPCTSTR BOOL lpExistingFileName, lpNewFileName, bFailIfExists ); • lpExistingFileName : 복사 대상 피일명 • lpNewFileName : 새로 만들어질 파일명 • bFailIfExists : 새로 만들어질 파일이 이미 있을 경우의 처리 – FALSE : 기존 파일을 지워 버리고 새로운 파일을 복사한다. – TRUE : 기존 파일이 있을 경우 이 함수는 복사하지 않고 에러 코드를 리 턴한다. BOOL CopyFileEx( LPCTSTR LPCTSTR LPPROGRESS_ROUTINE LPVOID LPBOOL DWORD lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, pbCancel, dwCopyFlags ); 3. 파일 관리 • lpExistingFileName : 복사 대상 피일명 • lpNewFileName : 새로 만들어질 파일명 • lpProgressRoutine : 이 파라미터로 전달한 콜백함수를 주기적으로 호출해 준다. • lpData : Argument to be passed to the callback function. • pbCancel : 복사 중에 중지할 수 있는 기능이 있다. DWORD CALLBACK CopyProgressRoutine( LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber, DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData ); 3. 파일 관리 BOOL MoveFile( LPCTSTR lpExistingFileName, LPCTSTR lpNewFileName ); BOOL DeleteFile( LPCTSTR lpFileName ); DWORD GetFileSize( HANDLE hFile, LPDWORD lpFileSizeHigh ); BOOL CreateDirectory( LPCTSTR LPSECURITY_ATTRIBUTES lpPathName, lpSecurityAttributes ); BOOL RemoveDirectory( LPCTSTR lpPathName ); DWORD GetCurrentDirectory( DWORD nBufferLength, LPCTSTR lpBuffer ); DWORD GetSystemDirectory( LPTSTR lpBuffer, UINT uSize ); DWORD GetWindowsDirectory( LPTSTR lpBuffer, UINT uSize ); 3. 파일 관리 UINT GetDriveType( LPCTSTR lpRootPathName ); – lpRootPathName에 조사 대상 디스크의 루트 디렉토리를 지정 하는 문자열을 준다. NULL이면 현재 디렉토리가 사용된다. 리턴 값 설명 DRIVE_UNKNOWN 알 수 없는 타입이다. DRIVE_NO_ROOT_DIR 루트 디렉토리가 없다. DRIVE_REMOVABLE 이동식 디스크이다. 플로피 디스크 DRIVE_FIXED 고정된 디스트이며, 하드 디스크 DRIVE_REMOTE 네트웍에 연결된 드라이브다. DRIVE_CDROM CD_ROM 드라이브이다. DRIVE_RAMDISK 램디스크이다. #include <windows.h> #include "resource.h" BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ; return TRUE; } BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hList,hButton; switch (message) { case WM_INITDIALOG : hList = GetDlgItem(hDlg,IDC_LIST1); hButton = GetDlgItem(hDlg,IDC_BUTTON1); return TRUE ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_BUTTON1: { char temp[256]; for(char ch = 'a'; ch <= 'z';ch++) { wsprintf(temp,"%c:\\",ch); switch(GetDriveType(temp)) { case DRIVE_UNKNOWN: strcat(temp," : 알 수 없는 타입이다."); SendMessage(hList,LB_ADDSTRING,0,(LPARAM)temp); break; case DRIVE_NO_ROOT_DIR: //strcat(temp," : 루트 디렉토리가 없다."); //SendMessage(hList,LB_ADDSTRING,0,(LPARAM)temp); break; case DRIVE_REMOVABLE: strcat(temp," : 이동식 디스크이다."); SendMessage(hList,LB_ADDSTRING,0,(LPARAM)temp); break; case DRIVE_FIXED: strcat(temp," : 하드 디스크"); SendMessage(hList,LB_ADDSTRING,0,(LPARAM)temp); break; case DRIVE_REMOTE: strcat(temp," : 네트웍에 연결된 드라이브다."); SendMessage(hList,LB_ADDSTRING,0,(LPARAM)temp); break; case DRIVE_CDROM: strcat(temp," : CD_ROM 드라이브이다."); SendMessage(hList,LB_ADDSTRING,0,(LPARAM)temp); break; case DRIVE_RAMDISK: strcat(temp," : 램디스크이다."); SendMessage(hList,LB_ADDSTRING,0,(LPARAM)temp); break; } } } return TRUE; case IDOK : case IDCANCEL : EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } 3. 파일 관리 HANDLE FindFirstFile( LPCTSTR LPWIN32_FIND_DATA lpFileName, lpFindFileData ); BOOL FindNextFile( HANDLE LPWIN32_FIND_DATA hFindFile, lpFindFileData ); BOOL FindClose( HANDLE hFindFile ); – lpFileName : 검색식을 준다. 검색 시작 위치와 검색 대상 파일을 와일드 카드식으로 표현한다. (C:\\Windows\\*.exe) – lpFindFileData : 검색 결과가 구조체에 담겨서 넘어온다. WIN32_FIND_DATA FindData; HANDLE hFindFile = FindFirstFile("C:\\Windows\\*.*",&FindData); BOOL bResult = TRUE; while(bResult) { bResult = FindNextFile(hFindFile,&FindData); SendMessage(hList,LB_ADDSTRING,0,(LPARAM)FindData.cFileName); } FindClose(hFindFile); 3. 파일 관리 HANDLE FindFirstChangeNotification( LPCTSTR lpPathName, BOOL bWatchSubtree, DWORD dwNotifyFilter ); – 윈도우는 특정 디렉토리의 내용이 변경될 때 통지를 해줄 수 있으 며 응용 프로그램은 이 통지를 받았을 때 자신이 가지고 있는 목 록을 갱신하거나 특별한 동작을 할 수 있다. – lpPathName : 감시의 대상이 되는 디렉토리 경로 • 이 객체는 변화가 생기면 신호상태가 되므로 대기 함수와 함께 사용 하면 변화의 시점을 정확하게 통지 받을 수 있다. – bWatchSubtree : 서브 디렉토리까지 검사할 것인지를 지정한다. – dwNotifyFilter : 어떤 변화를 감시할 것인가를 지정하며 다음 플 래그들의 조합으로 지정한다. • • • • • FILE_NAME : 파일명이 변경되었다. 파일 생성, 삭제 DIR_NAME : 디렉토리가 변경되었다. 생성, 삭제 ATTRIBUTE : 속성중 일부가 변경되었다. SIZE : 파일의 크기가 변경되었다. LAST_WRITE : 파일의 최후 기록 시간이 변경되었다. 16. DLL 1. DLL (Dynamic Link Library) – 동적 연결 라이브러리 (DLL) : • Microsoft Windows의 가장 중요한 구조적 요소 중 하나이다. – 라이브러리의 기초 • • • • • DLL은 직접 실행될 수 없다. 메시지를 받지 않는다. 프로그램에서 호출되는 함수들을 가진 별도의 파일이다. 프로그램이 라이브러리 내의 함수들 중 하나를 호출할 때만 동작한다. 확장자가 DLL이면 자동으로 로드 된다. – DLL의 목적 • 다수의 서로 다른 프로그램들에 의해 사용될 수 있는 함수와 자원을 제공 – DllMain • DLL이 실행 파일에 의해 요청되거나 해제될 때 호출된다. • DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) • hInstance : 라이브러리의 인스턴스 핸들 • dwReason : Windows가 DllMain을 호출하는 이유 – DLL_PROCESS_ATTACH – 동적 연결 라이브러리가 프로세스의 주소 공간으로 매핑되어 있음을 나 타낸다. – 프로세스가 수행되는 동안 오직 한번의 호출된다. 1. DLL (Dynamic Link Library) – – – – – – – DLL_PROCESS_DETACH 해당 프로세스에 DLL이 더 이상 필요로 하지 않는다는것을 의미한다. 라이브러리가 자신을 정리한다. DLL_THREAD_ATTACH 추가된 프로세스가 새로운 스레드를 만든다. DLL_THREAD_DETACH 스레드가 종료될 때 Window는 호출한다. – 우선 함수를 제공하는 DLL에서는 자신이 제공하는 함수에 대한 정 보를 밖으로 공개해 놓아야 한다. Export – DLL을 사용하는 클라이언트에서는 어떤 DLL에 있는 어떤 함수를 사용하겠다고 선언해야 한다. Import – __declspec ( extended-decl-modifier-seq ) • 함수에 대한 정보를 제공하는 선언문이며 엑스포트 또는 임포트하는 함수 앞에 수식어로 이문구가 있어야 한다. • extern "C" __declspec(dllexport) int InitHook( HINSTANCE hDll, HWND hWndHost ) • extern "C" typedef __declspec(dllimport) int (*PFNInitHook)( HINSTANCE hDll, HWND hWndHost ); • extern "C" typedef __declspec(dllimport) void (*PFNGetDeadWndTxt)( char *pszBuf, int nMaxBuf ); • extern "C" typedef __declspec(dllimport) void (*PFNReleaseHook)(); 1. DLL (Dynamic Link Library) – extern “C” • mangled name을 만들지 않도록 지정함으로써 C형식으로 함수의 정 보를 공개하도록 한다. • 명시적 연결 m_hInstDll = ::LoadLibrary( "l3t_hook.dll" ); if( !m_hInstDll ) { MessageBox( "l3t_hook.dll 을 찾을 수 없습니다.", "오류" ); return FALSE; } m_pfnInitHook = (PFNInitHook)::GetProcAddress( m_hInstDll, "InitHook" ); m_pfnReleaseHook = (PFNReleaseHook)::GetProcAddress( m_hInstDll, "ReleaseHook" ); m_pfnGetDeadWndTxt = (PFNGetDeadWndTxt)::GetProcAddress( m_hInstDll, "GetDeadW if( (!m_pfnInitHook) || (!m_pfnReleaseHook) ) { MessageBox( "잘못된 dll입니다.", "오류" ); ::FreeLibrary( m_hInstDll ); m_hInstDll = 0; return FALSE; } 17. 소켓의 기초 1. 서버 소켓 • 소켓이 작업하는 방식 – 1. 연결의 기다리는 소켓 : 서버 소켓 – 2. 연결을 시도하는 소켓 : 클라이언트 소켓 • 서버 소켓 – 연결을 시도하는 클라이언트 소켓과의 연결을 구축한다. – 서버 소켓은 데이터의 흐름에 대해서는 신경 쓰지 않는다. 오직 연결만 처리한다. – 데이터의 흐름은 같은 프로그램 내의 다른 소켓이 담당하게 된다. 서버 1. 연결 시도 2. 소켓 생성 클라이언트 (로컬 소켓) 클라이언트 (원격 소켓) 3. 연결 및 데이터 송수신 1. 서버 소켓 • • • • 1. 2. 3. 4. 서버 서버 로컬 서버 소켓이 존재하는 상태에서 원격 소켓이 연결을 시도 소켓은 패킷 송수신을 담당할 로컬 소켓을 생성 소켓과 연결을 시도한 원격 소켓을 연결 로컬 소켓과 원격 소켓 간의 데이터 송수신 – 연결을 시도하는 소켓은 무조건 큐에 들어간다. – 큐에 들어가서 자신이 처리될 순서를 기다린다. – 서버 소켓은 큐에 들어 있는 원격 소켓을 하나씩 처리한다. 1. 서버 소켓 • 서버 소켓 흐름도 소켓 생성 : socket() 결합 : bind() 1회 실행 듣기 : listen () 항상, 참 대기 : Accept() 무한 반복 로컬 소켓 생성 연결구축, 로컬 소켓과 원격 소켓 #include <winsock2.h> #include <windows.h> #include "resource.h" BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { WSADATA wsaData; WSAStartup(MAKEWORD(2,2), &wsaData); DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ; WSACleanup(); return TRUE; } BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hEdit1,hEdit2; static HWND hButton1,hButton2; static SOCKET hServerSock; static SOCKET hClientSock; switch (message) { case WM_INITDIALOG : { hEdit1 = GetDlgItem(hDlg,IDC_EDIT1); hEdit2 = GetDlgItem(hDlg,IDC_EDIT2); hButton1 = GetDlgItem(hDlg,IDC_BUTTON1); hButton2 = GetDlgItem(hDlg,IDC_BUTTON2); EnableWindow(hButton1,FALSE); EnableWindow(hButton2,FALSE); hServerSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in ServerAddr; ZeroMemory(&ServerAddr,sizeof(sockaddr_in)); ServerAddr.sin_addr.s_addr = ADDR_ANY; ServerAddr.sin_family = AF_INET; ServerAddr.sin_port = htons(50000); bind(hServerSock,(sockaddr *)&ServerAddr,sizeof(sockaddr_in)); listen(hServerSock,SOMAXCONN); SetWindowText(hEdit1,"클라이언트 접속을 기다리고 있습니다."); } return TRUE ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_ACCEPT: { sockaddr_in ClientAddr; int nAddrLen = sizeof(ClientAddr); hClientSock = accept(hServerSock,(sockaddr *)&ClientAddr,&nAddrLen); if (hClientSock == INVALID_SOCKET) { int ErrorCode = WSAGetLastError(); char errMsg[1024]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,ErrorCode,0,errMsg,1204,NULL); MessageBox(NULL,errMsg,"",MB_OK); } char temp[256]; wsprintf(temp,"%s 클라이언트 접속 요청",inet_ntoa(ClientAddr.sin_addr)); SetWindowText(hEdit1,temp); EnableWindow(hButton1,TRUE); EnableWindow(hButton2,TRUE); return TRUE; } case IDC_BUTTON1: { char temp[256]; recv(hClientSock,temp,256,NULL); SetWindowText(hEdit2,temp); return TRUE; } case IDC_BUTTON2: { char temp[256]; GetWindowText(hEdit2,temp,256); send(hClientSock,temp,256,NULL); return TRUE; } case IDOK : case IDCANCEL : closesocket(hServerSock); closesocket(hClientSock); EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } 1. 서버 소켓 – accept 함수 • 클라이언트 소켓이 연결을 시도할 때까지 블로킹상태에 빠지기 때 문에 프로그램이 죽은 것처럼 보인다. • 첫 번째 인자 : 대기 모드에 들어가 있는 소켓의 핸들. 반드시 listen 함수 호출에서 성공한 핸들이어야 한다. • 두 번째 인자 : 연결을 시도한 클라이언트 소켓의 번호를 받을 sockaddr_in 구조체의 주소 • 세 번째 인자 : 두 번째 인자로 들어가는 구조체의 크기 • 원격지에서 연결을 시도한 소켓과 연결을 담당한다. • 두 번째 인자를 통하여 넘어온 IP를 확인하여 연결을 허락할지 끊어 야 할지를 결정한다. – 연결을 끊을 때는 closesocket함수를 이용하낟. • 리턴 값 : 원격지 소켓과의 데이터 송수신을 처리할 소켓의 핸들을 반환한다. (accept함수 내부에서 생성) – 실패하면 INVALID_SOCKET 이 리턴된다. • accept함수가 성공하면 프로그램에는 소켓이 두 개가 존재하게 된 다. 2. 클라이언트(원격) 소켓 – 원격 소켓 • 원격 소켓의 역할은 서버 소켓에 연결을 시도하는 것이다. • 원격 소켓이 서버에 연결하기 위해서는 반드시, 서버의 주소와 포트 번호를 알고 있어야 한다. • 클라이언트 프로그램이 종료되면, 원격 소켓은 자동으로 종료된다. • 소켓은 종료할 때, 자신과 연결된 상대 소켓에게 종료되었다고 알려 준다. 2. 클라이언트(원격) 소켓 • 클라이언트(원격) 소켓 흐름도 소켓 생성 : socket() 서버에 연결 : connect () 실패 예 종료 : closesocket() 1회 실행 연결 ? 성공 작업 완료? 데이터 송수신 send(),receive() 무한 반복 #include <winsock2.h> #include <windows.h> #include "resource.h" BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { WSADATA wsaData; WSAStartup(MAKEWORD(2,2), &wsaData); DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ; WSACleanup(); return TRUE; } BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hEdit1,hEdit2; static HWND hButton1,hButton2; static SOCKET hClientSock; switch (message) { case WM_INITDIALOG : { hEdit1 = GetDlgItem(hDlg,IDC_EDIT1); hEdit2 = GetDlgItem(hDlg,IDC_EDIT2); hButton1 = GetDlgItem(hDlg,IDC_BUTTON1); hButton2 = GetDlgItem(hDlg,IDC_BUTTON2); EnableWindow(hButton1,FALSE); EnableWindow(hButton2,FALSE); hClientSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); } return TRUE ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_CONNECT: { sockaddr_in ServerAddr; ZeroMemory(&ServerAddr,sizeof(sockaddr_in)); ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); ServerAddr.sin_family = AF_INET; ServerAddr.sin_port = htons(50000); connect(hClientSock,(sockaddr *)&ServerAddr,sizeof(ServerAddr)); SetWindowText(hEdit1,"서버에 접속되었습니다."); EnableWindow(hButton1,TRUE); EnableWindow(hButton2,TRUE); return TRUE; } case IDC_BUTTON1: { char temp[256]; recv(hClientSock,temp,256,NULL); SetWindowText(hEdit2,temp); return TRUE; } case IDC_BUTTON2: { char temp[256]; GetWindowText(hEdit2,temp,256); send(hClientSock,temp,256,NULL); return TRUE; } case IDOK : case IDCANCEL : closesocket(hClientSock); EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } 3. TCP • UDP – 연결이 존재하지 않는다. – 최선형 (best effort) 프로토콜로 최선을 다해서 데이터가 도착하 도록 노력은 하지만, 도착하지 않아도 책임은 지지 않는다. – 도착하지 않았을 때의 책임은 각각의 프로그램에서 담당한다. – 연결이 존재하지 않기 때문에 각각의 패킷은 서로 연관성이 없어 야 한다. – 전송 패킷은 반드시 한 번에 전송되어야 한다. • TCP의 경우는 여러 번에 걸쳐 전송될 수도 있다. • TCP – TCP프로토콜은 가상으로 연결된 상태이다. – 한 번에 전송한 패킷이 한번에 전송될 수도 있고 여러 번에 걸쳐 서 전송될 수도 있다. 3. TCP – 여러 번에 걸쳐서 전송된 패킷이 한번에 전송될 수도 있다. – 연결이 된 이후부터 연결을 닫을 때까지 전송한 데이터가 모두 하 나의 데이터이다. – TCP에서는 어디서부터 어디까지가 의미 있는 하나의 패킷인지 확 인하는 작업이 필수적이다. – 전송 측과 수신 측 모두 두 개의 버퍼를 사용한다. • 애플리케이션 버퍼, 전송 또는 수신을 위한 소켓 라이브러리 버퍼 • send 함수를 호출했다고 해서 패킷이 실제로 상대 소켓으로 전달되 었다고 가정해서는 안 된다. • send함수는 소켓 라이브러리의 버퍼로 데이터를 옮겨놓는 순간 반환 된다. • 만약에 소켓 라이브러리 버퍼가 꽉 차면 블로킹 상태가 된다. • 상대 소켓으로 데이터를 전송하고 나서 버퍼에 여유가 생겨서 send 함수가 모든 데이터를 이동하면, 그때 블로킹이 풀린다. – TCP 프로토콜에서 연결이 구축되어 있는 동안 전송되는 패킷은 모두 연속되어 있다. 패킷을 처리할 때는, 각각의 패킷 길이만큼 잘라서 처리해야 한다. 4. 패킷 – 패킷은 크게 두 부분으로 나뉜다. ( 헤더와 데이터 ) – 헤더에는 반드시 전체 패킷의 크기가 들어가야 한다. • 한 패킷을 잘라 오기 위하여 – 패킷 헤더는 일반적으로 어떠한 패킷이던지 동일한 길이로 구성 한다. 헤더 패킷 전체 길이 (4byte) 식별자 (4byte) 로그인 24 LOGIN 회원ID (8byte) PassWord (8byte) 파일요청 80 FILE_REQUEST 회원ID (8byte) 파일의 HashKey (64byte) 파일 전송 파일길이+8 FILE_TRANSFER 파일내용 가변길이 18. 소켓 초기화 1. WSAStartup함수 – 윈도우 소켓을 초기화한다. – Ws2_32.dll함수를 응용 프로그램의 영역으로 로드한다. WSADATA wsaData; WSAStartup(MAKEWORD(2,2), &wsaData); – WSADATA구조체 변수는 WSAStartup함수가 반환하는 윈도우 소 켓의 세부 정보를 가져 온다. – 첫 번째 인자에는 소켓 라이브러리의 버전을 저장한다. • 하위 바이트에는 메이저 버전 : 2 • 상위 바이트에는 마이너 버전 : 2 • MAKEWORD(하위, 상위) – MAKEWORD(3,5) : 이 코드는 윈도우 버전 3.5를 의미한다. 2. WSACleanup함수 – 윈도우 소켓을 종료하는 함수이다. – Ws2_32.dll을 사용하지 못하게 한다. WSACleanup(); 3. 메모리 바이트 순서 – 인텔 계열의 CPU는 리틀 엔디안이라는 방식으로 데이터를 메모 리에 저장한다. • 16진수 “2F78”은 782F로 메모리에 저장된다. – 모토로라의 CPU는 빅 엔디안이라는 방식으로 데이터를 메모리에 저장한다. • 16진수 “2F78”은 2F78 – 네트워크 바이트 순서는 빅 엔디언을 이용합니다. • htons 함수 – htons 함수는 “host to network short”의 약자이다. – 호스트 바이트 순서로 되어있는 unsigned short를 네트워크 바이 트 순서로 변환한다. – 이 함수는 소켓 프로그래밍에서 포트 번호를 변환하기 위해서 사 용한다. • ntohs 함수 – ntohs 함수는 “network to host short”의 약자이다. – 네트워크 바이트 순서로 되어 있는 unsigned short자료형을 호스 트 바이트 순서로 변환한다. 3. 메모리 바이트 순서 • htonl함수 – htonl함수는 “host to network long”의 약자이다. – 이 함수는 호스트 바이트 순서로 된 unsigned long자료형을 네트 워크 바이트 순서로 변환한다. – 이 함수는 소켓 프로그래밍에서 IP주소를 변환하기 위해 사용한다. • ntohl함수 – ntohl함수는 “network to host long”의 약자이다. – 네트워크 바이트 순서로 된 unsigned long자료형을 호스트 바이 트 순서로 변환한다. • inet_ntoa함수 – 4바이트로 된 IP주소를 도트 표기법에 기반한 문자열로 변환한다. – 클라이언트의 주소를 문자열로 변환해서 보여줄 때 사용 • inet_addr함수 – inet_addr함수는 internet address의 약자이다. – 도트 표기법으로 되어 있는 주소 문자열을 4바이트 IP주소로 변환 19. 파일 전송 1. TransmitFile함수 BOOL TransmitFile( SOCKET HANDLE DWORD DWORD LPOVERLAPPED LPTRANSMIT_FILE_BUFFERS DWORD hSocket, hFile, nNumberOfBytesToWrite, nNumberOfBytesPerSend, lpOverlapped, lpTransmitBuffers, dwFlags ); – hSocket : 파일을 보낼 소켓의 핸들 – hFile : 전송할 파일의 핸들 – nNumberOfBytesToWrite : 전송할 양으로 파일 전체를 전송할 때 는 0을 사용한다. – nNumberOfBytesPerSend : 한 번에 전송할 패킷의 크기. 패킷의 크기를 시스템에 맡길 경우, 0을 사용한다. – lpOverlapped : 중첩 입출력 구조체로 비동기 작업을 수행할 수 있도록 한다. – lpTransmitBuffers : 파일을 전송하기 전에 전송할 헤더와 전송 후 에 전송할 테일을 가리키는 구조체 포인터이다. #include #include #include #include #include <winsock2.h> <windows.h> "resource.h" <commctrl.h> <Mswsock.h> BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { WSADATA wsaData; WSAStartup(MAKEWORD(2,2), &wsaData); DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ; WSACleanup(); return TRUE; } void DispErrorMessage() { DWORD ErrorCode = GetLastError(); char errMsg[1024]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,ErrorCode,0,errMsg,1204,NULL); MessageBox(NULL,errMsg,"",MB_OK); } BOOL GetFileName(char temp[]) { strcpy(temp,"123.txt"); OPENFILENAME ofn; ZeroMemory(&ofn,sizeof(ofn)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = NULL; ofn.lpstrFilter = "모든 파일(*.*)\0*.*\0텍스트 파일(*.txt)\0*.txt\0\0\0"; ofn.lpstrFile = temp; ofn.nFilterIndex = 2; ofn.nMaxFile = 256; ofn.Flags = OFN_EXPLORER | OFN_ALLOWMULTISELECT | OFN_ENABLESIZING ; return GetOpenFileName(&ofn); } typedef struct Tansmitstruct { char pFileName[256]; int nFileSize; }TRANSMITSTRUCT; BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hEdit1; static HWND hButton1; static SOCKET hServerSock; static SOCKET hClientSock; switch (message) { case WM_INITDIALOG : { hEdit1 = GetDlgItem(hDlg,IDC_EDIT1); hButton1 = GetDlgItem(hDlg,IDC_BUTTON1); EnableWindow(hButton1,FALSE); hServerSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in ServerAddr; ZeroMemory(&ServerAddr,sizeof(sockaddr_in)); ServerAddr.sin_addr.s_addr = ADDR_ANY; ServerAddr.sin_family = AF_INET; ServerAddr.sin_port = htons(50000); bind(hServerSock,(sockaddr *)&ServerAddr,sizeof(sockaddr_in)); listen(hServerSock,SOMAXCONN); SetWindowText(hEdit1,"클라이언트 접속을 기다리고 있습니다."); } return TRUE ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_ACCEPT: { sockaddr_in ClientAddr; int nAddrLen = sizeof(ClientAddr); hClientSock = accept(hServerSock,(sockaddr *)&ClientAddr,&nAddrLen); if (hClientSock == INVALID_SOCKET) { int ErrorCode = WSAGetLastError(); char errMsg[1024]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,ErrorCode,0, MessageBox(NULL,errMsg,"",MB_OK); } char temp[256]; wsprintf(temp,"%s 클라이언트 접속 요청",inet_ntoa(ClientAddr.sin_addr)); SetWindowText(hEdit1,temp); EnableWindow(hButton1,TRUE); return TRUE; } case IDC_BUTTON1: { char TransFileName[256]; if (GetFileName(TransFileName) == FALSE) { return TRUE; } char temp[256]; wsprintf(temp,"%s 파일을 전송합니다.",TransFileName); SetWindowText(hEdit1,temp); HANDLE hFile = CreateFile(TransFileName,GENERIC_READ,FILE_SHARE_READ,NULL,OP if (hFile == INVALID_HANDLE_VALUE) { DispErrorMessage(); return TRUE; } BY_HANDLE_FILE_INFORMATION fileinfo; GetFileInformationByHandle(hFile,&fileinfo); char FileName[256]; char FileExt[256]; _splitpath(TransFileName,NULL,NULL,FileName,FileExt); strcat(FileName,FileExt); TRANSMITSTRUCT transmitstruct; transmitstruct.nFileSize = fileinfo.nFileSizeLow; strcpy(transmitstruct.pFileName,FileName); TRANSMIT_FILE_BUFFERS TransBuf; ZeroMemory(&TransBuf,sizeof(TRANSMIT_FILE_BUFFERS)); char pTailMsg[32] = "End Of File"; TransBuf.Head = &transmitstruct; TransBuf.HeadLength = sizeof(transmitstruct); TransBuf.Tail = pTailMsg; TransBuf.TailLength = sizeof(pTailMsg); BOOL bTrans = TransmitFile(hClientSock,hFile,0,0,NULL,&TransBuf,0); if (bTrans == FALSE) { DispErrorMessage(); } CloseHandle(hFile); SetWindowText(hEdit1,"파일 전송을 완료했습니다."); return TRUE; } case IDOK : case IDCANCEL : closesocket(hServerSock); closesocket(hClientSock); EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } #include #include #include #include <winsock2.h> <windows.h> <commctrl.h> "resource.h" BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { WSADATA wsaData; WSAStartup(MAKEWORD(2,2), &wsaData); DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ; WSACleanup(); return TRUE; } void DispErrorMessage() { DWORD ErrorCode = GetLastError(); char errMsg[1024]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,ErrorCode,0,errMsg,1204,NULL); MessageBox(NULL,errMsg,"",MB_OK); } typedef struct Tansmitstruct { char pFileName[256]; int nFileSize; }TRANSMITSTRUCT; BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hEdit1; static HWND hButton1,hProgress; static SOCKET hClientSock; switch (message) { case WM_INITDIALOG : { hEdit1 = GetDlgItem(hDlg,IDC_EDIT1); hButton1 = GetDlgItem(hDlg,IDC_BUTTON1); hProgress = GetDlgItem(hDlg,IDC_PROGRESS1); EnableWindow(hButton1,FALSE); hClientSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); } return TRUE ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_CONNECT: { sockaddr_in ServerAddr; ZeroMemory(&ServerAddr,sizeof(sockaddr_in)); ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); ServerAddr.sin_family = AF_INET; ServerAddr.sin_port = htons(50000); connect(hClientSock,(sockaddr *)&ServerAddr,sizeof(ServerAddr)); SetWindowText(hEdit1,"서버에 접속되었습니다."); EnableWindow(hButton1,TRUE); return TRUE; } case IDC_BUTTON1: { SetWindowText(hEdit1,"서버에서 파일을 수신 중입니다."); SendMessage(hProgress,PBM_SETRANGE32,0,0); SendMessage(hProgress,PBM_SETPOS,0,0); TRANSMITSTRUCT transstruct; int nTotalSize = sizeof(TRANSMITSTRUCT); int nTotalRecv = 0; do{ int nReceived = recv(hClientSock,(char *)(&transstruct+nTotalRecv),nTo nTotalRecv += nReceived; }while(nTotalSize != nTotalRecv); SendMessage(hProgress,PBM_SETRANGE32,0,transstruct.nFileSize); SendMessage(hProgress,PBM_SETPOS,0,0); char temp[256]; wsprintf(temp,"%s(크기 : %d k)파일 수신 중 입니다.",transstruct.pFileName,transstruct.n SetWindowText(hEdit1,temp); HANDLE hFile = CreateFile(transstruct.pFileName,GENERIC_WRITE,FILE_SHARE_READ if (hFile == INVALID_HANDLE_VALUE) { DispErrorMessage(); return TRUE; } BYTE pFileBuf[1024]; nTotalSize = transstruct.nFileSize; nTotalRecv = 0; do{ DWORD dwByteRead; if ( (nTotalSize-nTotalRecv) > sizeof(pFileBuf)) dwByteRead = sizeof(pFileBuf); else dwByteRead = nTotalSize-nTotalRecv; int nReceived = recv(hClientSock,(char *)&pFileBuf,dwByteRead,0); if (nReceived == SOCKET_ERROR) { DispErrorMessage(); CloseHandle(hFile); return TRUE; } nTotalRecv += nReceived; DWORD dwByteWritten = 0; WriteFile(hFile,pFileBuf,dwByteRead,&dwByteWritten,NULL); SendMessage(hProgress,PBM_SETPOS,nTotalRecv,0); }while(nTotalSize != nTotalRecv); CloseHandle(hFile); nTotalSize = 32; nTotalRecv = 0; do{ int nReceived = recv(hClientSock,(char *)&pFileBuf+nTotalRecv,nTotalSiz nTotalRecv += nReceived; }while(nTotalSize != nTotalRecv); SetWindowText(hEdit1,"파일 수신을 완료했습니다."); return TRUE; } case IDOK : case IDCANCEL : closesocket(hClientSock); EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } 20. 1 대 1 채팅 #include #include #include #include <winsock2.h> <windows.h> <commctrl.h> "resource.h" #define WM_SOCKTEVENT WM_USER+100 void DispErrorMessage() { DWORD ErrorCode = GetLastError(); char errMsg[1024]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,ErrorCode,0,errMsg,1204,NULL); MessageBox(NULL,errMsg,"",MB_OK); } BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); HINSTANCE hInst; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { WSADATA wsaData; WSAStartup(MAKEWORD(2,2), &wsaData); hInst = hInstance; HMODULE hMod = LoadLibrary("RICHED32.DLL"); InitCommonControls(); DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ; FreeLibrary(hMod); WSACleanup(); return TRUE; } BOOL CALLBACK DlgProc2 (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hIPCtrl; static HWND hRadio1,hRadio2; switch (message) { case WM_INITDIALOG : { hIPCtrl = GetDlgItem(hDlg,IDC_IPADDRESS1); hRadio1 = GetDlgItem(hDlg,IDC_RADIO1); hRadio2 = GetDlgItem(hDlg,IDC_RADIO2); SendMessage(hRadio1,BM_SETCHECK,TRUE,NULL); EnableWindow(hIPCtrl,FALSE); } return TRUE ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_RADIO1: EnableWindow(hIPCtrl,FALSE); return TRUE ; case IDC_RADIO2: EnableWindow(hIPCtrl,TRUE); return TRUE ; case IDOK : case IDCANCEL : if (SendMessage(hRadio1,BM_GETCHECK,NULL,NULL)) { EndDialog (hDlg, 0) ; } else { DWORD nAddress; int nIP = (int)SendMessage(hIPCtrl,IPM_GETADDRESS,0,(LPARAM)&nAddress); if (nIP != 4) MessageBox(hDlg,"IP주소를 모두 입력하세요",NULL,MB_OK); else EndDialog (hDlg, nAddress) ; } return TRUE ; } break ; } return FALSE ; } BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hRichEdit1,hEdit1; static HWND hButton1; static SOCKET hClientSock; static SOCKET hServerSock; switch (message) { case WM_INITDIALOG : { hEdit1 = GetDlgItem(hDlg,IDC_EDIT1); hRichEdit1 = GetDlgItem(hDlg,IDC_RICHEDIT1); hButton1 = GetDlgItem(hDlg,IDC_BUTTON1); EnableWindow(hButton1,FALSE); UpdateWindow(hDlg); DWORD dwIP = DialogBox(hInst,MAKEINTRESOURCE(IDD_DIALOG2), NULL, DlgProc2) ; if (dwIP == 0) { hServerSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in ServerAddr; ZeroMemory(&ServerAddr,sizeof(sockaddr_in)); ServerAddr.sin_addr.s_addr = ADDR_ANY; ServerAddr.sin_family = AF_INET; ServerAddr.sin_port = htons(50000); bind(hServerSock,(sockaddr *)&ServerAddr,sizeof(sockaddr_in)); listen(hServerSock,SOMAXCONN); WSAAsyncSelect(hServerSock,hDlg,WM_SOCKTEVENT,FD_ACCEPT); SetWindowText(hDlg,"서버"); } else { hClientSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in ServerAddr; ZeroMemory(&ServerAddr,sizeof(sockaddr_in)); ServerAddr.sin_addr.s_addr = htonl(dwIP); ServerAddr.sin_family = AF_INET; ServerAddr.sin_port = htons(50000); connect(hClientSock,(sockaddr *)&ServerAddr,sizeof(ServerAddr)); WSAAsyncSelect(hClientSock,hDlg,WM_SOCKTEVENT,FD_READ|FD_CLOSE); SetWindowText(hDlg,"클라언트"); } EnableWindow(hButton1,TRUE); } return TRUE ; case WM_SOCKTEVENT: { int nEvent = LOWORD(lParam); int nError = HIWORD(lParam); switch(nEvent) { case FD_ACCEPT: { sockaddr_in ClientAddr; int nAddrLen = sizeof(ClientAddr); hClientSock = accept(hServerSock,(sockaddr *)&ClientAddr,&nAddrLen closesocket(hServerSock); hServerSock = INVALID_SOCKET; WSAAsyncSelect(hClientSock,hDlg,WM_SOCKTEVENT,FD_READ|FD_CL } break; case FD_READ: { char temp[256]; int nReceived = recv(hClientSock,temp,256,NULL); SetWindowText(hRichEdit1,temp); } break; case FD_CLOSE: { closesocket(hClientSock); hClientSock = INVALID_SOCKET; MessageBox(hDlg,"상대방에서 연결을 종료했습니다.",NULL,MB_OK); } break; } } return TRUE; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_BUTTON1: { char temp[256]; GetWindowText(hEdit1,temp,256); send(hClientSock,temp,256,NULL); } return TRUE ; case IDOK : case IDCANCEL : closesocket(hClientSock); EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; }