Windows Programming
차례 1. Overview 2. 프로그램의 구조와 메시지 3. Drawing 4. Input Message 5. Control 6. Resource 7. Printing 8. MDI 9. DLL 10. Memory, File I/O, Clipboard 11. Process & Thread 12. IPC 13. Synchronization 14. Network Programming
1. Overview
Getting Started (1) 주 교재 부 교재 프로그래밍 환경 “Programming Windows”, Charles Petzold, Microsoft Press “Windows application Programming Interface”, 김상형 부 교재 “Microsoft Visual C++ Bible 6.0”, 이이표, 김병세, 삼양출판사 “VC++ 와 윈도우 프로그래밍”, 김주호 “Visual C++ Programming Bible”, 이상엽, 영진 출판사 프로그래밍 환경 Microsoft Visual C++ 6.0 MSDN Platform SDK(Software development kit) or MFC
Getting Started (2) 윈도우의 구성 DLL Windows의 대부분 Windows 98 : \Windows\System Windows NT : \Winnt\System ,\Winnt\System32 Windows의 대부분 Kernel : KERNEL32.dll OS의 핵심 부분으로 메모리 관리, 파일 입출력, 프로그램의 로드와 실행 등 OS의 기본 기능 수행 User : USER32.dll 윈도우, 다이얼로그, 메뉴 등 사용자 인터페이스 관리 GDI : GDI32.dll 화면, 프린터 등에 대한 출력을 담당하며, 펜, 브러시, 폰트 등의 GDI 오브젝트를 관리
Getting Started (3) OS가 제공하는 서비스 Windows Programming Tools User : shell service ex. 도스의 command.com 혹은 유닉스나 리눅스의 shell Programmer : API(Application Programming Interface) 응용 프로그램을 만들기 위해서 제공되는 함수의 집합 Win16(윈도우즈3.x) vs. Win32 API(윈도우즈95~) Windows Programming Tools SDK(Software Development Kit) API 함수를 이용 단점 : 프로그램의 골격 및 GUI를 위한 코드를 프로그래머가 모두 작성해야 함 단순 작업이 반복되고 프로그램이 커질수록 관리가 어려움
Getting Started (4) RAD (Rapid Application Development) Visual Basic, PowerBuilder, Delphi 단점 : 저수준의 제어가 어려움 MFC(Microsoft Foundation Class Library) 함수가 아니라 유용한 클래스의 집합 기본적인 프로그램 골격 및 GUI 클래스 제공 WIN32 API를 직접 이용하여 저수준의 제어 가능
2. 프로그램의 구조와 메시지
Dos vs. Windows Dos 절차적 혹은 순차적 프로그램 프로그래머가 프로그램 진행을 전적으로 제어 원하는 서비스가 있으면 프로그램이 도스 시스템을 호출 int main(int argc, char * argv[]) { ……………………. while (1) { char ch = bioskey(); keycheck(ch); } void keycheck(char ch) { …………………………. switch (ch) case ‘X’: }
Dos vs. Windows Windows Event-driven or message-driven 윈도우 시스템과 프로그래머가 프로그램의 진행을 분담해서 처리 윈도우 시스템은 이벤트를 감지하여 프로그램에 전달 프로그래머는 관심 있는 메시지만 처리하면 됨 example 사용자 입력 장치 드라이버 메시지 큐 응용 프로그램 윈도우 시스템 장치 드라이버 출력
Dos vs. Windows 출력장치 device driver 프로그램 device driver message queue 입력장치 프로그램
구성방식 꼭 알아야 할 요소들 Event & Message Message queue Message loop Window Proc Handle Instance Resource Hardware 접근방식 비트 연산자
필수적인 이해요소 (1) Event & Message Message Queue 윈도우 OS가 감지, 해당 프로그램으로 메시지를 전달 이때 전달되는 메시지는 표준화 되어있는 상수 값 (winuser.h) Message Queue Message가 저장 되는 곳 FIFO System message Queue & Program message Queue RIT(Raw Input Thread) 해당 프로그램의 Message Queue로 전달
메시지큐 윈도우즈OS device 프로그램 driver 메시지큐 (메모장) 메모장 프로그램 시스템 분배기 시스템 device (RIT) 시스템 메시지큐 device driver 입력장치 프로그램 메시지큐 (그림판) 그림판 device driver
필수적인 이해요소 (2) Message Loop Window Procedure Message queue에 어떤 message가 들어왔는지를 지속적으로 감시하고 분석해 주는 부분 Window Procedure Callback : 운영체제에서 호출하는 함수 Message loop에서 해석한 message를 구체적으로 처리하는 기능을 수행 구조 : 간단한 switch.. , case.. 문의 집합 LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_LBUTTONDOWN: case WM_DESTROY: …………………………………………… }
필수적인 이해요소 (3) Handle Dos Windows 데이터를 참조하거나 조작하기 위해 pointer 를 이용 ex) fopen() Windows 데이터를 참조하거나 조작하기 위해 handle 사용 프로그램에서 현제 사용중인 객체들을 식별하기 위해 윈도우 OS가 부여하는 고유 번호 윈도우를 만들거나, 파일을 열면 OS 가 부여 해당 윈도우나 파일을 다시 참조시 핸들을 사용 32 bit 정수형, 중복되지 말아야 된다. 핸들형 변수를 만들어 핸들을 대입 받아 사용
Handle을 사용하는 이유 device driver 윈도우핸들 기존의 참조 대상 이동된 참조 대상 프로그램 device 핸들테이블 메모리주소
Handle 종류 Data Type 의미 HWND 윈도우에 대한 핸들 HCURSOR 커서에 대한 핸들 HICON 아이콘에 대한 핸들 HINSTANCE 프로그램 자신의 ‘인스턴스’에 대한 핸들 HDC 장치 컨택스트에 대한 핸들 HMENU 메뉴에 대한 핸들
필수적인 이해요소 (4) Instance 프로그램 Code segment & Data segment 모든 프로그램은 같은 코드를 실행 프로그램에 따라 데이터는 달라짐 실제 메모리 상에 할당된 객체 Module instance & Data Instance 모듈 인스턴스 데이터 인스턴스1 데이터 인스턴스2
필수적인 이해요소 (5) Resource 사용자 인터페이스를 구성하는 자원들의 정적 data 메뉴, 커서, 아이콘 Resource Scrip에 의해 정의 자체 compile 과정 (.res) 메모리를 효율적으로 사용하기 위해 사용 필요한 시점에 파일로부터 로딩 DISCARDABLE 설정 가능
필수적인 이해요소 (6) 하드웨어 운용 방식 Dos Windows 비디오나 프린터에 출력하기 위해서는 Port, Bios, DMA 등을 통해 직접 제어 하드웨어 장치에 종속된 프로그램 장치의 종류와 모델에 따른 프로그래밍 필요 Windows 디바이스 드라이버를 윈도우가 내장 하드웨어에 독립적인 프로그램 DC를 이용하여 GDI(Graphic Device Interface)를 호출 장치의 종류나 모델에 상관없이 1번만 프로그램을 작성하면 됨
필수적인 이해요소 (7) 비트 OR 연산자 함수에 인수 전달시, 여러 개의 옵션을 묶어 전달 옵션들의 실제값은 2의 제곱승으로 정의 옵션별로 비트 자리가 정해져 있다. 사용자는 매크로 값만 알고 있으면 된다. ex) 윈도우 스타일, 문자열 출력 방식…. WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
Window Program의 파일구성 Code file : C header와 source file Resource file .res file 실행 중에 내용이 변하지 않는 데이터 저장 bitmap, icon, cursor, string, dialog box, menu,.. .def file code와 data의 메모리 속성 stack과 heap의 크기 지정 Makefile .dsw file, cf) Visual C++ 4.X는 확장자 .mdp
표기법 Ex) Char szAddress[10]; 접두어(Prefix) 의미 BM_ 버튼 메시지 CB_ 콤보 박스 메시지 DM_ 다이얼로그 메시지 EM_ 에디트 컨트롤 메시지 LB_ 리스트 박스 메시지 WM_ 윈도우 메시지 Ex) Char szAddress[10]; 접두어(Prefix) Data Type a 배열 (array) b bool ch 문자 cb 바이트 개수 dw 부호없는 long 형 정수 h 핸들 sz Null로 끝나는 문자열 I integer p 포인터 접두어(Prefix) 의미 BS_ 버튼 스타일 CBS_ 콤보 박스 스타일 DS_ 다이얼로그 스타일 ES_ 에디트 컨트롤 스타일 LBS_ 리스트 박스 스타일 WS_ 윈도우 스타일
Type Type BOOL : int로 부터 재정의 LPSTR : char* HWND : 윈도우 식별자 HINSTANCE : 인스턴스 식별자, UINT : 4 bytes HANDLE : UINT를 재정의 WPARAM : 메시지의 부가정보, UINT를 재정의 LPARAM : 메시지의 부가정보, LONG을 재정의
Window Program의 구조 (1) MsgQueue WinMain WinProc 가져온 메시지 제일 먼저 전달된 메시지 WM_PAINT WM_KEYDOWN WM_SIZE DefWindowProc WinMain Message Handler WinProc 제일 먼저 전달된 메시지 처리되지 않은 메시지 가져온 메시지 MsgQueue
Window Program의 구조 (2) Int winapi WinMain(…) { InitApplication(); // 클래스 등록 InitInstance(); // 창 생성 while (GetMessage(&msg,NULL,0,0) TranslateMessage(&msg); DispatchMessage(&msg); } //메시지를 전송 받으면 실행, 실행이 끝나면 메시지를 전송한 곳으로 되돌아 간다. LRESULT WndProc(…) { <전송받은 메시지에 따른 처리를 수행> }
#include “windows.h” // SDK의 API의 각종 상수, 구조체가 정의된 헤더 파일 #include “generic.h” // 이 프로그램에서 사용한 상수가 정의되고 함수 선언 HINSTANCE hInst; // 인스턴스 핸들을 기억 HWND hMainWnd; // 메인 윈도우 핸들을 기억 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; if(!hPrevInstance) if(!InitApplication(hInstance)) // 윈도우 클래스를 등록 return FALSE; hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장 if(!InitInstance(hInstance, nCmdShow)) // 메시지 루프에 진입한다. While(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam;
BOOL InitApplication(HINSTANCE hInstance) { WNDCLASS wc; // WNDCLASS는 윈도우 클래스의 등록에 필요한 구조체 wc.lpfnWndProc = MainWndProc; // 윈도우 프로시져 wc.cbClsExtra = 0; // 클래스 여분 바이트 wc.cbWndExtra = 0; // 윈도우 여분 바이트 wc.hInstance = hInstance; // 인스턴스 핸들 wc.hIcon = LoadIcon(NULL, IDI_APPLIATION); // 아이콘 지정 wc.hCursor = LoadCursor(NULL, IDC_ARROW); // 커서 지정 wc.hbrBackground = GetStockObject(WHITE_BRUSH); // 배경색 지정 wc.lpszMenuName = “EX1_1Menu”; // 메뉴 지정 wc.lpszClassName = “EX1_1WClass”; // 클래스 이름 지정 return RegisterClass(&wc); // 윈도우 클래스를 등록한다. }
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hMainWnd = hWnd = CreateWindow( “EX1_1WClass”, // 생성하려는 윈도우 클래스 이름 “EX 1-1”, // 윈도우 타이틀 지정 WS_OVERLAPPEDWINDOW, // 윈도우 스타일 CW_USEDEFAULT, // 시작 X좌표 CW_USEDEFAULT, // 시작 Y좌표 CW_USEDEFAULT, // 윈도우의 폭 CW_USEDEFAULT, // 윈도우의 높이 NULL, // 부모 윈도우 핸들 NULL, // 메뉴 핸들 hInstance, // 응용 프로그램 인스턴스 핸들 NULL // 윈도우 작성일 ); if(!hWnd) // 윈도우가 실패했으면, return FALSE; ShowWindow(hWnd, nCmdShow); // 메인 윈도우 형태 결정, WM_PAINT 발생 UpdateWindow(hWnd); // WM_PAINT 메시지를 바로 처리 return TRUE; }
long APIENTRY MainWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { case WM_COMMAND: { // 사용자가 메뉴 항목을 선택할 때 발생하는 메시지 switch(LOWORD(wParam)) { // wParam의 하위 워드에 메뉴 ID case ID_FILE_EXIT: SendMessage(hWnd, WM_CLOSE, 0, 0); break; } case WM_PAINT: { // 사용자 영역이 다시 그려져야 할 때 발생하는 메시지 HDC hDC; PAINTSTRUCT ps; hDC = BeginPaint(hWnd, &ps); TextOut(hDC, 10, 10, “Hello, Everybody”, 16); EndPaint(hWnd, &ps); case WM_DESTROY: // 윈도우가 없어지기 직전에 발생 PostQuitMessage(0); default: return DefWindowProc(hWnd, message, wParam, lParam);
WinMain 분석 WinMain 함수 인자 (1) WNDCLASS 등록 (2) Main Window의 생성 앞으로 사용할 윈도우의 특성을 OS에 알림 (2) Main Window의 생성 앞서 등록한 윈도우를 메인 윈도우로 생성 (3) Message Loop의 진입 윈도우에서 발생한 메시지를 처리하기 위해서 루프에 들어감 함수 인자 의미 hInstance 현재 실행중인 어플리케이션의 인스턴스 핸들 hPrevInstance 동일한 어플리케이션이 실행중일 경우 이전에 실행된 프로그램의 인스턴스 핸들을 나타냄(win32 의 경우 항상 NULL) lpCmdLine 커맨드 라인의 문자열을 가르키는 포인터 nCmdShow 메인 윈도우를 어떻게 출력할 것인지를 결정하는 인자로 ShowWindow()함수 호출시 사용된다.
(1) WNDCLASS 등록 WNDCLASS 구조체 : 윈도우 클래스를 OS에 등록 lpfnWndProc - 메시지를 처리할 Window Procedure의 이름 hInstance - 이 윈도우가 속한 instance handle hIcon - 아이콘을 지정, LoadIcon hCursor - 커서를 지정, LoadCursor hbrBackground - 배경색 지정 lpszMenuName - 메뉴 지정 lpszClassName - 윈도우 이름 RegisterClass API를 이용해서 OS에 등록 클래스 스타일 의미 CS_HREDRAW 사용자 영역의 폭이 변경되면 전체를 다시 그린다. CS_VREDRAW 사용자 영역의 높이가 변경되면 전체를 다시 그린다. CS_NOCLOSE 시스템 메뉴의 Close 항목을 사용할 수 없게 한다. CS_DBCLICKS 윈도우 프로시저에 더블클릭 메시지를 보낸다.
(2) Main Window의 생성& 출력 Window Style Popup & Child WS_HSCROLL WS_VSCROLL WS_POPUP WS_CLIPSIBLINGS WS_CLIPCHILDREN ………. Window Style WS_OVERLAPPED WS_SYSMENU WS_CAPTION WS_MINIMIZEBOX WS_MAXMIZEBOX WS_CHILD WS_VISIBLE WS_BORDER Popup & Child
WS_OVERLAPPEDWINDOW & Client Area #define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED |\ WS_CAPTION |\ WS_SYSMENU |\ WS_THICKFRAME |\ WS_MINIMIZE |\ WS_MAXMIZE) Client Area window border, title bar, menu bar, scroll bar를 제외한 영역
Window border Client Area 사용자 영역의 원점 Title bar Menu bar <사용자 영역>
ShowWindow & UpdateWindow BOOL ShowWindow(HWND hWnd, // handle of window int nCmdShow // show state of window); WM_PAINT 메시지 발생 SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE, SW_SHOW BOOL UpdateWindow( HWND hWnd ); ShowWindow에서 발생한 WM_PAINT 메시지를 처리 SW_HIDE SW_MAXIMIZE SW_MINIMIZE SW_RESTORE SW_SHOW 윈도우를 감춘다. 윈도우를 전체 화면 크기로 만든다. 윈도우를 아이콘화 한다. 이전 상태로 되돌린다. 윈도우를 활성화시킨다.
(3) Message Loop의 진입 GetMessage TranslateMessage DispatchMessage Message queue로부터 메시지를 읽어 온다. WM_QUIT메시지를 받으면 0(False)을 리턴한다. Message loop를 빠져 나오면서, 프로그램을 종료한다. TranslateMessage Message 중 키보드와 관련된 message에 대해서 특정 작업 수행 문자가 입력되었다는 Message (WM_CHAR)를 생성 DispatchMessage Message를 윈도우 프로시저(WndProc)에 전달하는 역할을 한다. OS에게 윈도우 프로시져의 호출을 요청 WM_QUIT Message가 오기 전까지 Loop
MSG 구조체 Typedef struct tagMSG { HWND hwnd; UINT messgae; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } 멤버 의미 Hwnd 메시지를 받을 윈도우 핸들 Message 어떤 종류의 메시지 인가? wParam 전달된 메시지의 부가 정보 lParam time 메시지가 발생한 시간 Pt 메시지가 발생했을 때의 마우스의 위치 마우스 왼쪽 버튼을 누른 경우 1st parameter: hwnd 2nd parameter: WM_LBUTTONDOWN 3rd parameter: Ctrl key나 shift key가 눌렸는지 여부 4th parameter: LOWOD(lPARAM)은 커서의 x좌표 HIWORD(lPARAM)은 커서의 y좌표
Window Procedure LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 해당 윈도우에서 발생한 메시지를 최종적으로 처리 각 메시지마다 추가 정보가 wParam, lParam에 온다. - help참조 ex) WM_KEYDOWN 메시지 nVirtKey = (int) wParam; // virtual-key code lKeyData = lParam; // key data message handler가 없는 메시지는 DefWindowProc함수가 처리한다. WM_CLOSE, WM_ACTIVATE, WM_SYSCOMMAND 등 수십개의 메시지를 디폴트로 처리해준다.
중요한 메시지 (1) WM_PAINT (1) (2) 윈도우의 모양에 변화가 생길 때 발생 가려졌다가 복구 크기가 변경될 때 BeginPaint()를 호출함으로써 시작 EndPaint()를 호출하여 끝냄 (2) (1) (2)에 WM_PAINT 메시지 발생
중요한 메시지 (2) WM_SIZE 윈도우의 크기가 변경될 때 wParam : 메시지가 발생한 이유 lParam : 상위 워드 : 변경된 후의 윈도우 높이 하위 워드 : 변경된 후의 윈도우 폭 플래그 값 SIZE_MAXHIDE 다른 윈도우가 최대화 되어 이 윈도우가 가려졌다 SIZE_MAXMIZED 최대화되었다. SIZE_MAXSHOW 다른 윈도우가 원래 크기로 복구되어 이 윈도우가 드러났다. SIZE_MINIMIZED 최소화 되었다. SIZE_RESTORED 크기가 변경되었다.
중요한 메시지 (3) WM_MOVE 윈도우의 위치가 변경될 때 wParam : 위치 변경 플래그 (SIZE_RESTORED) lParam 상위워드 : 윈도우의 x 좌표 하위워드 : 원도우의 y 좌표
중요한 메시지 (4) WM_DESTROY 사용자의 명령에 의해 Windows가 원도우를 종료하고 있음을 알려준다. 자원을 OS에 반환 PostQuitMessage()를 호출 프로그램의 메시지 큐에 WM_QUIT를 삽입 GetMessage는 0을 반환 WinMain이 메시지 루프를 벗어나게 함 윈도우가 생성될 때 : WM_CREATE -> WM_SIZE -> WM_PAINT 윈도우가 종료될 때 : WM_CLOSE -> WM_DESTROY -> WM_QUIT
실습 간단한 윈도우 프로그램 생성 WndClass의 내용을 변경하여 다음 과정을 수행 윈도우의 스타일 바꾸기 1) 배경색 바꾸기 hbrBackgroud 를 이용 2) 커서 바꾸기 LoadCursor 3) 윈도우 타이틀 바꾸기 윈도우의 스타일 바꾸기 1) 크기 변경 2) 스타일 변경 3) ShowWindow 옵션 변경 WinProc 변경 1) Exit 메뉴를 누르면 메시지 출력 MessageBox()를 이용 2) Exit 메뉴를 눌렀을 때 종료할지 여부를 결정
실습 3) WM_RBUTTONDOWN에 대한 처리 추가 오른쪽 버튼이 눌렸다는 메시지 출력 메시지 상자를 통해 부팅이 가능하도록 수정 ExitWindowEx(EWX_REBOOT, 0) 이용
3. Drawing
GDI (1) GDI (Graphic Device Interface) 란 응용프로그램 실제출력장치 Device Driver gdi32.dll 다양한 출력 장치에 대한 고수준 인터페이스 Device-independency 제공 화면 출력과 프린터 출력 방법 동일 출력 장치에 대한 정보를 분석하여 드라이버를 로드, 호출함으로써 실제 출력 작업을 수행 응용프로그램 GDI Device Driver 실제출력장치 WIN32 API
GDI (2) GDI 개체 StockObject GDI 기본 구성 요소 화면에 그림을 그리거나 문자를 출력할 때 사용하는 개체 펜, 브러쉬, 비트맵, 영역, 글꼴, 팔레트 프로그램이 종료 되어도 GDI 개체는 자동적으로 제거되지 않는다. (프로그램에서 제거해 주어야 한다.) StockObject 사용 빈도가 많아서 OS가 기본으로 제공하는 GDI 오브젝트 ex) BLACK_PEN, BLACK_BRUSH, ANSI_FIXED_FONT 생성하거나 제거할 필요가 없다. GDI 기본 구성 요소 (1) 텍스트(Text) (2) 선과 곡선(Lines and Curves) 직선, 사각형, 타원
GDI (2) GDI 관련 함수 (3) 채워진 영역(Filled Areas) (4) 비트맵(Bitmaps) 브러시 개체를 이용 – 색상, 패턴, 비트맵 이미지 (4) 비트맵(Bitmaps) 디스플레이 장치의 픽셀과 일치하는 직사각형 배열 GDI 관련 함수 Device Context의 속성 관련 함수 Text 관련 출력 함수 도형 관련 출력 함수 그림 도구 변경 함수 Mapping mode 관련 함수
Device Context (1) Device Context 출력 장치에 대한 정보를 나타내는 구조체 윈도우에서 모든 출력 요구는 DC를 통해 이뤄진다. 화면 출력의 경우 device context를 얻는 방법 WM_PAINT BeginPaint - EndPaint WM_PAINT가 아닌 메시지 (클라이언트 영역) GetDC - ReleaseDC WM_NCPAINT GetWindowDC - ReleaseDC 비트맵 출력 CreateCompatibleDC – DeleteDC
Device Context Handle를 얻는다. 독립성 hDC의 사용은 독립성을 유지 다음과 같은 점에 유의. hDC를 정적 변수로 선언해서는 안된다. 윈도우 프로시저의 서로 다른 case문에서 동일한 hDC 사용할 수 없다. 사용이 끝나면 반드시 release한다. Device Context Handle를 얻는다. GDI로 얻은 handle에 출력한다. Device Context를 제거한다. 출력 절차
Device Context의 속성 관련 함수 int GetDeviceCaps(HDC hdc, // device-context handle int nIndex // index of capability to query); Device Context의 속성을 알아낼 때 사용 nIndex의 값과 의미(29개) DRIVERVERSION - GDI Driver 버전 TECHNOLOGY - Device 종류 HORZSIZE - 출력 화면의 폭 (mm단위) VERTSIZE - 출력 화면의 높이(mm단위) HORZRES - 출력 화면의 폭 (pixel 단위) VERTRES - 출력 화면의 높이(pixel 단위) LOGPIXESX - 출력 화면의 가로 방향 DPI LOGPIXESY - 출력 화면의 가로 방향 DPI SIZEPALETTE - System Palette 크기 실습 : 화면의 해상도 출력하기
Text 관련 함수 속성관련 BOOL GetTextMetrics(HDC hdc, LPTEXTMETRIC lptm); 설정된 글꼴에 대한 정보를 구함 typedef struct tagTEXTMETRIC { int tmHeight, int tmAveCharWidth, BYTE tmItalic, BYTE tmUnderlined… } COLORREF SetTextColor(HDC hdc, COLORREF crColor); COLORREF SetBkColor(HDC hdc, COLORREF crColor); UNIT SetTextAlign(HDC hdc, UINT fMode); fMode : 좌표의 위치를 결정(TA_CENTER|TA_LEFT…)
Text 관련 함수 출력함수 BOOL TextOut( HDC hdc, int nXStart, int nYStart, LPCTSTR lpString, int cbString); LPCTSTR : 문자열 포인터 int DrawText( HDC hDC, LPCTSTR lpString, int nCount, LPRECT lpRect, UINT uFormat); 사각형 안에 정렬을 지정해서 (DT_LEFT, DT_VCENTER, DT_NOCLIP) 문자열을 출력할 수 있다. 실습 메뉴를 만들어서 글자색, 배경색, align 바꿔보기 (TextOut, DrawText)
도형 관련 출력 함수(1) CP 가장 기본적인 함수 GDI에서 다음 그래픽이 출력될 위치 내부적으로 기억됨 SetPixel(); GetPixel(); COLORREF SetPixel(HDC hdc, int X, int Y, COLORREF crColor); 색상 RGB Black RGB(0, 0, 0) White RGB(255, 255, 255) Blue RGB(0, 0, 255) Gray RGB(128, 128, 128) Yellow RGB(255, 255, 0)
도형 관련 출력 함수 (2) BOOL MoveToEx(HDC hdc, int X, int Y, POINT lpPoint); CP의 위치를 X,Y로 이동 BOOL LineTo(HDC hdc, int nXEnd, int nYEnd); CP에서 지정한 위치까지 Line BOOL Ellipse(HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect); BOOL Rectangle(HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect); BOOL RoundRect(HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect, int nWidth, int nHeight); 실습 : 메뉴를 누르면 선, 사각형, 타원, 모서리가 둥근 사각형 출력 마우스 버튼을 누르면 그 자리에 사각형 출력
GDI 개체(1) GDI 개체를 사용하는 방법 (1) GDI 개체를 생성 CreatePen, CreateSolidBrush 등 이용 (2) DC를 얻는다. (GetDC나 BeginPaint) (3) 개체를 DC에 등록 SeleteObject 함수 이용 리턴되는 기존 개체는 저장 (4) DC를 사용하여 출력 (5) 이전 개체로 환원 (SelectObject) (6) DC를 OS로 반환 (ReleaseDC나 EndPaint) (7) 생성해서 사용한 개체를 삭제 (DeleteObject)
GDI 개체(2) 펜 (Pen) ※선이나 영역의 경계선을 그릴 때 사용 ※두께, 색상, 스타일 설정 가능 BOOL CreatePen(int nPenStyle, int nWidth, COLORREF crColor); 펜의 스타일 내 용 모 양 PS_SOLID 실선 PS_DASH 파선 PS_DOT 점선 PS_DASHDOT 일점 쇄선 PS_DASHDOTDOT 이점 쇄선 PS_NULL 선을 그리지 않음
GDI 개체(3) 브러쉬 (Brush) ※ 색과 패턴을 이용하여 영역의 내부를 채울 때 사용 (1) CreateSolidBrush : 단일색을 가진 브러쉬 생성 (2) CreateHatchBrush : 패턴을 가진 브러쉬 생성 해치 브러시의 스타일 내 용 모 양 HS_BDIAGONAL 오른쪽에서 왼쪽으로 45도 내려가는 빗금 HS_CROSS 십자가 형태의 빗금 HS_DIAGCROSS X자 형태의 빗금 HS_FDIAGONAL 왼쪽에서 오른쪽으로 45도 내려가는 빗금 HS_HORIZONTAL 수평으로 빗금 HS_VERTICAL 수직으로 빗금
GDI 개체(4) 예제 – 영역을 채운 사각형 출력 HPEN hPen, hOldPen; HBRUSH hBrush, hOldBrush; hPen = CreatePen(PS_DASH, 1, RGB(255, 0, 0)); hBrush = CreateHatchBrush(HS_CROSS, RGB(0, 128, 0)); hdc = GetDC(hWnd); hOldPen = (HPEN)SelectObject(hdc, hPen); hOldBrush = (HBRUSH)SelectObject(hdc, hBrush); Rectangle(hdc, 0, 0, 400, 400); SelectObject(hdc, hOldPen); SelectObject(hdc, hOldBrush); ReleaseDC(hWnd, hdc); DeleteObject(hPen); DeleteObject(hBrush);
GDI 개체(5) 비트맵 Pixel 단위로 표현한 그림 DDB (Device Dependent Bitmap, 장치 종속 비트맵) DDB는 별도의 색상정보(팔레트)가 포함되지 않음 16 컬러로 제한 GDI 비트맵 DIB (Device Independent Bitmap, 장치 독립 비트맵) 이미지에 대한 색상 정보(팔레트)나 해상도, 데이터 압축 등을 포함 하드웨어 종류가 달라져도 변형 없이 출력 가능 24 비트 true color 표현 가능 Palette 오브젝트 응용 프로그램이 필요로 하는 색과 시스템이 제공할 수 있는 색과의 차이를 조정하여 보다 효율적으로 색상을 표현
GDI 개체(6) 메모리 디바이스 컨텍스트 (Memory DC) 비트맵 출력을 위해 필요 메모리 DC를 생성하려면 특정 출력 장치에 대한 DC를 먼저 생성 출력 대상 DC에 대응하는 DC를 메모리에 생성
GDI 개체(7) 비트맵 출력 방법 ① 화면 DC 생성 ② 화면 DC와 호환성을 갖는 메모리 DC 생성 ③ 비트맵을 읽는다. ⑥ 각종 제거 작업을 수행 메모리 DC 복원 및 생성한 메모리 DC 삭제 비트맵 제거 화면 DC release
GDI 개체(8) 비트맵 출력 예제 HDC hMemDC; HBITMAP hBitmap, hOldBitmap; BITMAP bm; hMemDC = CreateCompatibleDC(hdc); hBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP)); hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap); GetObject(hBitmap, sizeof(BITMAP), &bm); BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0, SRCCOPY); SelectObject(hMemDC, hOldBitmap); DeleteObject(hBitmap); DeleteDC(hMemDC); ReleaseDC(hWnd, hdc);
GDI 개체(9) 비트맵 출력 함수 BitBlt(HDC destDC, int destx, int desty, int width, int height, HDC srcDC, int srcx, int srcy, DWORD ROP); StretchBlt : 사이즈를 변경해서 출력 래스터 연산(ROP코드) 논리 연산 설명 SRCCOPY dst = src 그대로 복사 NOTSRCCOPY dst = ~src 반전시켜 복사 SRCAND dst = dst&src AND 연산 결합 SRCPAINT dst = dst|src OR 연산 결합 SRCINVERT dst = dst^src XOR 연산 결합
GDI 개체(10) 래스터 연산 실습 : Bitmap 출력, 화면중앙에 출력, 화면 전체로 확대, 타일로 출력 대상 비트맵 SRCCOPY NOTSRCCOPY SRCPAINT 래스터 연산 원본 비트맵 SRCAND SRCINVERT
Drawing Mode (1) Drawing Mode 도형이 그려질 때 원래 그려져 있던 그림과 새로 그려지는 그림과의 관계를 정의하는 함수 Int SetROP2(HDC hdc, int fnDrawMode); Int GetROP2(HDC hdc);
Drawing Mode (2) 흑백에서의 비트 연산 예 원본 그림 COPY OR AND XOR
Mapping Mode Window & ViewPort Window ViewPort 프로그램에서 사용하는 그래픽 출력함수는 논리적인 좌표 영역을 이용한다. ViewPort 화면의 장치 좌표를 이용한다.
Mapping Mode 관련 함수 Mapping mode Device Context에서 좌표를 해석하는 방법 논리적 단위 사용 관련 API int GetMapMode( HDC hdc); int SetMapMode( HDC hdc, int fnMapMode ); fnMapMode : 맵핑모드 식별자 BOOL LPtoDP( HDC hdc, LPPOINT lpPoints, int nCount ); logical point -> device point BOOL DPtoLP( HDC hdc, LPPOINT lpPoints, int nCount ); device point -> logical point
사용자 영역 매핑모드 단위크기 X축 증가 Y축 증가 MM_TEXT Pixel 오른쪽 아래쪽 MM_LOMETRIC 0.1mm 오른쪽 위쪽 MM_HIMETRIC 0.01mm 오른쪽 위쪽 MM_LOENGLISH 0.01inch 오른쪽 위쪽 MM_HIENGLISH 0.001inch 오른쪽 위쪽 MM_TWIPS 1/1440inch 오른쪽 위쪽 MM_ISOTROPIC 임의적 선택 선택 MM_ANISOTROPIC 임의적 선택 선택 Y좌표 증가(+) X좌표 증가(+) 사용자 영역 원점 Y좌표 감소(-) MM_TEXT MM_HIMETRIC, MM_HIENGLISH
WM_PAINT (1) WM_PAINT가 보내지는 시점 무효영역 바로 다시 그리고 싶을 경우 우선순위가 가장 낮다. 메시지 큐에 대기중인 메시지가 없어야 한다. 무효영역이 있어야 한다. 무효영역 화면의 일부가 변경되어 다시 그려져야 할 부분 운영체제는 최대한 좁은 면적만 무효영역으로 만듬 InvalidateRect(HWND hwnd, RECT *lpRect, BOOL bErase) 해당 작업영역을 무효화 시켜 준다. 바로 다시 그리고 싶을 경우 UpdateWindow() WM_PAINT를 메시지 큐를 거치지 않고 WndProc으로 보냄
WM_PAINT (2) WM_PAINT에서의 화면 복구 1 2 1번 윈도우에 의해 가려졌던 2번 윈도우가 클릭된다. 1번 윈도우에 의해 가려졌던 2번 윈도우가 클릭된다. 가려졌던 영역이 일단 WM_ERASERKGND 메시지에 의해 지워진다. WM_PAINT 메시지에 의해 가려졌던 영역이 복구된다.
WM_PAINT (3) 클리핑 영역 BeginPaint() 운영체제가 실제 그리기에 사용하는 영역 무효영역중에서도 화면에 보이는 가시 영역 BeginPaint()에 의해 계산 BeginPaint() 동작 DC를 발급 받는다. 클리핑 영역을 조사하여 DC에 설정, 운영체제는 이 값을 이용하여 영역 바깥으로 출력되는 것을 잘라낸다. 무효영역을 없앤다. 다시 그려지는 영역에 캐럿이 있다면 잠시 숨기고, EndPaint()에 의해 복구된다. WM_ERASEBKGND 메시지를 보내 배경을 지운다. WM_PAINT 메시지를 보내 작업 영역을 그리도록 한다.
GetDC vs BeginPaint Device Context를 얻고, 화면 출력에 사용 HDC GetDC( HWND hWnd ); Device Context를 얻고, 화면 출력에 사용 ReleaseDC로 Device Context를 제거 HDC BeginPaint( HWND hwnd, LPPAINTSTRUCT lpPaint ); WM_PAINT 메시지에서만 사용 가능 lpPaint로 복구할 영역 정보를 얻을 수 있다. EndPaint로 Device Context를 제거 case WM_PAINT: { HDC hDC; PAINTSTRUCT ps; hDC = BeginPaint(hWnd, &ps); // ps.rcPaint에 복구 영역의 좌표가 들어 있다. 이 부분만 그릴 수 있다면 이상적이다. EndPaint(hWnd, &ps) }
4. Input Message
Introduction Message 하나의 사건을 가리키는 상수 현재 약 200 개의 메시지가 존재 windows.h에 정의 ex)WM_KEYDOWN, WM_MOUSEMOVE, ……..
4.1 기본 입력 메시지 6가지의 입력 메시지 분류 Keyboard, Mouse, Timer 입력 메시지 문자, Scrollbar, Menu 입력 메시지 Note) 키보드, 마우스 : 현재성이 중요 분류 하드웨어적인 이유로 발생하는 메시지 Keyboard, Mouse, Timer Keyboard message or Mouse message를 해석한 메시지 Character, Scrollbar, Menu
Keyboard 입력 메시지(1) 메시지 종류 wParam와 lParam에 부가적인 정보 WM_KEYDOWN, WM_KEYUP WM_SYSKEYDOWN, WM_SYSKEYUP : ALT Key wParam와 lParam에 부가적인 정보 wParam lParam Virtual Keycode(windows.h에 정의) 키의 scan code와 keyboard의 상태 - 비트 0 -15 : 키의 반복 횟수, - 비트 16-23 : 키의 스캔 코드 - 비트 24 : 확장키(1: T, 0: F), 확장키란 ASCII코드가 없는 키 ex) Insert, Delete 키 - 비트 25-26 : 쓰이지 않음 - 비트 27-28 : OS가 사용 - 비트 29 : 1이면 ALT키가 눌린 것, 0이면 ALT키가 눌리지 않은 것 - 비트 30 : 키의 이전 상태, 1이면 키가 눌린 것, 0이면 키가 눌리지 않은 것 - 비트 31 : 1->WM_KEYDOWN, 0->WM_KEYUP
Keyboard 입력 메시지(2) Virtual Keycode(window.h) 프로그래밍시 wParam에 전달되는 가상 키 코드가 더 중요 VK_로 시작 ASCII코드가 있는 경우, ASCII코드 사용 VK_RETURN Enter키가 눌린 경우 VK_ESCAPE VK_HOME VK_INSERT VK_NUMLOCK VK_SPACE VK_LEFT ESC키가 눌린 경우 Home키가 눌린 경우 Insert키가 눌린 경우 NumLock키가 눌린 경우 Space Bar가 눌린 경우 왼쪽 방향키가 눌린 경우
문자 입력 메시지 메시지 종류 WM_KEYDOWN vs WM_CHAR WM_CHAR, WM_SYSCHAR(ALT Key가 눌린 상태) cf)WM_KEYDOWN ASCII코드가 존재, TranslateMessage함수가 call TranslateMessage : 키의 값을 가지고 문자를 만들어 냄 WM_KEYDOWN vs WM_CHAR ‘s’ 키를 누를 때: WM_KEYDOWN -> WM_CHAR -> WM_KEYUP ‘insert’키를 누를 때: WM_KEYDOWN -> WM_KEYUP
Mouse 입력 메시지 마우스 클릭 마우스의 이동 마우스 더블클릭 클라이언트 영역 & 비클라이언트 영역 현재성 클라이언트 영역 & 비클라이언트 영역 마우스의 이동 현재성 마우스 더블클릭 Application에서 조합하여 만든 메시지 일정 시간 안에 두 번 눌리면 메시지를 변경
Mouse 입력 메시지(1) 메시지 종류 (9종류) WM_LBUTTONDOWN, WM_MOUSEMOVE, WM_LBUTTONUP, WM_LBUTTONDBLCLK,... WM_LBUTTONDBLCLK wc.style = CS_DBLCLKS; // ……….. WNDCLASS에 등록 MouseWheel WM_MOUSEWHEEL Mouse dragging을 처리하는 메시지는 제공 안됨 wParam 마우스 버튼 상태와 특수키(CTRL, Shift)들의 상태를 알 수 있다. - MK_SHIFT , MK_CONTROL, MK_LBUTTON, MK_MBUTTON, MK_RBUTTON,…… lParam 메시지가 발생한 시점의 X, Y좌표 X좌표 : 하위워드, Y좌표 : 상위워드
Mouse 입력 메시지(2) 2 1 T1에서 T2 사이에는 1번 윈도우로 마우스 메시지가 발생하고, T2와 T3사이에는 2번 윈도우로 마우스 메시지가 발생한다. T1 T2 T3 마우스 메시지와 윈도우
Mouse 입력 메시지(3) Mouse dragging할 때의 문제점 드래그시의 문제점 ① 드래그 시작 ② 윈도우 바깥으로 나가면 마우스 메시지가 이 윈도우에게로 발생하지 않는다. 드래그시의 문제점
Mouse 입력 메시지(4) Identifies the window that has the mouse capture. SetCapture function 커서가 어디에 있었는지에 상관없이 모든 마우스 메시지가 그 함수의 인자로 주어진 윈도우로 전달 마우스 메시지를 독점 HWND SetCapture(HWND hWnd // handle of window to receive mouse capture); ReleaseCapture function 마우스 메시지의 독점을 해제 BOOL ReleaseCapture(VOID) GetCapture function Identifies the window that has the mouse capture. HWND GetCapture(VOID);
Mouse 입력 메시지(5) Mouse dragging의 구현 두 동작의 결합 드래깅 시작 버튼 up인 경우 드래깅을 끝낸다. 마우스 이동 & 마우스 단추를 누른다. 드래깅 시작 HWND SetCapture(HWND hWnd) 이 함수를 호출하면 application 밖에서도 위치를 알 수 있다. 버튼 up인 경우 드래깅을 끝낸다. BOOL ReleaseCapture(VOID); case WM_MOUSEMOVE: if (wParam == MK_LBUTTON) SetCapture(hWnd); } break; case WM_LBUTTONUP: if (GetCapture() == hWnd) ReleaseCapture(); ………………………………………….
Mouse 입력 메시지(6) 비 작업영역에서 Title bar, 경계선, 스크롤 바 NC_ wParam : 비 클라이언트 영역을 가르킨다. lParam : 스크린 좌표
Mouse 입력 메시지(7) 예제 – 상자 그리기 마우스메시지 이용 SetCapture, ReleaseCapture 이용 마우스의 좌측 버튼을 누르면서 드래그하면 박스 그려짐
Timer 입력 메시지(1) WM_TIMER Timer의 용도 한번만 지정 주기적으로 작업을 수행, ex)주기적인 저장 Msg Queue에 중복으로 저장 안됨 wParam : 타이머 ID lParam : 호출될 함수의 번지 Timer의 용도 멀티태스킹 시간 표시 일정한 시간 간격으로 파일을 자동 저장 슬라이드 쇼 움직이는 동작 처리
Timer 입력 메시지(2) Timer 실행방법 (1) WM_CREATE WM_DESTROY UINT SetTimer(HWND hWnd, UINT nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc); HWND hWnd : WM_TIMER메시지를 받는 윈도우 핸들 UINT idTimer : 0이 아닌 값, timer ID UINT uTimer : 시간 간격, 0.001초 단위 TIMERPROC lpTimerFunc : 메시지를 받을 함수, NULL - 일정 간격으로 WM_TIMER 메시지를 받음 WM_DESTROY BOOL KillTimer(HWND hWnd, UINT uIDEvent); Ex) SetTimer(hWnd, 1, 5000, NULL)
Timer 입력 메시지(3) Timer의 구별 wParam에 Timer 번호가 기록 Timer 의 수 ex) 제한 없다. Win16 : 16 개 ex) WM_TIMER : Switch (wParam) { case 1: ~~~ case 2: ~~~ }
Timer 입력 메시지(4) Timer 실행방법 (2) 타이머 프로시저 타이머를 설치할 때 지정한 시간이 경과되면 자동적으로 실행되는 함수를 지정할 수 있다. WM_TIMER보다 비교적 정확 TimerProc() LRESULT CALLBACK TimerProc (HWND hWnd, UNIT uMsg, //WM_TIMER 메시지 WPARAM wParam, // 타이머 ID LPARAM lParam // 현재 시간) TimerProc()의 실행이 끝나면 호출한 곳으로 돌아감 실습 : WM_TIMER의 발생 간격과 TIMERPROC의 발생 간격 측정 1초마다 화면에 시간 표시
Keyboard 입력 메시지(3) 예제 – 글자 떨어뜨리기 메뉴의 Begin, End로 시작, 종료 2초 간격으로 글자를 떨어뜨린다. (폰트, 글자색, 배경색 바꾸기) 왼쪽, 오른쪽 방향키로 방향 이동 윈도우의 하단을 벗어나면 다시 상단으로 감 윈도우의 크기를 변화시키면 상단 중앙에서부터 다시 떨어짐 처음에는 WM_TIMER로 처리하고, TIMERPROC으로 같은 동작 처리 글자 떨어지기 변경 밑에 도달하면 위로 이동, 위에 도달하면 아래로 이동 떨어질 때 빨간색, 올라올 때 파란색 글자 옆으로 이동 왼쪽에 도달하면 오른쪽으로, 오른쪽에 도달하면 반대로 이동 메뉴를 사용하여 이동 방향 변경
Scrollbar 입력 메시지 윈도우를 생성할 때, WS_VSCROLL, WS_HSCROLL을 주면 scroll bar 생성 메시지 종류 WM_VSCROLL, WM_HSCROLL wParam 하위 워드 - SB_LINEDOWN, SB_LINEUP - SB_PAGEDOWN, SB_PAGEUP - SB_THUMBTRACK, SB_THUMBPOSITION 상위 워드 - SB_THUMBTRACK, SB_THUMBPOSITION일 때의 thumb의 위치 값 lParam Scrollbar의 윈도우 핸들
메시지 전달 SendMessgae(hwnd,msg,wParam,lParam) 곧바로 윈도우 프로시저로 보내짐 메시지가 완전히 처리되기 전에는 리턴하지 않는다. 윈도우간, 윈도우와 차일드 컨트롤간의 통신에 주로 사용 PostMessage(hwnd, msg, wParam, lParam) 메시지를 메시지 큐에 넣어 놓기만 하고 바로 리턴 급하지 않거나 지금 작업이 완전히 끝내야만 처리할 수 있는 메시지에 사용 성공하면 TRUE 리턴, 가급적 확인할 것
메시지 큐 메시지 종류 Queue 종류 Queue 메시지 Non-Queue 메시지 사용자의 입력으로부터 발생 Non-Queue 메시지 큐를 통하지 않고 바로 윈도우 프로시져로 보내짐 ex) UpdateWindow를 호출했을 때의 WM_PAINT Queue 종류 System Message Queue: 하나만 존재 Thread Message Queue : 스레드당 존재 즉 메시지 큐는 스레드당 하나씩 생기는 것 스레드 생성시에는 큐가 없음
4.2 사용자 정의 메시지 (1) User Defined Message 윈도우들간의 정보 교환 구성 함수와의 차이 0 through WM_USER – 1 Messages reserved for use by the system. WM_USER through 0x7FFF Integer messages for use by private window classes. WM_APP through 0xBFFF Messages available for use by applications. 0xC000 through 0xFFFF String messages for use by applications. Greater than 0xFFFF Reserved by the system for future use 함수와의 차이 프로세스 통신 가능
4.2 사용자 정의 메시지 (2) 해당 윈도우에 메시지를 보내는 방법 WM_COPYDATA LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); 윈도우에 바로 전달해서 msg 처리 후 리턴 BOOL PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); msg queue에 넣은 후 바로 리턴 WM_COPYDATA 서로 다른 process에 속한 윈도우간에 pointer 전달
4.2 사용자 정의 메시지 (3) ex) 프로세스간의 메시지 통신 메시지 ID 정의 통신하려는 윈도우 탐색 메시지 전달 실습 UNIT RegisterWindowMessage(LPCTSTR lpString) 사용되지 않는 ID를 리턴 통신하고자 하는 윈도우는 같은 문자열을 사용해서 메시지 ID를 얻어야 한다. 통신하려는 윈도우 탐색 FindWindow() : Caption 값을 이용, 윈도우 핸들을 알아냄 메시지 전달 SendMessage() 실습 윈도우 2개를 만들어서 사용자 정의 메시지를 정하고, 메시지를 받으면 메시지 박스를 출력한다.
예제 case WM_COMMAND: switch(LOWORD(wParam)) { case ID_FILE_EXIT: SendMessage(hWnd, WM_CLOSE, 0, 0); break; ………… } switch(message) { case WM_MYMESSAGE: LPSTR lpData; lpData = (LPSTR)lParam; // ……….원하는 일을 수행한다. }
메시지 수신 GetMessage() PeekMessage() 메시지가 없으면 새로운 메시지가 전달될 때까지 리턴하지 않는다. 메시지가 없더라도 즉각 리턴 WM_QUIT 메시지에 대한 예외 처리 필요 for (;;) { if (PeekMessage(&Message,NULL,0,0,PM_REMOVE)) { if(Message.message==WM_QUIT) break; TranslateMessage(&Message); DispatchMessage(&Message); }
서브 클래싱 서브클래싱 윈도우 프로시저로 보내지는 메시지를 중간에 가로챔 중간에 메시지를 변경할 수 있다. 주로 컨트롤에서 많이 이용
Control
Control Basic (1) Control 기능이 미리 정해진 윈도우 스타일에 따라 기능이 달라짐 원하는 기능을 가진 컨트롤을 생성하여 이용하면 된다. 스타일에 따라 기능이 달라짐 단독으로 존재할 수 없고, 항상 child 윈도우 각 control을 사용할 때 알아야 할 것 컨트롤의 스타일 컨트롤에 보낼 수 있는 메시지 컨트롤이 부모 윈도우에게 보내는 알림 코드 종류 Built-in control : OS에 미리 등록된 컨트롤 (19가지 - Win32) Custom control : third party에서 제공하는 컨트롤
Control Basic (2) 6가지 기본 컨트롤
Control Basic (3) Notification Message 컨트롤이 자신에게 생긴 일을 parent 윈도우에게 알릴 때 사용 WM_COMMAND 메시지를 사용 (cf. Scroll 제외) 메시지 타입 : WM_COMMAND LOWORD(wParam) : Child 윈도우 ID (자식 윈도우 이름) HIWORD(wParam) : 알림 코드 (발생한 일의 종류) lParam : 컨트롤의 윈도우 핸들
Control Basic (4) WM_COMMAND : Switch (LOWORD(wParam)) Case ControlID1 : ~~~~ Case ControlID2 : ~~~~ Switch(HIWORD(wParam)) Case 알림코드 :~~~
Control Basic (5) Control 작성법 Control Class Parent가 CreateWindow() 함수를 호출하여 생성 CreateWindow(classname, title, style, left, top, width, height, parenhtwnd, (HMENU)id, hinstance, lparam); Control Class Control의 형태는 CreateWindow(lpClassName,…) 에서 lpClassName 에 의해 결정 lpClassName 생성되는 Control “BUTTON” Push, Radio, Check “LISTBOX” 목록상자 “COMBOBOX” 복합상자 “EDIT” 글상자 “SCROLLBAR” 이동줄 “STATIC” 라벨
Control Basic (6) Control Style Control ID & Handle 세번째 인자(wStyle)를 이용하여 지정 WS_CHILD : Child 윈도우 WS_VISIBLE : ShowWindow없이 생성과 동시에 보이도록 한다. Control ID & Handle ID는 0 이상의 정수 HWND GetDlgItem(HWND hDlg, int nIDDlgItem); 컨트롤 ID로부터 핸들 정보를 알아낸다. hOKBtn = CreateWindow(“BUTTON”, “OK”, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE, 20, 20, 80, 40, hWnd, IDC_OKBTN, hInst, NULL);
Control Basic (7) Focus 키 입력의 시작점 초점 설정 창에 여러 컨트롤이 존재하는 경우 키 입력은 초점이 설정된 컨트롤에 대행 수행 초점 설정 SetFocus(hwnd);
Button Control (1) Button control의 종류
Button Control (2) Button control의 종류(wStyle) Push button : BS_PUSHBUTTON Default push button : BS_DEFPUSHBUTTON Check box : BS_CHECKBOX,BS_AUTO_CHECKBOX Radio button : BS_RADIOBUTTON Owner draw button : BS_OWNERDRAW, BS_GROUPBOX Notification code (부모 윈도우로) BN_CLICKED : 사용자가 click하면 발생 BN_DISABLE : button이 비활성화 되면 발생 BN_DBLCLICKED : 사용자가 double click하면 발생 BN_SETFOCUS BN_KILLFOCUS
Button Control (3) 상태 조사 및 종류 상태 조사 및 종류 SendMessage(hWnd, message, WPARAM, LPARAM); Push Button Default push button Check box Radio button Owner draw button - OK, Cancel button - 엔터를 누르면 실행되는 버튼 - 배타적이지 않은 옵션 선택, ex) 문자 속성 지정 - BM_SETCHECK, BM_GETCHECK - 배타적인 옵션 선택, ex) 문단 정렬 - push button처럼 동작 - button control style을 BS_OWNERDRAW로 지정 - WM_MEASUREITEM, WM_DRAWITEM을 parent 윈도우가 처리
CheckBox 종류 수동, 자동 수동의 경우 그때그때의 상태를 보고 조작을 해 주어야 한다. BST_CHECK, BST_UNCHECK ex) If (SendMessage(c1, BM_GETCHECK, 0, 0) == BST_UNCHECKED) { SendMessage(c1, BM_SETCHECK, BST_CHECKED, 0); // 기타 … }
RadioBox 종류 수동, 자동 그룹 버튼을 생성한 후에는 CheckRadioButton() 호출 하나의 선택사항에 대해 여러 개의 라디오 버튼들이 그룹 형성 같은 그룹에 속한 라디오 버튼은 오직 하나만 선택 그룹을 이루는 첫 번째 라디오 버튼에만 WS_GROUP 스타일 버튼을 생성한 후에는 CheckRadioButton() 호출 ex) CreateWindow(…, BS_GROUP, …); r1 = CreateWindow(…, WS_GROUP, …); R2 = CreateWindow(……); CheckRadioButton(hwnd, r1, r2, r1);
Static Control(1) Static Control 작성 Control에 문자열 입출력 주로 텍스트 출력에 사용 문자열 입출력 외에는 특별한 이벤트나 제어 메시지가 없다. 작성 lpClassName : “STATIC” Control에 문자열 입출력 Control에 문자열 lpString을 출력 SetWindowText(hWnd, lpString); Control의 문자열을 lpBuf에 읽어들임 GetWindowText(hWnd, lpBuf, nMaxLength) 모든 윈도우에 적용 가능 -> 윈도우 타이틑 입출력
Static Control(2) Style(wStyle) SS_BLACKFRAME SS_GRAYFRAME SS_WHITEFRAME SS_BLACKRECT SS_GRAYRECT SS_WHITERECT SS_LEFT, SS_CENTER, SS_RIGHT SS_NOPREFIX SS_SIMPLE 둘레를 black으로 칠한다. 둘레를 gray로 칠한다. 둘레를 white로 칠한다. 내부를 black으로 칠한다 내부를 gray로 칠한다 내부를 white로 칠한다 텍스트의 정렬 방식 &을 단축키로 인식하는 것을 중지한다. 한 줄짜리 static 컨트롤을 지정한다.
Edit Control(1) Edit control Edit control’s style 문자의 입력과 편집 ES_MULTILINE : 디폴트는 한 줄짜리 에디터 ES_WANTRETURN : Return키 사용 가능 (멀티라인 사용시) ES_AUTOHSCROLL : 수평 스크롤 ES_AUTOVSCROLL : 수직 스크롤 ES_PASSWORD : 모든 내용이 *로 표시 ES_LEFT, ES_CENTER, ES_RIGHT : 정렬 ES_UPPERCASE : 입력되는 문자를 대문자로 표시 ES_LOWERCASE : 입력되는 문자를 소문자로 표시 ES_READONLY : 키보드로 편집 불가
Edit Control(2) 제어 메시지 EM_GETLINE : 특정 라인의 내용 읽기 EM_GETLINECOUNT : 전체 라인 수 EM_CANUNDO : 가장 최근 명령의 취소여부 알아 내기 EM_UNDO : 가장 최근 명령을 취소 EM_SETSEL : 선택영역(블록) 잡기 EM_GETSEL : 선택영역(블록)의 위치 알아 내기 WM_SETTEXT : 새로운 내용으로 설정 WM_GETTEXT : 편집 내용 읽어 오기
Edit Control(3) 알림코드 EN_CHANGE 문자열이 변경되었다. EN_UPDATE 문자열이 변경되기 직전이다. EN_HSCROLL 가로 스크롤을 마우스로 건드림 EN_VSCROLL 세로 스크롤을 마우스로 건드림 EN_MAXTEXT EDIT에 문자열을 더 입력할 수 없음 EN_SETFOCUS 해당 EDIT에 초점이 설정 EN_KILLFOCUS 해당 EDIT에서 초점이 떠났음
Edit Control(4) 화면 출력 입출력 EN_UPDATE EN_CHANGE 문자열이 변경된 후 화면에 출력하기 전에 보내주는 메시지 문자열 길이에 따라 에디트의 폭을 늘리거나 별도의 조치 가능 EN_CHANGE 문자열이 화면으로 출력되고 난 후 보내지는 메시지 입출력 edit contol의 내용을 최대 nMax 문자까지 buf에 읽어들인다. nCount = GetWindowText(hEdit, buf, nMax) edit control에 buf의 내용을 출력한다. SetWindowText(hEdit, buf)
Edit Control(5) 폰트 변경 WM_SETFONT 실습 – 간단한 editor wParam : 폰트의 핸들 lParam : 폰트를 바꾼후 에디트를 다시 그릴 것인가? 실습 – 간단한 editor Edit control 생성 (WM_CRETE에서 CreateWindow 사용) Edit control의 font 변경 HFONT hFontE hFontEdit = CreateFont(30,0,0,0,0,0,0,0,HANGEUL_CHARSET,3,2,1, VARIABLE_PITCH | FF_ROMAN, “궁서”); SendMessage(hEdit, WM_SETFONT, (WPARAM)hFontE, MAKELPARAM(FALSE,0)); Undo, copy, paste, cut, clear 구현 메뉴 생성, 생성한 edit control의 WM_UNDO, WM_COPY, WM_PASTE, WM_CUT, WM_CLEAR 등을 이용 글자가 입력되면 EN_CHANGE가 오는지 확인 에디트 2개를 만들어서 내용을 복사
List Box Control(1) List Box control Style(wStyle) 선택 가능한 대상을 나열하고, 이 중에서 선택 Style(wStyle) LBS_NOTIFY 필수 LBS_STANDARD LBS_HASSTRINGS LSB_MULTICOLUMN LBS_MULTIPLESEL LBS_NOTIFY LBS_SORT 여러 스타일의 조합 (LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER) 각 항목별로 별도 데이터 저장 수평으로도 스크롤이 가능한 다중열 리스트 여러 항목 동시 선택 가능 click, double click시에 알림 코드 발생 알파벳순으로 정렬
List Box Control(2) 제어 메시지(SendMessage) LB_ADDSTRING LB_INSERTSTERING 항목 추가, lParam를 사용하여 추가하고자 하는 문자열 번지를 넘겨줌 LBS_SORT인 경우는 추가하면서 정렬된다. LB_INSERTSTERING 지정한 위치에 항목을 추가한다. LSB_SORT인 경우에도 정렬하지 않는다. LB_DELETESTRING 지정한 위치에 있는 항목을 삭제, wParam으로 항목의 번호를 넘겨주며, 남은 항목수 리턴 LB_DIR 특정 디렉토리에서 해당하는 파일들을 읽어들여서 리스트에 항목으로 추가한다. (파일 관련 대화상자에서 이용 가능) LB_GETCURSEL, LB_SETCURSEL 현재의 선택된 항목의 인덱스를 얻어오거나 변경 LB_GETCOUNT : 전체 항목수 LB_GETSEL, LS_SETSEL 인덱스로 지정한 항목의 상태를 얻거나 변경
List Box Control(3) 알림 코드 (List Box에서 이벤트 발생시) LB_GETTEXT, LB_SETTEXT 특정 인덱스의 문자열을 얻어오거나 변경 LB_GETITEMDATA LBS_HASSTRINGS일 때, 별도의 데이터를 읽어들임 LB_SETITEMDATA LBS_HASSTRINGS일 때, 별도의 데이터를 저장 알림 코드 (List Box에서 이벤트 발생시) HIWORD(wParam) LBN_DBLCLK 한 항목을 Double click하면 발생 LBN_ERRSPACE 내부적으로 메모리 부족 발생 LBN_KILLFOCUS 입력 포커스를 잃으면 발생 LBN_SELCHANGE 선택 항목이 변경되면 발생 LBN_SETFOCUS 입력 포커스를 받으면 발생
List Box Control(4) 항목 데이터 문자열 이외에 32비트의 item data를 가질 수 있다. 정수나 포인터를 사용해서 다른 형태의 데이터도 저장 가능 사용법 LB_SETITEMDATA 메시지 전달 wParam : item index lParam : item data ex) SendMessage(hList, LB_SETITEMDATA, 0, (LPARAM)1000); wParam으로 항목의 index를 주고 LB_GETITEMDATA 메시지 전달 ex) itemData = SendMessage(hList, LB_GETITEMDATA, 0, 0)
List Box Control(5) 항목 검색 LB_FINDSTRING LB_FINDSTRINGEXACT wParam : 문자열 검색 시작 index -1 로 지정할 경우 처음부터 lParam : 찾고자 하는 문자열 선두 부분이 일치하는 것을 찾아 index 리턴 찾지 못하면 , LB_ERR 리턴 LB_FINDSTRINGEXACT lParam과 정확히 일치하는 항목만 검색 LB_SELECTSTRING LB_FINDSTRING으로 찾은 후 LB_SETCURSEL 메시지 전달
List Box Control(6) 실습 리스트 박스와 static control 생성 WM_CREATE에서 CreateWindow 리스트 박스에 아이템 추가 (LB_ADDSTRING) 리스트에서 다른 항목을 선택할 때마다 static contorl에 현재 선택된 항목을 출력 리스트 항목에 부가 데이터 설정하고 더블 클릭하면 그 내용을 static control에 출력 에디트 컨트롤에 입력된 스트링을 리스트에서 찾아서 결과를 static control에 출력 LB_DIR 속성을 이용하여 파일 탐색기 구현 SendMessage(hWndList, LB_DIR, DIRATTR, (LPARAM)"*.txt");
Combo Box Control(1) Combo Box Style(wStyle) Edit box + List Box 필요한 경우에만 항목을 열어서 화면공간 절약 Style(wStyle) CBS_SIMPLE : list box처럼 항상 펼쳐진 스타일 CBS_DROPDOWN : edit control + list box CBS_ DROPDOWNLIST : static control + list box CBS_AUTOHSCROLL : 수평 스크롤 가능 CBS_HASSTRINGS : 각 항목에 부가 정보 저장 CBS_SORT : 알파벳순으로 정렬
Combo Box Control(2) 제어 메시지 CB_ADDSTRING CB_INSERTSTRING 콤보 박스의 리스트에 항목 추가 CBS_SORT라면 추가하면서 정렬 CB_INSERTSTRING 콤보 박스의 리스트 박스에 지정한 위치에 항목 추가 CBS_SORT라도 추가하면서 정렬하지 않는다. CB_DELETESTRING CB_DIR 와일드 카드를 지정하면 특정 디렉토리에서 파일 리스트를 읽어들여서 리스트에 추가 CB_GETCURSEL, CB_SETCURSEL 리스트 박스에서 현재 선택된 항목의 인덱스를 읽어오거나 변경 CB_GETCOUNT CB_GETLBTEXT, CB_SETLBTEXT
Combo Box Control(3) 알림 코드 CBN_CLOSEUP CBN_DBLCLK 리스트 박스가 닫힐 때 발생 CBN_DBLCLK 리스트 박스 내의 항목을 더블클릭 CBN_DROPDOWN : 리스트 박스가 펼쳐짐 CBN_EDITCHANGE : 에디트의 내용이 변경 CBN_EDITUPDATE CBN_SETFOCUS CBN_KILLFOCUS CBN_SELCHANGE CBN_ERRSPACE
Scrollbar (1) 종류 구조 표준 스크롤바 스크롤바 컨트롤 옆과 아래에 밀착 WS_HSCROLL, WS_VSCROLL CreateWindow()로 만들어지는 독립적인 윈도우 구조
Scrollbar (2) 표준 Scrollbar는 스타일을 지정할 수 없다. Message WM_VSCROLL, WM_HSCROLL을 부모 윈도우에게 전달 WM_COMMAND를 사용하지 않는다. 인수 설명 LOWORD(wParam) 사용자가 스크롤 바의 어디를 눌렀는지를 알려줌 HIWORD(wPARAM) 썸의 위치값, 단 사용자가 썸을 드래그할 때만 전달 lParam 스크롤 바위 윈도우 핸들, 표준 스크롤 바에서는 NULL
Scrollbar (3) LOWORD(wParam)에 전달되는 scroll message SB_PAGEUP SB_PAGEDOWN SB_LINEUP SB_LINEDOWN Thumb을 drag하는 동안 SB_THUMBTRACK이 계속 발생 Thumb의 drag가 끝나면 SB_THUMBPOSITION이 발생 BOOL EnableWindow(HWND hWnd, BOOL bEnable); - 윈도우를 활성화 또는 비활성화 시킨다.
Scrollbar(4) 스크롤 범위와 위치 기본 정의 스크롤바 : 0~100 스크롤바 컨트롤 : 0~0 SetScrollRange(HWND hwnd, int nbar, int nMinPos, int nMaxPos, BOOL bRedraw) bRedraw = false SetScrollPos(HWND hwnd, int nbar, int nMinPos, int nMaxPos, BOOL bRedraw) bRedraw = true nBar 설명 SB_CTL 스크롤바 컨트롤이며, hWnd 는 범위 지정 스크롤 바의 윈도우 핸들 SB_HORZ 수평 표준 스크롤바, hWnd는 스크롤바를 가진 윈도우의 핸들 SB_VERT 수직 표준 스크롤바, hWnd는 스크롤바를 가진 윈도우의 핸들
Scrollbar(5) 정확한 범위 설정 작업영역의 크기에서 화면 크기를 뺀 부분으로 설정 화면 크기에 따라 스크롤 범위가 변경되어야 한다. WM_SIZE에서 스크롤 범위 지정 ex) case WM_SIZE: xMax = 1024 – LOWORD(lParam); yMax = 768 – HIWORD(lParam); SetScrollRange(hWnd, SB_VERT, 0, yMax, TRUE); SetScrollPos(hWnd, SB_VERT, 0, TRUE); SetScrollRange(hWnd, SB_HORZ, 0, xMax, TRUE); SetScrollPos(hWnd, SB_HORZ, TRUE); Return 0;
Scrollbar(6) 스크롤 영역 화면이 스크롤 전체 화면이 변하는 것 전체 화면을 다시 그리지 않는다. 위치만 변하는 부분은 비트맵으로 복사 과정 스크롤 된 만큼 화면을 고속 복사(bitblt()) 새로 드러난 부분 무효화 WM_PAINT에 의해 드러난 부분이 새로 그려짐 ScrollWindow(Hwnd hwnd, int xAmount, int yAmount, RECT *lpRect, Rect *lpClipRect) 클리핑 영역 무효 영역과 가시화 영역의 교집합이며, 그리기의 대상이 되는 영역
Scrollbar(7) 비례 스크롤 바 위치와 길이를 함께 보여줌 SetScrollInfo() 사용 SetScrollRange(), SetScrollPos()의 기능을 한꺼번에 SetScrollInfo(hWnd, SB_CTL, &si, TRUE) si는 SCROLLINFO형 구조체 typedef struct { UINT cbsize; // 이 구조체의 바이트 수 UNIT fMask; // 대부분 SIF_ALL 사용 int nMin; // 최소값 int nMax, // 최대값 UINT nPage; // 한 페이지의 크기, Thumb의 길이를 결정 int nPos; // 스크롤 바의 위치 int nTrackPos; // Thumb를 끌고 있는 현재의 위치 } SCROOLLINFO;
Scrollbar(8) ex) SCROLLINFO si; .... case WM_SIZE: si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_ALL | SIF_DISABLENOSCROLL; si.nMin= 0; si.nMax = 768; si.nPage = HIWORD(lParam); si.nPos = yPos; SetScrollInfo(hWnd, SB_VERT, &si, TRUE); si.nMax = 1024; si.nPage = LOWORD(lParam); si.nPos=xPos;
Scrollbar Control Scrollbar control Scrollbar control style 연속적인 범위의 값에서 하나의 값 선택 Scrollbar control style SBS_VERT : 수직 스크롤 바를 생성 SBS_HORZ : 수평 스크롤 바를 생성 제어 메시지 SBM_ENABLE_ARROWS : 활성, 비활성 여부 지정 SBM_GETPOS : 현재 thumb의 위치 알아 내기 SBM_GETRANGE : 양끝에 설정된 값 알아 내기 SBM_SETPOS : 현재 thumb의 위치 설정 SBM_SETRANGE : 양끝에 새로운 값 설정하기 관리 함수 ShowScrollBar() EnableWindow()
Scrollbar Control (2) 한 윈도우 표준 스크롤바와 스크롤바 컨트롤을 모두 사용 lParam 로 구분 표준 : NULL 컨트롤 : 컨트롤 바 윈도우 핸들값
ETC(1) 자식 윈도우 프로시져 가로채기 설정) HWND butwnd; WNDPROC butProc; butwnd = CreateWindow(“buttom”,”버튼”, WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON, 10, 20, 100, 40, hwnd, (HMENU)1, hinst, NULL); butProc = (WNDPROC)SetWindowLong(butwnd, GWL_WINPROC, (LONG)ButtonProc); Callback 함수) LRESEULT CALLBACK ButtonProc(HWND hwnd, UNIT imsg, WPARAM wParam, LPARAM lParam); { Switch (imsg) { // 버튼에 해당하는 내용 기록 } return CallWindowProc(butProc, hwnd, imsg, wParam, lParam);
ETC(2) 자식 윈도우 색상 변경 ex) case WM_CTCOLORSTATIC : return (LRESULT)CreateSolidBrush(RGB(10,10,10)); Message Child Window WM_CTLCOLORSTATIC 정적 외곽 박스형 WM_CTLCOLORMSGBOX 메시지 박스 WM_CTLCOLORLISTBOX 리스트 박스 WM_CTLCOLOREDIT 에디트 박스 WM_CTLOLORSRLLBAR 스크롤 바 WM_CTLCOLORDLG 다이얼로그
ETC(3) : 버튼 윈도우 메시지 문제점) 버튼의 수가 증가하게 되면? 해결점) 함수의 포인터 ex) case WM_COMMAND : if(LOWORD(wParam)==1) // 1번으로 설정된 버튼이면 switch (HIWORD(wParam)) case BN_CLICKED : ~~~;~~~; break; case BN_SETFOCUS : ~~~;~~~; break; 문제점) 버튼의 수가 증가하게 되면? 해결점) 함수의 포인터 형(* 함수명)(); ex) void Button1(HWND hwnd, UNIT imsg, WPARAM wParam, LPARAM lParam); void Button2(HWND hwnd, UNIT imsg, WPARAM wParam, LPARAM lParam); void Button3(HWND hwnd, UNIT imsg, WPARAM wParam, LPARAM lParam); void (*Button[3])(HWND hwnd, UNIT imsg, WPARAM wParam, LPARAM lParam)= {Button1, Button2, Button3}; case WM_COMMAND; Button[LOWORD(wParam)](hwnd, imsg, wParam, lParam); break;
ETC (4) 사용자 정의 그리기 버튼 생성시 : BS_OWNERDRAW 윈도우에서 --> WM_DRAWITEM 메시지 발생 (wParm : 버튼의 번호, lParam : 버튼의 정보) 이 메시지와 함께 들어오는 lParam을 DRAWITEMSTRUCT 포인터로 받음 구조체를 이용하여 사용자가 직접 그림 ex) case WM_DRAWITEM: drawbut = (LPDRAWITEMSTRUCT)lParam;
ETC (5) 윈도우 활성, 비활성화 Static Window에 메시지 전달 EnableWindow() 를 이용 BOOL EnableWindow(HWND hwnd, BOOL bEnable); 활성일 경우 true, 비활성이면 false Static Window에 메시지 전달 WM_CREATE에서 static window 생성 새로운 프로시져 등록 (StaticProc()) 이벤트에 따른 메시지를 정적 윈도우로 전달 SendMessage()
6.Resource
Introduction Resource 읽기 전용의 사용자 인터페이스 관련 데이터 리소스는 코드와 분리해서 관리한다. VC++ ResourceView가 시각적으로 관리 cf) VC++ 1.5까지는 AppStudio Text로 편집 : Open As에서 Text로 선택
Dialog Box (1) Dialog Box 응용 프로그램에서 사용자와 대화하기 위해서 사용하는 팝업 윈도우 컨트롤을 자식 윈도우로 갖는 특수한 부모 윈도우
Dialog Box (2) 종류 Modal dialog box Modeless dialog box 입력을 받지 않으면 다음 작업 진행이 불가능한 사용자 입력을 받을 때 사용 대화 상자를 닫아야만 다른 작업이 가능 모달 대화상자의 경우 입력이 끝나면 화면에서 사라진다. 대부분의 dialog box가 여기에 해당 ex) 파일 메뉴의 열기(Open) 명령의 대화상자 Modeless dialog box 모달 대화상자처럼 사용자가 입력을 했다고 없어져야 하는 것은 아니다. 대화상자를 띄운 상태로 부모 윈도우와 대화상자가 공존 ex )찾기 명령 명령의 대화상자 생성, 파괴하는 API 만 다르다. DialogBox vs. CreateDialog, EndDialog vs. DestroyWindow
Dialog Box (3) 대화 상자의 작성 절차 Control palette의 구성 컨트롤 (1) 리소스 뷰에서 대화상자의 모양 작성 (2) 대화상자의 동작이 기술된 대화상자 프로시저 작성 (3) 대화상자 생성 Control palette의 구성 컨트롤 static control group box check box combo box horizontal scroll spin scroll slider list control tab control rich edit control picture control edit control push button control radio control list box vertical scroll progress bar hotkey control tree control animation control custom control
Dialog Box (4) IDD_SELECTOBJECT DIALOG DISCARDABLE 0, 0, 185, 92 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION “Select Object” FONT 8, “MS Sans Serif” BEGIN DEFPUSHBUTTON “OK”, IDOK, 129, 6, 50, 14 PUSHBUTTON “Cancel”, IDCANCEL, 129, 23, 50, 14 LISTBOX IDC_LISTBOX, 8, 7, 113, 80, LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP END
Dialog Box Procedure (1) 대화상자 프로시져 대화상자도 윈도우이기 때문에 윈도우 프로시저가 필요 주로 두 개의 메시지만을 처리 WM_INITDIALOG 메시지 대화상자가 생성될 때 제일 처음 오는 메시지 컨트롤들을 초기화 WM_COMMAND 메시지 컨트롤이 부모 윈도우에게 자신에게 발생한 일을 알리는데 사용되는 알림 메시지(notification message) LOWORD(wParam) : 컨트롤 ID(자식 윈도우 이름) HIWORD(wParam) : 발생한 일의 종류를 나타내는 알림 코드
Dialog Box Procedure (2) GetDlgItem 컨트롤을 조작하기 위해서는 윈도우 핸들이 필요 자식 윈도우의 윈도우 핸들을 얻어옴 HWND GetDlgItem(hWndDlg, controlID); 대화 상자 생성 Modal 대화 상자 : DiagloBox Modeless 대화 상자 : CreateDialog -> ShowWindow 대화 상자의 종료 Modal 대화 상자 : EndDialog Modeless 대화 상자 : DestroyWindow
Dialog Box Procedure (3) BOOL APIENTRY 대화상자 프로시져 이름(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) case WM_INITDIALOG: // 가장 먼저 발생하므로 여기서 컨트롤을 초기화한다. return TRUE; case WM_COMMAND : // 컨트롤의 알림 메시지 처리 switch((LOWORD(wParam)) // 컨트롤에 따라 분류 case 컨트롤 ID1 : switch(HIWORD(wParam)) // 일어난 일에 따라 처리 ....... } case 컨트롤 ID2 : switch(HIWORD(lParam)) // 일어난 일에 따라 처리 ..... return FALSE;
Dialog Box Procedure (4) BOOL APIENTRY SelectObj(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) case WM_INITDIALOG: // 리스트 박스의 윈도우 핸들을 얻는다. HWND hListBox = GetDlgItem(hDlg, IDC_LIST); // 리스트 박스로 메시지를 보내 채운다. SendMessage(hListBox, LB_ADDSTRING, NULL, (LONG)(LPCSTR)"Rectangle"); SendMessage(hListBox, LB_ADDSTRING, NULL, (LONG)(LPCSTR)"Ellipse"); SendMessage(hListBox, LB_ADDSTRING, NULL, (LONG)(LPCSTR)"Line"); SendMessage(hListBox, LB_ADDSTRING, NULL, (LONG)(LPCSTR)“Box"); // wCurType 변수 값에 따라 리스트 박스의 현재 선택을 만든다. SendMessage(hListBox, LB_SETCURSEL, wCurType, 0L); return (TRUE); }
Dialog Box Procedure (5) case WM_COMMAND: { HWND hListBox = GetDlgItem(hDlg, IDC_LIST); switch(wParam) case IDOK : // OK 버튼이 눌린 경우 DWORD dwIndex = SendMessage(hListBox, LB_GETCURSEL, 0, 0L); wCurType = (enum OBJECTTYPE)dwIndex; EndDialog(hDlg, TRUE); return (TRUE); } case IDCANCEL : return (FALSE);
Modal Diabog Box 생성 : DialogBox 파괴 : EndDialog int DialogBox(HINSTANCE hInstance, LPCTSTR lpTemplate, HWND hWndParent, DLGPROC lpDialogFunc); 파괴 : EndDialog BOOL EndDialog(HWND hDlg, int nResult); case WM_COMMAND: { switch(LOWORD(wParam)) case IDM_SELECT_OBJECT : DialogBox(hInst, MAKEINTRESOURCE(IDD_SELECTOBJECT), hWnd, SelectObj); break; } .....................…
Modeless Dialog Box (1) 생성 : CreateDialog 삭제 : DestroyWindow HWND CreateDialog(HINSTANCE hInstance, LPCTSTR lpTemplate, HWND hWndParent, DLGPROC lpDialogFunc); ShowWindow를 이용해서 화면에 출력 ShowWindow(dialogwnd, SW_SHOW); // 화면에 출력 ShowWindow(dialogwnd, SW_HIDE); // 화면에서 감춤 삭제 : DestroyWindow BOOL DestroyWindow(HWND hWnd);
Modeless Dialog Box (2) 모달리스 대화상자는 메시지 루프 변경이 필요 부모 윈도우와 대화상자가 동시에 떠있기 때문에 어느 윈도우로 메시지가 가야 할지 정해야 한다. while (GetMessage(&msg, NULL, NULL, NULL)) { if (hDlg == NULL || !IsDialogMessage(hDlg, &msg)) TranslateMessage(&msg); DispatchMessage(&msg); }
Common Dialog Box (1) Common Dialog Box 공통 대화 상자를 띄우는 절차 comdlg32.dll 라이브러리에서 제공하는 대화 상자 구조체를 초기화하여 함수를 호출하면 간편하게 다양한 기능의 대화 상자를 이용할 수 있다. 공통 대화 상자를 띄우는 절차 commdlg.h를 포함 Project->Settings->Link의 Object/Library modules에 comdlg32.lib를 넣기 공통 대화상자별로 필요한 구조체 초기화 정해진 함수 호출
Common Dialog Box(2) Common Dialog Box의 종류, 구조체, 호출함수 종류 구조체 호출함수 종류 구조체 호출함수 File Open OPENFILENAME GetOpenFileName File Save OPENFILENAME GetSaveFileName Printer PRINTDLG PrintDlg Find FINDREPLACE FindText Replace FINDREPLACE ReplaceText Color Choose CHOOSECOLOR ChooseColor Font Choose CHOOSEFONT ChooseFont Page Setup PAGESETUPDLG PageSetupDlg
실습 (1) 대화상자에서 색, 타입을 지정하면 지정된 색과 타입의 도형 출력 화면에 버튼 4개 생성 2개는 BS_OWNERDRAW, 나머지는 FONT, FILE Ownerdraw 버튼 그리기 (FillRect and Bitmap) 대화상자 템플릿 작성 및 대화상자 프로시져 작성 버튼을 누르면 도형 타입을 선택할 수 있는 대화상자 띄우기 DialogBox WM_PAINT에서 선택한 값에 따라 도형 바꿔서 그리기 버튼을 누르면 color choose dialog를 띄워서 색을 선택하면 도형을 선택한 색으로 바꿔 그리기 FONT 버튼을 누르면 font choose dialog를 띄워서 지정한 폰트로 변경
실습 (2) File 버튼을 누르면 파일 열기 대화 상자 띄우기 리스트에 텍스트를 채우고 find dialog로 찾아보기
PRINTDLG pd; pd.lStructSize = sizeof(PRINTDLG); pd.hwndOwner = hWnd; pd.Flags = PD_RETURNDC; if(PrintDlg(&pd)) // 해당 함수의 호출, 인자로 앞의 변수가 사용되었다. { …………………. } 만일 프린터 설정 대화상자를 띄우고 싶다면, pd.flags=PD_RETURNDC | PD_PRINTSETUP
ICON Icon Built-in Icon 메인 윈도우가 축소되거나 배경 화면에 등록할 때 응용 프로그램을 나타내기 위한 그래픽 이미지 WNDCLASS의 hIcon에 LoadIcon으로 등록 종류 Built-in Icon : OS에 미리 등록된 Icon User defined Icon : 사용자가 만든 Icon Built-in Icon wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 디폴트 응용 프로그램 아이콘 IDI_APPLICATION 대화상자에서 사용하는 별표(*) 모양의 아이콘 IDI_ASTERISK 느낌표(!) 모양의 아이콘 (경고할 때 사용) IDI_EXCLAMATION 손모양의 아이콘( 심각한 경고를 할 때 사용) IDI_HAND 물음표(?) 모양의 아이콘 (질문할 때 사용) IDI_QUESTION
User defined Icon User defined icon 리소스 편집기로 아이콘 작성 resource.h에 아이콘 등록 - 숫자와 string 둘 다 가능 아이콘의 Resource ID가 숫자인 경우: wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYICON); 아이콘의 Resource ID가 string인 경우: wc.hIcon = LoadIcon(hInstance, “IDI_MYICON”);
Icon 직접 표시하기 직접 아이콘을 표시해야 하는 경우 ex) 시계 프로그램 WNDCLASS 등록할 때 wc.hIcon = NULL; WndProc 처리할 때 case WM_PAINT: { PAINTSTRUCT ps; HDC hDC; hDC = BeginPaint(hWnd, &ps); if (IsIconic(hWnd)){ /* Icon상태에 맞게 출력 작업 수행 */} else { /* 정상적인 상태에 맞게 화면 복구 */} EndPaint(hWnd, &ps); break; }
Cursor Cursor Built-in Cursor 마우스의 위치를 가리키는 포인터 cf) caret Hot spot : 마우스가 가르키는 지점 WNDCLASS의 hCusror에 LoadCursor로 등록 모양에 따라 작업 종류 파악 ex) 모래 시계,... 종류 Built-in Cursor : OS에 미리 등록된 Cursor User defined Cursor : 사용자가 만든 Cursor Built-in Cursor wc.hCursor = LoadCursor(NULL, IDC_ARROW);
Built-in Cursor IDC_ARROW 화살 모양의 디폴트 커서 IDC_CROSS IDC_IBEAM IDC_ICON IDC_SIZENESW IDC_SIZENS IDC_SIZENWSE IDC_UPARROW IDC_WAIT 십자가 모양의 커서(윈도우 등의 이동할 때 사용) 텍스트 입력 위치를 결정하는 데 쓰이는 커서 비어 있는 커서 우측 하단에 사각형을 포함하는 커서 북쪽과 남쪽을 가리키는 커서 북서쪽과 남동쪽을 가리키는 커서 수직으로 선 화살표 모양의 커서 모래 시계 모양의 커서
User defined Cursor User defined cursor 리소스 편집기로 커서 작성 resource.h에 커서 등록 - 숫자와 string 둘 다 가능 Hot spot 마우스 포인터의 위치 wc.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_MYCURSOR));
그 밖의 커서 모양 표시하기 WNDCLASS에 등록하지 않고 매번 그리는 경우 WM_SETCURSOR 또는 WM_MOUSEMOVE 메시지가 올 때마다 SetCursor함수 호출 WndProc에서 static HCURSOR hMyCursor; ….. switch(msg) { case WM_CREATE: // 앞으로 사용할 커서 모양을 미리 load해 놓는다 hMyCursor = LoadCursor(hInstance, (LPSTR)”MyCursor”); ……… case WM_SETCURSOR: SetCursor(hMyCursor); break;
WM_SETCURSOR WM_SETCURSOR wParam : window handle lParam : hit-test code (뒤 페이지 참고) case WM_SETCURSOR: if(LOWORD(lParam) == HTCLIENT) { SetCursor(hMyCursor); // hMyCursor에 커서가 미리 load되었다고 하자. return TRUE; } return DefWindowProc(hWnd, message, wParam, lParam);
HTBORDER 크기 조절이 불가능한 윈도우의 테두리에 커서가 위치 HTBOTTOM 윈도우의 하단 테두리에 커서가 위치 HTBOTTOMLEFT HTBOTTOMRIGHT HTCAPTION HTCLIENT HTHSCROLL HTLEFT HTMAXBUTTON HTMENU HTMINBUTTON HTRIGHT HTSYSMENU HTTOP HTTOPRIGHT HTVSCROLL 윈도우의 좌측 하단 테두리에 커서가 위치 윈도우의 우측 하단 테두리에 커서가 위치 윈도우의 타이틀바 영역에 위치 윈도우의 사용자 영역에 위치 수평 스크롤바에 위치 윈도우의 좌측 테두리에 위치 전체화면(Maximize)버튼에 위치 메뉴 영역에 위치 아이콘화 버튼에 위치 윈도우의 우측 테두리에 위치 시스템 메뉴에 위치 윈도우의 상단 테두리에 위치 윈도우의 좌측 상단 테두리에 위치 윈도우의 수직 스크롤바에 위치
잠깐동안 다른 커서 모양 표시 HCURSOR hSaveCursor; HCURSOR hHourGlass; ……… hHourGlass = LoadCursor(NULL, IDC_WAIT); // 모래시계 커서를 loading hSaveCursor = SetCursor(hHourGlass); // 커서 모양을 모래시계로 변경 /* 시간이 오래 걸리는 작업 수행 ex) file I/O */ SetCursor(hSaveCursor); // 커서를 원상복구 ReleaseCapture(); // 마우스 독점 해제 커서가 하는 일은 단순히 마우스 위치를 표시하는 것으로 이외에도 프로그램의 상태를 나타내는 것이다. 예를 들어 작업할 수 없는 영역에 들어갈 경우에 커서가 X자 모양으로 바뀌는 경우
예제 Cursur를 만들어 좌측 버튼을 클릭시 커서 변경 마우스가 움직이거나 다른 메시지 발생시 원래값으로 돌아간다. HCURSOR cur; ... case WM_LBUTTONDOWN: cur = LoadCursorFromFile("test.cur"); SetCursor(cur); case WM_LBUTTONUP: Setcursor(LoadCursor(NULL, IDC_ARROW)); break; 마우스가 움직이거나 다른 메시지 발생시 원래값으로 돌아간다. 해결) flag 설정 BOOL cflag = FALSE; // 함수 밖에서 extern으로 설정 case WM_LBUTTONDOWN: // 커서 변경 cflag= TRUE; case WM_LBUTTONUP: // 커서 변경 cflag= FALSE; case WM_MOUSEMOVE: if (cflag) { // 커서를 로드 } else { //원래의 커서로}
예제 마우스가 어플리케이션 밖에 나갔다 들어올 경우 wc에 설정된 커서와 교차되는 문제점 해결점) WM_SETCURSOR 자신의 윈도우에 포커스가 있을 때 발생하는 메시지 ex) case WM_SETCURSOR: if (cflag) { hcur=LoadCursorFromFile("test.cur"); SetCursor(hcur); } else { SetCursor(LoadCursor(hInst,IDC_ARROW); } break; wc에 설정된 커서와 교차되는 문제점 GetClassLong() : RegisterClassEx에 등록된 정보를 꺼내올 때 SetClassLong() : RegisterClassEx에 정보를 등록시킬 때 HCURSOR cur = LoadCusorFromFile("test.cur"); SetClassLong(hwnd, GCL_HCURSOR, (LONG)cur);
실습 도형 그리기 프로그램에 도형의 종류에 따라 cursor 변경 도형 타입만큼의 커서 등록 WM_CREATE, LoadCursor WM_SETCURSOR에서 도형의 타입에 따라 커서 로드
캐럿 (1) 캐럿 생성 및 파괴 화면 표시 키보드의 다음 입력 위치를 가리키는 용어 시스템 전역적으로 하나만 존재 BOOL CreateCaret(HWND hWnd, HBITMAP hBitmap, int nWidth, int nHeight) BOOL DestroyCaret(VOID) 생성 시점 WM_SETFOCUS 메시지가 전달 되었을 떄 화면 표시 BOOL ShowCaret(HWND hWnd) BOOL HideCaret(HWND hWnd)
캐럿 (2) Ex) 캐럿의 위치 깜박임 속도 cf) WM_PAINT BOOL SetCaretPos(int X, int Y); 일부로 숨기지 않아도 된다. 캐럿의 위치 BOOL SetCaretPos(int X, int Y); BOOL GetCaretPos(LPPOINT lpPoint); 깜박임 속도 BOOL SetCaretBlinkTime(UINT uMSeonds); UINT GetCaretBlinkTime(VOID); HideCaret(hWnd); HDC hdc = GetDC(hWnd); //그리기 ReleaseDC(hWnd, hdc); ShowCaret(hWnd);
Bitmap Bitmap Bitmap 생성 DC(Device Context) GDI OS에서 가장 많이 사용하는 BMP 파일을 resource화 종류 장치 의존 bitmap(DDB) : palette 정보 포함 안함 자원을 이용하여 비트맵을 로드,출력 장치 독립 bitmap(DIB) : palette 정보 포함 Bmp 파일에서 비트맵을 로드하여 출력 Bitmap 생성 VC++ resource editor 사용 DC(Device Context) 출력 장치를 가리키는 자료구조 DC의 구성요소 : bitmap, brush, pen, palette, font GDI 출력 장치에 출력을 하는 그래픽 출력 함수의 집합
Bitmap 과정 비트맵을 생성 비트맵 로드 비트맵 출력 비트맵에서 패턴 얻어오기 hBitmap = LoadBitMap(hInstance, 비트멥 ID) 비트맵 출력 비트맵에서 패턴 얻어오기 CreatePatternBrush(hBitmap); Ex) 비트맵을 이용 배경화면 사용 bBitmap = LoadBitmap(hInstance,ID_BITMAP); hBrush = CreatePattern(hBitmap); ,, wc.hbrBackground = hBrush;
Bitmap DC를 비트맵으로 CreateBitmap (비트맵 가로크기, 세로크기, 비트맵 플랜, 픽셀당 비트수, 팔레트 데이터); ex) 문자를 비트맵으로 HBITMAP hBitmap; HDC memdc; hBitmap = CreateBitmap(100,100,1,24,NULL); memdc = CreateCompatableDC(hdc); SelectObject(memdc,hBitmap); TextOut(memdc, 0, 0, "Test",12);
Bitmap 출력방식 Memory Device Context 생성 특정 출력 장치의 DC와 동일한 속성을 가지는 DC를 메모리에 만든 것 DC간의 자료 교환 메모리 DC 화면 DC 또는 프린터 DC BitBlt(1:1 복사), StretchBlt(축소 또는 확대 복사) HDC hDC, hMemDC; hDC = GetDC(hWnd); // hWnd가 가리키는 윈도우에 대한 DC를 얻는다. hMemDC = CreateCompatible(hDC); // 메모리 DC 생성 /* 원하는 작업 수행*/ DeleteDC(hMemDC); // 메모리 DC 삭제 ReleaseDC(hWnd, hDC);
BitBlt vs StretchBlt(1) BOOL BitBlt( HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, HDC hdcSrc, int nXSrc, int nYSrc, DWORD dwRop ); HDC hdcDest : target DC int nXDest, int nYDest : target DC에서의 bitmap의 시작 X, Y좌표 int nWidth, int nHeight : target DC에서의 bitmap의 폭, 높이 HDC hdcSrc : source DC int nXSrc, int nYSrc: source DC에서의 bitmap의 시작 X, Y좌표 DWORD dwRop : raster 적용 기술 SRCAND - (source DC AND target DC) => target DC SRCCOPY - source DC => target DC SRCPAINT - (source DC OR target DC) => target DC SRCINVERT - (source DC XOR target DC) => target DC BOOL StretchBlt( HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, DWORD dwRop ); 확대 or 축소
BitBlt vs StretchBlt(2) Target DC(MemDC) Source DC(화면DC) <BitBlt의 경우> <StretchBlt의 경우>
메모리 DC의 bitmap교체(1) 절차 bitmap을 메모리로 loading 메모리 DC의 bitmap 변경(SelectObject사용) 메모리 DC의 bitmap을 출력 DC로 복사 (BitBlt, StretchBlt사용)
메모리 DC의 bitmap교체(2) HDC hDC, hMemDC; HBITMAP hBitmap, hOldBitmap; // 비트맵을 메모리로 loading hBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_MYBITMAP)); hDC = GetDC(hWnd); hMemDC = CreateCompatibleDC(hDC); // 메모리 DC를 만든다. // 메모리 DC의 bitmap을 교체한다. hOldBitmap = SelectObject(hMemDc, hBitmap); …………….. // 각종 제거 작업을 수행한다. SelectObject(hMemDC, hOldBitmap); // 메모리 DC의 bitmap을 원상복구 DeleteObject(hBitmap); // bitmap을 제거한다. DeleteDC(hMemDC); // 메모리 DC를 삭제한다. ReleaseDC(hWnd, hDC);
Memory DC 화면 DC 로 복사 HDC hDC, hMemDC; HBITMAP hBitmap, hOldBitmap; BITMAP bm; // bitmap을 메모리로 올린다. hBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_MYBITMAP); hDC = GetDC(hWnd); hMemDC = CreateCompatible(hDC); // 메모리 DC를 만든다 // 메모리 DC의 bitmap을 교체한다 hOldBitmap = SelectObject(hMemDC, hBitmap); // bitmap의 크기를 알아낸다 GetObject(hBitmap, &bm); // 메모리 DC에 들어간 bitmap을 화면 DC로 복사한다 BitBlt(hDC, 10, 10, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0, SRCCOPY); // 각종 제거 작업을 수행한다 SelectObject(hMemDC, hOldbitmap); // 메모리 DC를 원상 복구 DeleteObject(hBitmap); // bitmap을 제거 DeleteDC(hMemDC); // 메모리 DC를 제거 ReleaseDC(hWnd, hDC);
int GetObject( HGDIOBJ hgdiobj, int cbBuffer, LPVOID lpvObject); - graphic object에 대한 정보를 얻는 데 사용한다.
그 밖의 bitmap의 출력 방법 빈 bitmap을 생성하고, GDI로 출력 : dynamic하다. HBITMAP CreateBitmap( int nWidth, int nHeight, UINT cPlanes, UINT cBitsPerPel, CONST VOID *lpvBits ); HBITMAP CreateCompatibleBitmap( HDC hdc, int nWidth, int nHeight ); 메모리 DC를 거치치 않고, 출력 장치로 출력 int SetDIBitsToDevice( HDC hdc, int XDest, int YDest, DWORD dwWidth, DWORD dwHeight, int XSrc, int YSrc, UINT uStartScan, UINT cScanLines, CONST VOID *lpvBits, CONST BITMAPINFO *lpbmi, UINT fuColorUse );
Menu 용어 액셀러레이터 메뉴 바(Menu) 팝업 메뉴(SubMenu)
Menu 속성 메뉴 관련 메시지 ID Caption WM_COMMAND 해당 메뉴들은 독립적인 ID 값을 가짐 메뉴의 내용 메뉴 관련 메시지 WM_COMMAND LOWORD(wParam) : 메뉴에 해당하는 ID HIWORD (wParam) : 0 lParam : 0
Menu Floating 팝업 메뉴 윈도우에서 기본적으로 제공해 주는 메뉴(WS_SYSMENU) 시스템 메뉴 ex) 메뉴 아이템 추가시 hSys = GetSystemMenu(hwnd, FALSE); AppendMenu(hSys, MF_STRING, 1, "Test"); Floating 팝업 메뉴 마우스 버튼 클릭시 그 위치에서 나타나는 메뉴 ex) case WM_RBUTTONDOWN: point.x = LOWORD(lParam); point.y = LOWORD(lParam); ClientToScreen(hwnd, &point); TrackPopupMenu(hsubMenu, PM_LEFTALIGN, point.x, point.y, 0, hwnd, NULL); Default Menu Item 팝업 메뉴를 더블 클릭하면 디폴트로 선택되는 메뉴 SetMenuDefaultItem()
Menu 생성 방법 방법 (1) 메뉴 편집기에서 생성 (2) LoadMenu(), SetMenu() 이용 윈도우 생성시 wndclass의 lpszMenuName에 resource ID 설정 (wc.lpszMenuName =“MyMenu") (2) LoadMenu(), SetMenu() 이용 여러 개의 메뉴를 생성시킨 후 윈도우에 붙임 (3) CreateMenu(), CreatePopupMenu() 프로그램 도중에 새로운 메뉴를 생성시킴
Menu 교체 각 상태에 맞는 메뉴를 실행 중에 로드 ex) 메뉴를 로드 : 프로그램이 종료되기 전에 직접 파괴 HMENU hMenu = LoadMenu(hInstance, "Menu"); SetMenu(hwnd, hMenu); 프로그램이 종료되기 전에 직접 파괴 DestroyMenu(hMenu);
Menu 생성 (실행중) 프로그램 실행중에 메뉴 생성 CreateMenu() --> AppendMenu() AppendMenu(hMenu, uFlag, MenuID, content) uFalg : MF_BITMAP, MF_OWNERDRAW, MF_STRING ex) HMENU hMenu = CreateMenu(); HMENU sMenu = CreatePopupMenu(); AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)sMenu,”Menu”); AppendMenu(sMenu, MF_STRING, ID_NEW,"&New\f1 tCtrl+N"); AppendMenu(sMenu, MF_STRING ,ID_OPEN,"&Open\f1 tCtrl+N"); AppendMenu(hMenu, MF_POPUP, (UINT)sMenu, "&File");
Menu 속성 변경 (1) 메뉴 초기화 메뉴의 상태 특정 메뉴의 체크 설정 및 해제 WM_INITMENU UNIT GetMenuState(HMENU hMenu, UINT uId, UINT uFlags); MF_BYCOMMAND, MF_BYPOSITION MF_CHECKED, MF_DISABLED, MF_GRAYED, MF_HILITE 특정 메뉴의 체크 설정 및 해제 CheckMenuItem(hMenu, uIDCheckItem, CheckFlag) MF_BYCOMMAND Indicates that the uIDCheckItem parameter gives the identifier of the menu item. MF_BYPOSITION Indicates that the uIDCheckItem parameter gives the zero-based relative position of the menu item. MF_CHECKED Sets the check-mark attribute to the selected state. MF_UNCHECKED Sets the check-mark attribute to the clear state. CheckMenuItem(hMenu, ID_NEW, MF_UNCHECKED); CheckMenuItem(hMenu, ID_NEW, MF_CHECKED);
Menu 속성 변경 (2) 특정 메뉴의 활성화 및 비활성화 기존 메뉴 수정 EnableMenuItem(hMenu, MenuID, ActiveFlag) EnableMenuItem(hMenu, ID_NEW, MF_CRAYED); EnableMenuItem(hMenu, ID_NEW, MF_ENABLED); 기존 메뉴 수정 BOOL ModifyMenu(HMENU hMenu, UINT uPostion, UINT uFlags, UINT uIDNewItem, LPCTSTR lpNewItem); 기존 메뉴의 내용이나 형태, 동작을 수정한다. ModifyMenu(hMenu, id, MF_BYCOMMAND, id, "&Save Selection");
Menu 속성 변경 (3) 메뉴 항목 관련 InsertMenu(hMenu, 위치, 메뉴 flag, 메뉴 ID, 문자열); DeleteMenu(hMenu, 위치, 메뉴 flag); 팝업 항목 사라짐 RemoveMenu(hMenu, 위치 , 메뉴 flag); 팝업 항목 그대로 DrawMenuBar(hWnd); 수정 후 메뉴를 다시 출력
기타 함수 메뉴를 얻음 메뉴의 문자열 얻기 상위 메뉴(설정된 메뉴) 서브 메뉴(팝업 메뉴) ex) HMENU hMenu; hMenu = GetMenu(hwnd) 서브 메뉴(팝업 메뉴) ex) HMENU hMenu; hMenu = GetSubMenu(hMenu, 0) // 0번째 서브메뉴 메뉴의 문자열 얻기 GetMenuString(hMenu, 메뉴 ID,문자열을 저장할 버퍼, 버퍼의 최대 크기, 메뉴 flag) MF_BYCOMMAND : 메뉴 ID 에 의해서 값을 얻음 MF_BYPOSITION : 0부터 시작하는 순서대로 ex) case WM_COMMAND: if (GetMenuString(hMenu, LOWORD(wParam), temp, 80, MF_BYCOMMAND) == 0) { MessageBox(hwnd, temp,"메뉴”, MB_OK); }
Menu 관련 메시지 WM_INITMENUPOPUP WM_CONTEXTMENU WM_MENUSELECT WM_COMMAND 메뉴가 활성화될 때, 즉 화면에 표시될 때 WM_CONTEXTMENU 마우스 오른쪽 버튼을 클릭했을 때 floating popup 메뉴 호출에 사용 가능 WM_MENUSELECT 메뉴 선택시 (항목위에 있을 경우) WM_COMMAND 메뉴 선택시 WM_HELP 메뉴에서 f1 누를 경우
응용 : 비트맵 메뉴 생성(1) 비트맵 메뉴 설정 메뉴에 나타낼 비트맵을 등록/로드 메뉴를 프로그램에서 작성해야 한다. ex) static HBITMAP hFileNew, hFileOpen,hFileMenu; hFileNew = LoadBitmap(hInstance, "New"); hFileOpen = LoadBitmap(hInstance, "Open"); hFileMenu = LoadBitmap(hInstance, "File Menu");
응용 : 비트맵 메뉴 생성 (2) 윈도우에 설정할 메뉴 생성 서브메뉴 생성 비트맵을 설정한 메뉴를 추가 hSubMenu = CreateMenu(); 비트맵을 설정한 메뉴를 추가 AppendMenu(hSubMenu, MF_BITMAP, ID_NEW, (PSTR)(LONG)hFileNew); AppendMenu(hSubMenu, MF_BITMAP, ID_OPEN, (PSTR)(LONG)hFileOpen); 생성한 메뉴를 parent menu에 붙임 AppendMenu(hMenu, MF_STRING|MF_POPUP, (UINT) hSubMenu, “Submenu");
Accelerator Hot Key - .res 파일에 저장 ex) Shift + SpaceBar(한/영 전환), Ctrl+Y(한 줄 삭제) // 엑셀러레이터를 메모리로 올린다. 엑셀러레이터 리소스의 이름은 “TestAccel”이다. hAccel = LoadAccelerators(hInstance, “TestAccel”); while(GetMessage(&msg, NULL, NULL, NULL)) { // 메시지 루프 안에서 엑셀러레이터 해석 함수를 집어 넣는다. if(!TranslateAccelerator(hWnd, hAccel, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (msg. wParam);
Other Resources String Table VersionInfo NULL로 끝나는 문자열 Data Segment를 절약하기 위해서 별도의 영역 차지 다국적 프로그램의 작성 문자열은 핸들러를 사용하지 않는다. 바로 버퍼로 이동 int LoadString( HINSTANCE hInstance, UINT uID, LPTSTR lpBuffer, int nBufferMax ); VersionInfo 모듈의 버전 정보 설치 프로그램에서 버전 정보를 비교 Resource Viewer를 사용하여 편집
8. Printing
Introduction 프린트 출력과 화면 출력 공통점 : GDI를 이용 차이점 : 프린터 출력은 제어코드가 필요 화면 출력은 GetDC, BeginPaint를 이용해서 DC 획득 프린터 출력은 CreateDC를 이용해서 DC 획득
Printing Step 프린터에 대한 DC를 얻는다. DC에 GDI를 이용해서 출력 작업 수행 CreateDC API를 이용해서 현재 선택된 프린터에 대한 DC 획득 DC에 GDI를 이용해서 출력 작업 수행 출력은 바로 종이로 나가는 것이 아니고, 프린터 매니저를 통해서 나간다. 종이와 같은 한정된 영역에 나가기 때문에, 새종이에 대한 출력의 시작과 끝을 알리는 코드 필요
관련된 API StartDoc 프린터 매니저에게 프린트 작업이 시작되었음을 알림, 프린터 출력을 파일로 보낼 수도 있음 StartPage 새로운 페이지의 출력이 시작되었음을 알림 EndPage 한 페이지의 출력이 끝났음을 알림 EndDoc 프린트 작업이 정상적으로 끝났음을 알림 AbortDoc 현재 프린트 작업을 종료하고, StartDoc 이후의 출력 작업 취소 (예) 프린터 작업을 취소, 에러가 발생할 경우 SetAbortDoc 프린트 도중에 발생하는 에러 처리 함수 ResetDC 프린터 출력 작업중에 종이의 방향이나 용지 설정의 전환 Printer DC를 얻는다. 프린트 작업을 시작(StartDoc API) for(page = 1; page <= 출력할 페이지 수; page++) { 새 페이지를 연다(StartPage API) 페이지의 내용을 GDI를 이용하여 printer DC에 출력 페이지를 닫는다(EndPage API) } 프린트 작업을 끝낸다(EndDoc API) Printer DC를 제거(DeleteDC)
Registry and INI file INI file in Windows 3.X Registry win.ini, system.ini, powerpnt.ini,…. 프로그램에 대한 정보가 산재 => 통합 관리의 어려움 여러 section으로 구성, key와 value로 표시 64KB의 한계 Registry 32MB까지 가능 INI file에 비하여 이진 데이터와 트리 형태의 데이터 저장 가능 regedit(윈도우 95), regedt32(윈도우 NT)로 편집 가능 HKEY_CLASSES_ROOT OLE 관련 정보, 셀 관련 정보 HKEY_LOCAL_MACHINE 시스템에 설치된 소프트웨어, 메모리, 그 외 하드웨어 HKEY_USERS 사용자 정보 HKEY_CURRENT_USER 현재 사용자의 환경에 관한 정보 저장 HKEY_CURRENT_CONFIG 시스템에 설치된 하드웨어 설정 정보 HKEY_DYN_DATA 시스템 성능에 대한 통계 정보
Printer DC의 생성 방법 win.ini를 이용 Registry를 이용 GetProfileString, WriteProfileString API를 이용 Registry를 이용 NT의 경우, win.ini이 존재하지 않음 프린트 관련 common dialog box를 이용 가장 효율적
HDC GetPrinterDC(BOOL bInvokeDlg) { HDC hDC; LPDEVMODE lpDevMode = NULL; LPDEVNAMES lpDevNames; LPSTR lpszDriverName; LPSTR lpszPortName; PRINTDLG pd; // pd를 초기화한다. pd.lStructSize = sizeof(PRINTDLG); pd.hwndOwner = hWnd; if(bInvokeDlg) pd.Flags = PD_RETURNDC; else pd.Flags = PD_RETURNDC | PD_RETURNDEFALUT; if(!PrintDlg(&pd)) // 프린터 작업이 취소되면, NULL을 리턴한다. return NULL; if(pd.hDC) // PrintDlg함수에서 프린터 DC를 얻었는지 검사. hDC = pd.HDC; else // 아니면, 프린터 DC를 직접 만든다. ……………………….. }
if(pd.hDC) // PrintDlg함수에서 프린터 DC를 얻었는지 검사. hDC = pd.HDC; if(pd.hDevNames) { GlobalFree(pd.hDevNames); pd.hDevNames = NULL; } if(pd.hDevMode) { return hDC;
Others in Printing 프린트 작업 취소 사용자가 SetAbortProc API를 이용 프린트 중간에 에러 발생 ex) SP_OUTOFDISK BOOL APIENTRY AbortProc(HDC hDC, int iError) { MSG msg; // 프린터 취소 함수를 띄우지 않았다면 바로 리턴한다. if(!bAbortDlgWnd) return TRUE; //사용자가 프린트 중단을 하지 않았다면 그 동안 큐에 있는 메시지를 처리 while(!bAbort && PeekMessage(&msg, NULL, NULL, NULL, TRUE)) { if(!IsDialogMessage(hAbortDlgWnd, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (!bAbort);
Creating Printing Cancel Dialog Box(1) Print Cancel Dialog Box Modeless dialog box ABORTDLG DIALOG DISCARDABLE 20, 20, 90, 64 STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU CAPTION “Print a File” BEGIN DEFPUSHBUTTON “Cancel”, IDCANCEL, 29, 44, 32, 14, WS_GROUP CTEXT “Sending”, -1, 0, 8, 90, 8 CTEXT “text”, IDC_FILENAME, 0, 18, 90, 8 CTEXT “to print spooler”, -1, 0, 28, 90, 8 END Dialog box template
Creating Printing Cancel Dialog Box(2) int APIENTRY AbortDlg(HWND hDlg, UINT msg, UINT wParam, LONG lParam) { switch(msg) case WM_COMMAND: return (bAbort = TRUE); case WM_INITDIALOG: SetFocus(GetDlgItem(hDlg, IDCANCEL)); SetDlgItemText(hDlg, IDC_FILENAME, szFileName); return TRUE; } return FALSE; Static control의 출력을 변경하기 위해서 SetDlgItemText 함수를 사용
Creating Printing Cancel Dialog Box(3) HWND hAbortDlgWnd; …………. /* 프린트 취소 대화상자를 생성한다. */ hAbortDlgWnd = CreateDialog(hInst, “AbortDlg”, hWnd, AbortDlg); if(!hAbortDlgWnd) { SetCursor(hSaveCursor); /* 커서를 원상 복구한다 */ return TRUE; } /* 프린트 취소 대화 상자를 보이게 한다. */ ShowWindow(hAbortDlgWnd, SW_NORMAL); UpdateWindow(hAbortDlgWnd);
Printing a Text to Paper(1) 절차 Printer DC를 얻는다(GetPrinterDC 호출) 프린터 작업을 시작한다(StartDoc 호출) 새 페이지를 연다(StartPage 호출) Printer DC로 텍스트 한줄을 출력한다(TextOut 호출) 페이지를 닫는다(EndPage 호출) 프린트 작업을 끝낸다(EndDoc 호출) Printer DC를 제거한다(DeleteDC 호출)
Printing a Text to Paper(2) HDC hDC; DOCINFO DocInfo; hDC = GetPrinterDC(); if(hDC != NULL) { // 프린터 job을 연다 DocInfo.cbSize = sizeof(DOCINFO); // StartDoc을 호출하기 위해 DOCINFO를 초기화 DocInfo.lpszDocName = “Test”; // 프린터 매니저에 표시되는 출력 문서의 이름 // NULL이면 프린터 출력, 아니면 그 이름의 파일로 출력 DocInfo.lpszOutput = (LPSTR)NULL; StartDoc(hDC, &DocInfo); // 한 페이지의 출력을 연다. StartPage(hDC); TextOut(hDC, 10, 10, “A Single Line of a Text”, 22); //페이지의 출력을 닫는다 EndDoc(hDC); DeleteDc(hDC); };
9. MDI
Introduction MDI(Multiple Document Interface) 하나의 메인 윈도우에 여러 개의 자식 윈도우를 올려 놓아 사용하는 방법 ex)윈도우 3.1 Program Manager, MS-Word SDI(Single Document Interface) Microsoft에서 SDI를 권장함 ex)NotePad(메모장), WordPad
MDI Program Structure(1) MDI Main(Frame) Window MDI Client Window MDI Child Window 윈도우를 정의 WNDCLASS를 채움 RegisterClass를 이용하여 등록 Window Procedure를 만듬 MDI Main Window File Edit … Window MDI Child #1 MDI Child Window MDI Child #2 MDI Client Window
MDI Program Structure(2) Popup Window와 Child Window Popup Window Parent Window가 필요 Parent Window의 영역 밖으로 나갈 수 있음 Child Window의 경우는 나갈 수 없음 CreateWindow로 생성 부모 윈도우의 핸들을 준다 윈도우 ID를 주지 않는다. 자식 윈도우의 경우는 윈도우 ID를 준다 윈도우 스타일을 (WS_POPUP | WS_VISIBLE)을 준다. 자식 윈도우의 경우는 WS_CHILD를 준다
MDI Program 작성(1) 윈도우 클래스의 등록 MDI 프레임 윈도우, MDI자식 윈도우 정의 2개의 윈도우를 등록 WNDCLASS wc; // MDI 프레임 윈도우 클래스를 등록 wc.style = 0; wc.lpfnWndProc = MDIFrameWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(MDIFRAME)); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE + 1); wc.lpszMenuName = “MDIMENU”; wc.lpszClassName = “MDIFRAME”;
MDI Program 작성(2) 윈도우 클래스의 등록(cont’d) MDI 자식 윈도우와 일반 윈도우의 차이점 if(RegisterClass(&wc)) { // MDI 자식 윈도우 클래스를 등록한다 wc.lpfnWndProc = MDIChildWndProc; wc.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(MDICHILD)); wc.lpszMenuName = NULL; wc.cbWndExtra = CBWNDEXTRA; wc.lpszClassName = “MDIChild”; if(!RegisterClass(&wc)) return FALSE; } else return FALSE; MDI 자식 윈도우와 일반 윈도우의 차이점 MDI 자식 윈도우는 반드시 Icon을 갖는다 MDI 자식 윈도우는 윈도우 Extra byte를 할당해야 한다
MDI Program 작성(3) 윈도우의 생성 MDI 메인 윈도우의 생성 MDI 자식 윈도우의 생성 - WM_CREATE 처리부 HWND hwndFrame; hwndFrame = CreateWindow(“MDIFrame”, “MDI Application”, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDDEFAULT, CW_USEDDEFAULT, CW_USEDDEFAULT, CW_USEDDEFAULT, NULL, NULL, hInst, NULL) ; if(!hwndFrame) return FALSE; ShowWindow(hwndFrame, nCmdShow); UpdateWindow(hwndFrame); MDI Main Window의 생성
MDI Program 작성(4) 윈도우의 생성(cont’d) case : WM_CREATE: { CLIENTCREATESTRUCT ccs; ccs.hWindowMenu = GetSubMenu(GetSubMenu(hWnd), WINDOWMENU); ccs.idFirstChild = ID_WINDOW_CHILD; hwndMDIClient = CreateWindow(“MDICLIENT”, NULL, WS_CHILD | WS_CLIPCHILDREN, 0, 0, 0, 0, hwndFrame, hInst, (LPSTR)&ccs); ShowWindow(hwndMDIClient, SW_SHOW); break; } MDI Child Window의 생성 : MDI Frame 윈도우의 프로시져
MDI Program 작성(5) 메시지 루프의 변경 단축키의 처리(CTRL+F4, CTRL+F6) TranslateMDISysAccel API를 이용해서 MDI 클라이언트 윈도우가 메시지를 처리할 기회 제공 While(GetMessage(&msg, NULL, NULL, NULL)) { if(!TranslateMDISysAccel(hwndMDIClient, &msg)) { TranslateMessage(&msg); Dispatchmessage(&msg); }
MDI Program 작성(6) 윈도우 프로시져의 작성 MDI 자식 윈도우를 제어하기 MDI Client 윈도우에 메시지를 보내서, MDI Child 윈도우를 제어 MDI 자식 윈도우의 생성 MDI Client 윈도우에 WM_MDICREATE 메시지를 보냄 MDI Frame 윈도우 프로시져 자신이 처리하지 못하는 메시지는 DefFrameProc 이 처리 MDI Child 윈도우 프로시져 자신이 처리하지 못하는 메시지는 DefMDIChildProc이 처리
MDI Program 작성(7) MDI 자식 윈도우의 제거 활성화된 MDI 자식 윈도우의 제거 HWND hwnd; MDICREATESTRUCT mcs; mcs.szTitle = szName; // 생성할 MDI 자식 윈도우의 갭션을 등록한다. mcs.szClass = “MDIChild”; // MDI 자식 윈도우의 클래스를 등록한다. mcs.x = mcs.y = mcs.cx = mcs.cy = CW_USEDEFAULT; // 시작위치, 폭, 높이 mcs.style = 0; // MDI클라이언트 윈도우에게 자식 윈도우를 생성시킨다. hwnd = SendMessage(hwndMDIClient, WM_MDICREATE, 0, (LPARAM)(LPMDICREATESTRUT)&mcs); ShowWindow(hwnd, SW_SHOW); MDI 자식 윈도우의 제거 활성화된 MDI 자식 윈도우의 제거 HWND hwndChild; hwndChild = (HWND)SendMessage(hwndClient, WM_MDIGETACTIVE, 0, 0L); SendMessage(hwndClient, WM_MDIDESTROY, hwndChild, 0L);
MDI Program 작성(8) MDI 자식 윈도우의 활성화와 비활성화 WM_MDINEXT wParam의 앞 윈도우나 뒤 윈도우를 활성화시킨다. lParam == TRUE이면, 이전 윈도우를 활성화 lParam == FALSE이면, 앞 윈도우를 활성화 WM_MDIACTIVE wParam로 지정한 MDI 자식 윈도우를 활성화시킨다. WM_MDIGETACTIVE 현재 활성화된 MDI자식 윈도우에 대한 포인터를 얻는다. lParam으로 BOOL 타입의 포인터를 지정해서 최대화 여부 지정 WM_MDIMAXIMIZE wParam으로 지정한 MDI 자식 윈도우를 최대화 시킨다. WM_MDIMINIMIZE wParam으로 지정한 MDI 자식 윈도우를 최소화 시킨다.
MDI Program 작성(9) 자식 윈도우의 정렬 창 <창 메뉴의 전형적인 구성> 바둑판 배열 계단 배열 WM_MDICASCADE MDI 자식 윈도우들을 계단식으로 배열한다 WM_MDITITLE MDI 자식 윈도우들을 바둑판식으로 배열한다. wParam에 MDITITLE_HORIZONTAL을 넣으면, 수평으로 배열 wParam에 MDITITLE_VERTICAL을 넣으면, 수직으로 배열 WM_MDIICONARRAGNE 아이콘화된 자식 윈도우를 정렬한다. 창 바둑판 배열 계단 배열 아이콘 정렬 1. Test.gif 2. 한.gif <창 메뉴의 전형적인 구성>
MDI 자식 윈도우의 데이터 설정하기(1) char KeyIn = ‘A’; LONG APIENTRY MDIChildWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_CHAR: { ch = wParam; // ch에 키값을 저장 InvalidateRect(hWnd, NULL, TRUE); //WM_PAINT 발생 break; } case WM_PAINT: { HDC hDC; PAINTSTRUCT ps; hDC = BeginPaint(hWnd, &ps); lpChar[0] = ch; lpChar[1] = NULL; TextOut(hDC, 10, 10, lpChar, 1); // 출력... EndPaint(hWnd, &ps); default: return DefMDIChildProc(hWnd, msg, wParam, lParam); return 0;}
MDI 자식 윈도우의 데이터 설정하기(2) WNDCLASS의 Extra byte이용하기 cbWndExtra 필드에 양수값을 할당 : 각 윈도우마다 cbWndExtra의 공간만큼 할당 인덱스를 이용해 데이터에 접근 SetWindowLong, GetWindowLong LONG APIENTRY MDIChildWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_CREATE: { HGLOBAL hGlobal; LPSTR lpChar; hGlobal = GlobalAlloc(GMEM_MOVALBE, 1); // 1바이트 할당 if(hGlobal == NULL) return -1L; lpChar = (LPSTR)GlobalLock(hGlobal); lpChar[0] = 0;
GlobalUnlock(hGlobal); SetWindowLong(hWnd, 0, hGlobal); break; } case WM_DESTROY: { // 종료될 때 할당했던 메모리를 제거한다. GlobalFree(GetWindowLong(hWnd, 0); case WM_CHAR: { LPSTR lpChar; HGLOBAL hGlobal; hGlobal = (HGLOBAL) GetWindowLong(hWnd, 0); lpChar = (LPSTR)GlobalLock(hGlobal); lpChar[0] = (char)wParam; InvalidateRect(hWnd, NULL, TRUE);
case WM_PAINT: { HDC hDC; PAINTSTRUCT ps; char lpStr[2]; LPSTR lpChar; HGLOBAL hGlobal; hGlobal = (HGLOBAL)GetWindowLong(hWnd, 0); lpChar = GlobalLock(hGlobal); hDC = BeginPaint(hWnd, &ps); lpStr[0] = lpChar[0]; lpStr[1] = NULL; TextOut(hDC, 10, 10, lpStr, 1); EndPaint(hWnd, &ps); GlobalUnlock(hGlobal); break; } default: return DefMDIChildProc(hWnd, msg, wParam, lParam); return 0L;
MDI 자식 윈도우의 데이터 설정하기(3) GetWindowLong의 기타 용도 윈도우 관련 정보를 접근할 때 사용 두 번째 인자 GWL_EXSTYLE 이 윈도우의 확장 스타일을 얻어 온다 GWL_STYLE 이 윈도우의 스타일을 얻어 온다 GWL_WNDPROC 이 윈도우의 프로시져 주소를 얻어 온다. CallWindowProc함수를 이용해 호출할 수 있다. GWL_HINSTANCE 이 윈도우가 속한 인스턴스 핸들을 얻어 온다 GWL_ID 윈도우 ID를 얻어 온다. 자식 윈도우일 경우에만 의미가 있다. GWL_HWNDPARENT 이 윈도우가 자식 윈도우라면, 부모 윈도우 핸들을 얻어 온다. DWL_DLGPROC 지정한 윈도우가 대화 상자일 때만, 의미가 있다. 대화상자의 프로시저의 주소를 돌려준다.
MDI 자식 윈도우의 데이터 설정하기(4) 윈도우 프로퍼티 이용하기 필요한 영역을 확보하고, 문자열로 접근 장점 : Extra Byte에 비하여 사용이 쉽다. 단점 : Extra Byte에 비하여 속도가 느리다. HANDLE GetProp( HWND hWnd, LPCTSTR lpString ); BOOL SetProp( HWND hWnd, LPCTSTR lpString, HANDLE hData ); HANDLE RemoveProp( HWND hWnd, LPCTSTR lpString );
9.DLL
DLL의 등장 배경 고전적인 프로그래밍 모델 하드웨어 인프라의 발전과 GUI로의 전환 해결 방안 소스 코드를 작성하고 컴파일해서 실행 파일 생성 하드웨어 인프라의 발전과 GUI로의 전환 요구되는 내장 기능이 늘어남 추가되는 코드의 양도 늘어남 실행 파일의 크기가 기하급수적으로 증가 메모리 요구량이 증가 로딩 속도 및 수행 속도 저하 프로그램간에 코드와 리소스를 중복해서 가지고 있기 때문에 리소스 사용이 비효율적 해결 방안 프로그램의 모듈화 중복되는 코드와 리소스 공유
DLL이란? (1) DLL(Dynamic Link Library) DLL vs. SLL 1983년 MS사의 Steve Wood가 고안 실행중 함수 호출시에 메모리로 load되어 실행 DLL vs. SLL SLL 컴파일 결과 생성된 오브젝트 파일과 소스에서 사용한 라이브러리가 합쳐져서 실행 파일을 구성한다. 즉, 라이브러리 코드 자체를 복사 DLL 링크시(실행파일 생성시)에는 라이브러리 이름과 사용한 함수의 위치만이 복사된다. 실행중에 함수를 실제로 사용할 시점이 되면 라이브러리를 로드하여 함수를 실행시킨다.
DLL이란? (2) DLL과 SLL(Static Link Library)의 비교 strcpy compile a1.c 실행 파일 a1.obj link SLL에서 해당 코드 자체가 copy된다. SLL호출 DLL에서 해당 코드 이름과 위치가 기록된다. DLL호출
DLL이란? (3) DLL을 로드한 응용 프로그램의 메모리맵 프로그램A코드 프로그램B코드 프로그램A코드 프로그램B코드 공유 데이터 인스턴스데이터A 인스턴스데이터B 프로세스A 프로세스B 프로그램A코드 DLL 코드 공유 데이터 인스턴스데이터A 프로그램B코드 DLL 코드 공유 데이터 인스턴스데이터B
DLL의 장점 (1) (1) 메모리의 효율적인 사용 코드 공유 리소스 공유 Reference count 관리 윈도우와 같은 멀티태스킹 환경에서는 동시에 여러 개의 프로그램이 실행되며 같은 코드를 사용하는 경우가 많다. CreateWindow, GetDC 같은 윈도우 API들은 모든 윈도우 응용 프로그램에서 사용 SSL을 이용한다면 각 프로그램마다 윈도우 API들이 중복되어 들어가게 되므로 메모리 낭비 DLL을 사용하면 실행 파일 안에는 함수의 위치만 기록하고 코드를 공유하므로 메모리를 획기적으로 절감 리소스 공유 자주 사용하는 비트맵 등에 이용 가능 Reference count 관리
DLL의 장점 (2) (2) 모듈화 및 재사용성 증가 (3) 플랫폼 버전 차이에 따른 문제를 해결 Reference count : dll을 참조하는 프로그램의 개수 Reference count가 0이 되면 메모리에서 제거 (2) 모듈화 및 재사용성 증가 (3) 플랫폼 버전 차이에 따른 문제를 해결 (4) 다목적용 프로그램의 작성 사용자의 요구에 맞게 dll만 바꾸면 됨 다국적용 프로그램의 작성 (5) 혼합 프로그래밍이 가능하다. ex) UI는 Visual Basic으로 작성하고 기능은 C++로 작성 (6) 기타 Device driver, Custom control을 DLL로 작성 VBX(Visual Basic eXtension), OCX(OLE Control eXtension), ActiveX
DLL 개요(1) SDK API들이 DLL로 제공 VC++ 4.0부터 C Libarary도 가능 기본적인 확장자는 dll 이 경우 자동으로 로드할 수도 있고 LoadLibrary를 이용해 명시적으로 로드 가능 확장자가 dll이 아니어도 상관없다 확장자가 dll이 아닌 경우는 모든 일을 Programmer가 수행 LoadLibrary, GetProcAddress, FreeLibrary API 이용 HANDLE hLibrary; HMENU hMenu; hLibrary = LoadLibrary(“MENULIB.EXE”); hMenu = LoadMenu(hLibrary, “MainMenu”); ……… FreeLibrary(hLibrary); 일종의 dll
DLL 개요 (2) DLL을 찾는 순서 실행 파일이 있는 디렉토리 현재 디렉토리 윈95: 시스템 디렉토리, NT: SYSTEM32 디렉토리 : GetSystemDirectory API 윈도우 디렉토리 : GetWindowsDirectory API PATH 환경변수에 기록된 디렉토리
Win32 DLL에서 달라진 점(1) (1) DllMain Dll의 시작과 끝은 DllMain에서 처리 int WINAPI DllMain(HANDLE hInst, ULONG umessage, LPVOID lpReserved); DLL_PROCESS_ATTACH Dll을 사용하는 프로세스가 생길 때 호출 DLL_PROCESS_DETACH Dll을 사용하는 프로세스가 사라질 때 호출 DLL_THREAD_DETACH Dll을 사용하는프로세스에서 쓰레드가 생성될 떄 호출 DLL_ THREAD _DETACH Dll을 사용하는 프로세스에서 쓰레드가 사라질 때 호출 Dll을 사용하려는 프로그램에 관한 정보를 OS에게 알려 주는 일종의 메시지
Win32 DLL에서 달라진 점(2) (2) DLL의 데이터 세그먼트 Threads and Multi-Threads 쓰레드간에는 공유 문제 존속 프로세스당 데이터 세그먼트를 할당하기 때문애 멀티 쓰레드 환경에서는 쓰레드간에 충돌 가능 TLS(Thread Local Storage) 사용-> thread-safe dll Threads and Multi-Threads Threads : CPU를 할당하는 최소 단위 하나의 프로세스는 하나이상의 쓰레드로 구성 Multi-Threads : 2개의 이상의 쓰레드가 작동
DLL 만들기(1) 작성 컴파일 DLL Linking 일반 프로그램과 같이 헤더, 소스, rc, 모듈 정의 파일로 구성 Dynamic library(.dll)와 import library(.lib) 생성 DLL Linking Implicit linking 프로그램이 시작하면서 사용하는 dll을 바로 로드 Import library 지정 라이브러리 함수들의 위치 저장 Explicit linking 실행 중에 API를 호출하여 명시적으로 dll을 로드 Import library가 필요없다.
DLL 만들기(1) (1) Dll의 헤더 파일 Dll에서 제공하는 함수들에 대한 정보 #ifndef __DLL32_H__ #define __DLL32_H__ #ifdef __cplusplus__ extern “C” { #endif int APIENTRY DLL32TestFunc(HWND hWnd, LPCSTR lpStr); } #endif // __DLL32_H__
DLL 만들기(2) DLL 소스 파일 Import Library를 생성 DllMain 필요 Dll을 구성하는 함수 정의 라이브러리 함수에는 반드시 APIENTRY를 붙여야 한다. Import Library를 생성 모듈 정의 파일 이용 모듈 정의 파일의 EXPORTS문에 라이브러리 함수를 나열 함수 정의시에 __decspec(dllexport) 사용 int APIENTRY DLL32TestFunc(HWND hWnd, LPCSTR lpStr) { // ………... 라이브러리 함수들 앞에는 꼭 붙어야 됨
DLL 만들기(3) Library DLL32 CODE EXECUTE READ DATA READ WRITE EXPORTS DLL32TestFunc @1 __declspec(dllexport) int APIENTRY DLL32TestFunc(HWND hWnd, LPCSTR lpStr) { // …………………... }
실행파일에서 DLL호출하기(1) 2가지 방법 존재 메모리 로드 시점 메모리 해제 시점 장점 프로그램이 실행될 때 LoadLibrary를 호출했을 때 Import Library 사용 LoadLibrary 등의 API 사용 프로그램이 종료될 때 FreeLibrary를 호출했을 때 사용이 간편 DLL 로드 시점을 정할 수 있다. 해당 DLL이 없을 경우에 대처 가능 단점 불필요하게 DLL이 메모리에 남아 있는 경우가 발생, DLL 이 없는 경우 프로그램이 실행 안 됨 사용이 복잡
실행파일에서 DLL호출하기(2) (1) Import Library 사용 (2) LoadLibrary 등의 API 사용 Dll의 import library를 project에 추가 [Project] ->[Settings]->[Link]에서 [Object/Library modules]에 추가 추가 후에는 일반 API처럼 사용 (2) LoadLibrary 등의 API 사용 절차 LoadLibrary 함수로 dll을 load한다 GetProcAddress로 원하는 함수의 주소를 얻어낸다 앞에서 얻어낸 함수 주소로 호출한다 FreeLibrary로 dll을 메모리에서 제거한다
DLL32TestFunc(hWnd, “테스트 중입니다.”); Import Library 사용 typedef int (APIENTRY *FPDLL32TestFunc) (HWND, LPSTR); HANDLE hLibrary; FP32DLLTestFunc lpfnDLL32TestFunc; hLibrary = LoadLibrary(“dll32.dll”); if(hLibrary) { lpfnDLL32TestFunc = GetProcAddress(hLibrary, “DLL32TestFunc”); if(lpfnDLL32TestFunc) lpfnDLL32TestFunc(hWnd, “Testing in Main Process”); } FreeLibrary(hLibrary); LoadLibrary 등의 API 사용
실습 (1) (1) Implicit linking Dll 작성 Dll을 생성하고 DllMain이 호출되는 각 경우에 메시지 박스 추가 간단한 메시지 박스를 출력하는 dll fuction 정의 정수를 parameter로 받아서 factorial을 계산하는 dll fuction 정의 def 파일을 작성한 후 컴파일해서 dll과 lib 생성 Dll을 호출할 프로그램 작성 Win32 프로젝트를 생성하고 library import 프로그램 내에서 두가지 dll 함수 사용
실습 (2) (2) Explicit linking Dll 작성 이전에 작성한 dll을 그대로 사용 Win32 프로젝트를 생성 Library에서 import할 함수의 type 설정 두가지 종류의 함수를 실제로 호출
실습 (3) (3) Resource dll Dll 작성 MFC dll with static linked library 사용자의 아이디와 암호 입력을 받아들이는 login dialog 생성 다이얼로그 클래스 정의 Login dialog를 띄우는 dll 함수 정의 def에 export할 함수 추가 컴파일해서 lib과 dll 생성 Dll을 호출할 프로그램 작성 LoadLibrary, GetProcAddress를 이용하여 로그온 다이얼로그를 띄우고 아이디와 암호를 메시지 박스로 보여주기
10.Memory, File I/O, Clipboard
Win32 메모리 구조 (1) Win32 메모리 모델 메모리 관리의 단순화 디스크의 일부를 메모리로 사용 small, large, huge 모델이 사라짐 (near, far 메모리 구별도 없다) 디스크의 일부를 메모리로 사용 메모리의 총량 물리적인 메모리 + 하드 디스크의 paging file
Win32 메모리 구조 (2) Memory with infinite capacity Physical Logical Virtual Memory Physical Logical Memory with infinite capacity
Win32 메모리 구조 (3) Virtual Memory 메모리에 프로그램이 적재될 때 반드시 연속적으로 적재될 필요는 없다 가상 주소 공간과 실 주소 공간의 분리 가상 주소 공간 : 현재 진행중인 프로세스가 생성하는 주소의 집합 실 주소 공간 : 주 기억 장치에서 사용가능한 주소 사용자에게는 실제 메모리의 특성을 감춘다. 사용자는 컴퓨터가 2n byte의 메모리를 가지고 있는 것으로 생각한다. 가상 메모리의 일부만이 실제 메모리에 할당된다.
Win32 메모리 구조 (5) Win32 메모리 모델 Virtual Address Translation 각 프로세스의 주소공간은 독립적 프로세스 생성시마다 4GB의 주소공간을 생성 가상적인 주소 공간 물리적인 메모리와 연결될 수 있는 메모리 번지 프로세스가 사용하는 주소는 메모리의 실제 물리적 위치를 나타내지 않음 프로세스간에 영향을 끼치지 않는다 Virtual Address Translation Page Table 물리적인 메모리와 논리적인 주소 공간을 대응
Win32 메모리 구조 (6) Virtual Address Translation
Memory Allocation ex) hMem=GlobalAlloc(..); Win32 API : 전역할당과 지역할당 동일 지역할당 - 프로세스의 주소 공간 안에 전역할당 - 프로세스의 주소 공안 외부에 Win32 : 두 종류의 메모리를 모두 프로세스용 주소 공간에 할당 메모리 할당(Handle) 메모리 할당시 실제 시작 주소 대신에 handle을 리턴 메모리 관리를 OS가 처리 Lock을 걸면, 핸들대신에 주소가 리턴 ex) hMem=GlobalAlloc(..); hMem 은 핸들 테이블에서 인덱스를 가리킨다. 메모리 관리가 필요한 이유 프로그램은 최상의 속도로 실행되어야 한다. 저장 공간의 차별화 (램, 디스크..) 메모리 관리 방법 가상 메모리 관리자 : 프로그램이 큰 가상 메모리 블록을 예약한 후 그 예약된 가상 메모리 블록을 나중에 할당 지역 힙 관리자 : 프로그램이 새 힙 관리자로 복수의 서로 다른 힙을 만듬
Memory Type 전역 메모리와 지역 메모리 디스크의 용량의 일부를 메모리로 간주 Win32에서는 전역 힙과 지역 힙을 구분하지 않는다. 디스크의 용량의 일부를 메모리로 간주 제한된 물리적 메모리 + 페이징 파일 실제 메모리= RAM + SWAP file 4KB 단위의 페이지 단위로 관리 할당받는 순서 Free Page --> Reserved page --> Committed page Page Table : 캐쉬 메모리 보통은 시스템에서 관리 프로그램 실행시 코드 이동을 결정
Memory Type 힙(Heap) 가상 메모리 관리자 프로그램에서 힙을 생성하면 0x7fffffff부터 시작하는 힙을 키움 프로그램은 실제 메모리를 다루지 않음 각 프로세스에게 개별적인 주소 공간을 제공 각 프로세스는 0x00000000에서 0xffffffff 범위의 메모리가 있는 것으로 인식 쓰레드는 해당 프로세스에 속한 메모리만 access 가능 힙(Heap) 프로그램에서 힙을 생성하면 0x7fffffff부터 시작하는 힙을 키움 개별 힙의 메모리는 그 힙을 만든 프로세스에서만 사용 DLL에서 힙을 생성시
Heap Memory Functions (1) 표준 메모리 함수 malloc, calloc, realloc ex) char *data = malloc(512); // calloc(512) // realloc(512) 윈도우 메모리 함수 HGLOBAL GlobalAlloc(UINT uFlags, SIZE_T dwBytes); The GlobalAlloc function allocates the specified number of bytes from the heap GMEM_FIXED 고정된 형태로 메모리 설정 GMEM_MOVEABLE 메모리가 이동이 된다 GPTR FIXED && ZEROINIT GHND MOVEABLE && ZEROINIT GMEM_ZEROINIT 모두 0으로 초기화 GMEM_SHARE 메모리를 공유
Heap Memory Functions (2) GlobalLock(할당한 메모리 핸들) The GlobalLock function locks a global memory object and returns a pointer to the first byte of the object's memory block LPSTR datapoint; LPSTR datapoint2; HANDLE hdata; hdata = GlobalAlloc(GHND,512); datapoint = (LPSTR)GlobalLock(hdata); // 데이터 사용 GlobalUnlock(hdata); datapoint2 = (LPSTR)GlobalLock(hdata);
Heap Memory Functions (3) HGLOBAL GlobalFree(메모리 핸들); The GlobalFree function frees the specified global memory object and invalidates its handle HGLOBAL GlobalDiscard(메모리 핸들); Although GlobalDiscard discards the object's memory block, the handle to the object remains valid. The process can subsequently pass the handle to the GlobalReAlloc function to allocate another global memory block identified by the same handle HGLOBAL GlobalReAlloc(HGLOBAL hMem, SIZE_T dwBytes, UINT uFlags); The GlobalReAlloc function changes the size or attributes of a specified global memory object. The size can increase or decrease. ex) hdata = GlobalRealloc(hdata, GHND, 1024);
Heap Memory Functions (4) VOID GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer); ex) MEMORYSTATUS ms; GlobalMemoryStatus(&ms); if (ms.dwAvailVirtual < msize) { // 메모리 부족 } else { // 메모리 할당 }
Virtual Memory (1) Virtual Memory Functions 이점 Win32 API provides a set of virtual memory functions That enable a process to manipulate or determine the status of pages in its virtual address space 이점 메모리를 예약 상태로 할당할 수 있다. 물리적인 메모리를 소비하지 않으면서, 주소 공간만 미리 할당 필요할 경우에 확정해서 사용 메모리의 액세스 권한을 지정 LPVOID VirtualAlloc(LPVOID lpAddress, DWORD dwSize, DWORD flAllocationType, DWORD flProtect) The VirtualAlloc function reserves or commits a region of pages in the virtual address space of the calling process lpAddress : 메모리 시작 번지 , 처음 시작시에는 항상 NULL
Virtual Memory (2) flAllocation Type flProtect MEM_COMMIT – 지정한 페이지 영역에 대해 물리적 기억 장치 할당 (메모리나 디스크에 있는 페이징 파일) MEM_RESERVE - 물리적 기억 장치를 할당하지 않고 프로세스의 가상 주소 공간의 범위를 예약 MEM_TOP_DOWN - 사용할 수 있는 최상위 주소에서 메모리를 할당 flProtect PAGE_READONLY 일기 전용 PAGE_READWRITE 읽고 쓰기 PAGE_EXCUTE 실행 가능한 데이터 PAGE_EXCUTE_READ 실행 가능하며 읽기 PAGE_EXCUTE_READWRITE 실행 가능하며 읽고 쓰기
Virtual Memory (3) BOOL VirtualFree(LPVOID lpAddress, DWORD dwSize, DWORD dwFreeType); MEM_DECOMMIT : 할당된 페이지를 할당 해제한다. MEM_RELEASE : 예약된 페이지를 예약 해제한다.
Virtual Memory (4) 예약과 할당 할당 단위 보호 속성 가상메모리는 “Page” 단위로 구성 Intel : 4KB 가상 메모리를 구성하는 각 페이지의 상태 Free : 사용되지 않는 자유 영역 Reserved : 장래 사용을 위해 예약만 되어 있는 페이지 Commit : 물리적 메모리가 할당되어 있는 상태, 바로 사용 가능 할당 단위 64KB 단위로 할당 보호 속성 BOOL VirtualProtect(LPVOID lpAddress, DWORD dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); The VirtualProtect function changes the access protection on a region of committed pages in the virtual address space of the calling process
Virtual Memory (5) 메모리 잠금 속도가 중요한 프로그램일 경우 특정 데이터가 반드시 RAM에만 존재하게 할 수 있다. BOOL VirtualLock(LPVOID lpAddress, DWORD dwSize); The VirtualLock function locks the specified region of the process's virtual address space into physical memory, ensuring that subsequent access to the region will not incur a page fault BOOL VirualUnlock(LPVOID lpAddress, DWORD dwSize);
Heap (1) 정의 및 장점 가상 메모리 공간상의 예약된 주소 공간 프로세스 생성시 작은 메모리 블록을 할당하는데 사용 1MByte의 디폴트 힙 생성 단순히 예약 작은 메모리 블록을 할당하는데 사용 요구한 만큼만 할당, 경제적이다. Win32에서는 힙의 구분이 없고, 지역 힙만이 존재 필요에 따라 다수 개의 힙을 생성할 수 있다.
Heap (2) 할당 HANDLE GetProcessHeap(VOID) The GetProcessHeap function obtains a handle to the heap of the calling process This handle can then be used in subsequent calls to the HeapAlloc, HeapReAlloc, HeapFree, and HeapSize functions LPVOID HeapAlloc(HANDLE hHeap, DWORD dwFlags, DWORD dwBytes); BOOL HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem); LPVOID HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, DWORD dwBytes); The HeapReAlloc function reallocates a block of memory from a heap This function enables you to resize a memory block and change other memory block properties
Heap (3) 새로운 힙 생성 HANDLE HeapCreate(DWORD flOption, DWORD dwInitalSize, DWORD dwMaximumSize); The HeapCreate function creates a heap object that can be used by the calling process The function reserves space in the virtual address space of the process and allocates physical storage for a specified initial portion of this block BOOL HeapDestroy(HANDLE hHeap); The HeapDestroy function destroys the specified heap object HeapDestroy decommits and releases all the pages of a private heap object, and it invalidates the handle to the heap
File I/O (1) 파일을 열거나 생성 HANDLE CreateFile(LPSTR lpFileName, DWORD dwDesiredAccess, DWODD dwShardMode, LPSECURITYATTRIBUTS lpSecurityAttributes, DWORD dwCreationDistribution, DWORD dwFlagAndAttributes, HANDLE hTempleteFile); The CreateFile function creates or opens the following objects and returns a handle that can be used to access the object lpfileName : 파일명 dwDesiredAccess 읽고 쓰기 모드 (GENERIC_READ, GENERIC_WRITE) dwCreationDistribution Specifies which action to take on files that exist CREATE_NEW, CREATE_ALLWAYS, OPEN_EXISTING, OPEN_ALWAYS
File I/O (2) ex) HANDLE hFile = CreateFile("test.c", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0) 파일 읽기와 쓰기 BOOL WriteFile( 파일 핸들, 기록할 데이터, 바이트 수 , 실제로 쓰여진 바이트, I/O overlapped 구조체) BOOL ReadFile( 파일 핸들, 기록할 데이터, 바이트 수, 실제 읽혀진 바이트, I/O overlapped 구조체) I/0 overlapped 구조체 : 디바이스 파이프 통로, 일반 파일에서는 NULL
File I/O (3) 파일 닫기와 기타함수 CloseHandle(hfile) MoveFile 특정 파일을 다른 파일 이름으로 변경, 이동 CopyFile 특정 파일을 새로운 파일로 복사 CreatDirectory 디렉토리를 만듬 DeleteFile 특정 파일을 지움 GetFileSize 파일의 크기를 얻음 FindFirstFile 특정 파일을 찾음 FindNextFile FindFirstFile에 의해서 찾은 다음에 특정 파일 찾음
Clipboard (1) 클립보드 클립보드 지원 (Edit 컨트롤) 클립보드에 들어갈 수 있는 데이터 프로그램간에 또는 프로그램 내부적으로 교환할 데이터를 잠시 저장해 두는 곳 시스템이 관리, 시스템 전체를 통틀어 하나밖에 없다. 클립보드 지원 (Edit 컨트롤) WM_CUT WM_COPY WM_PASTE 클립보드에 들어갈 수 있는 데이터 텍스트, 비트맵, RTF, 메타파일
Clipboard (2) Text 복사하기 메모리 할당 클립보드를 연다 클립보드를 비운다. 클립보드에 데이터 저장 BOOL OpenClipboard(HWND hWndNewOwner); 클립보드를 연 윈도우 핸들이 인수로 전달 클립보드를 비운다. EmptyClipboard(); 클립보드에 데이터 저장 HANDLE SetClipboardData(UINT uFormat, HANDLE hMem); 클립보드를 닫는다. CloseClipboard(); uFormat 설명 CF_BITMAP 비트맵 CF_TEXT 널 종료 문자열 CF_WAVE 오디오 데이터
Clipboard (3) 붙여넣기 클립보드에 원하는 포맷의 데이터가 있는지 확인 클립보드를 연다. BOOL IsClipboardFormatAvailable(UINT format); 클립보드를 연다. 보관된 데이터의 핸들을 얻어온다. HANDLE GetClipboardData(UINT uFormat); The GetClipboardData function retrieves data from the clipboard in a specified format The clipboard must have been opened previously 클립보드를 닫는다.
Clipboard (4) 클립보드 조작 OpenClipBoard(), CloseClipBoard(); BOOL IsClipboardFormatAvailable(UINT format) ex) BOOL check; OpenClipBoard(); if(ISClipBoardFormatAvailable(CF_TEXT)) { //클립보드에서 데이터를 얻는다 } CloseClipBoard(); EmptyClipBoard(); // 클립보드의 데이터 삭제
Clipboard (5) 클립보드에서 데이터 얻기와 데이터 쓰기 GetClipboardData(); ex) 클립보드의 내용을 사용 HANDLE hClip; LPSTR pClip; hClip= GetClipboardData(CF_TEXT); pClip= GlobalLock(hClip); // Pclip 사용 ex) 클립보드의 내용을 자신의 프로그램에 복사 OpenClipboard(hwnd); hClip=GetClipboardData(CF_TEXT); if(!hClip) { CloseClipboard(); return 0; } szBuf = GlobalReAlloc(szBuf, GlobalSize(hClip),GMEM_MOVEABLE); pBuf = (LPSTR)GlobalLock(szBuf); pClip = GlobalLock(hClip); // 복사 }
Clipboard (6) SetClipboardData(포맷, 메모리 핸들); The SetClipboardData function places data on the clipboard in a specified clipboard format ex) 데이터 수정 후 클립보드에 설정 SetClipBoardData(CF_TEXT, szBuf);
실습 CreateDirectory DeleteAllFiles DeleteDirectory 파일 읽어서 보여주기 파일 찾기 다이얼로그를 생성하여 사용자가 입력한 경로에 디렉토리 생성 DeleteAllFiles 사용자가 입력한 디렉토리 아래 있는 모든 파일을 삭제 DeleteDirectory 사용자가 입력한 디렉토리 안에 있는 파일을 다 삭제하고 디렉토리까지 삭제 파일 읽어서 보여주기 다이얼로그를 띄우면 사용자가 파일을 선택할 수 있도록 파일 열기 다이얼로그를 띄우고, 사용자가 선택한 파일을 읽어들여서 화면에 출력 파일 찾기 사용자가 디렉토리와 키워드를 입력하면 지정한 디렉토리 아래서 찾기 파일 제목이나 내용으로 선택해서 검색 가능 찾은 개수를 메시지 박스로 출력
11. Process & Thread
Introduction (1) Process 정의 : 실행중인 프로그램 Code region + Data Region + Running Context GUI Process와 Console Process의 2종류가 존재 프로세스마다 주소 공간을 따로 가짐 Process Control Block (PCB)
Introduction (2) Thread CPU 할당의 기본 단위, 즉 실행 단위 자신만의 PC(program counter), register, stack을 가짐 같은 프로세스에 속할 경우 같은 주소 공간 사용 같은 프로세스에 속한 다른 쓰레드와 code, data, system resource 공유 Primary thread : WinMain의 실행을 담당하고 추가적인 작업을 위한 쓰레드를 생성 Multi-threading : 여러 개의 thread가 존재
Introduction (3) Single and Multithreaded Processes
Introduction (4) 주소공간 Win32에서의 주소 공간 Win32 응용 프로그램 프로세스 시작 프로세스 끝 16 bit 응용프로그램 주소공간 Win32에서의 주소 공간 프로세스 시작 프로세스 끝 Primary thread 하나만 동작, 실행 경로가 단일하다 여러 쓰레드가 존재하기 때문에, 쓰레드 간의 동기화가 필요하다 단일 쓰레드와 멀티 쓰레드의 비교
Introduction (5) Benefits Responsiveness Resource Sharing Economy 프로세스를 쓰는 경우 프로세스가 blocking되면 다른 작업이 불가능하지만, thread를 쓰면 한 thread가 blocking되어도 다른 thread는 작업을 계속할 수 있다. Resource Sharing 부모 프로세스의 자원 공유 가능 Economy 프로세스간에 작업을 전환할 때 필요한 context switching overhead가 없다.
Introduction (6) 쓰레드를 사용하는 것이 유리한 경우 일을 효율적으로 분담하려는 경우 여러 개의 일을 동시에 진행할 경우 MDI Child 윈도우를 쓰레드로 작성 Background processing을 하는 경우 우선 순위에 따라 일을 처리할 경우 UI관련(마우스, 키보드)한 경우 높은 우선 순위 부여 계산이나 프린팅 작업의 경우 낮은 우선 순위 부여 Multi-client를 지원하는 서버를 작성하는 경우 네트워크 상에서 동작하는 서버 프로그램 쓰레드 간의 동기화 작업
Introduction (7) 요구 대기 쓰레드 처리 쓰레드 쓰레드를 이용한 client의 요구 처리 요청이 오기를 기다린다 client로부터 요청이 들어오면 요청마다 별도의 쓰레드를 만들어 처리한다 처리 쓰레드 쓰레드를 이용한 client의 요구 처리
프로세스 생성 생성함수 UINT WinExec(LPCSTR lpCmdLine, UINT uCmdShow); 16bit 윈도우와의 호환을 위해 제공하는 것으로, WIN32 프로그램에서는 CreateProcess API를 사용해야 한다 DWORD LoadModule (LPCSTR lpModuleName, LPVOID lpParameterBlock); LpModuleName에서 지정한 프로그램을 실행시킨다 BOOL CreateProcess(…) WIN32 API 새로운 프로세스와 primary thread를 생성한다 생성한 프로세스는 파라미터로 지정한 프로그램을 실행한다
CreateProcess(1) BOOL CreateProcess( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation );
CreateProcess(2) CreateProcess API의 인자 설명 LPCTSTR lpApplicationName 실행 파일 이름 실행 파일을 찾는 경로 현재 디렉토리CreateProcess를 호출한 프로세스의 현재 디렉토리 윈도우 시스템 디렉토리 윈도우 디렉토리 Path 환경 변수에 들어 있는 디렉토리 NULL일 경우 2번째 인자에 실행 파일 이름이 명시됨 LPTSTR lpCommandLine 새로 생성할 프로세스에게 주어질 명령행 인자 LPCTSTR lpApplicationName == NULL이면 실행 파일 이름이 인자로 들어감 LPSECURITY_ATTRIBUTES lpProcessAttributes 프로세스의 보안 속성
CreateProcess(3) CreateProcess API의 인자 설명(cont’d) 자원=> LPSECURITY_ATTRIBUTES lpThreadAttributes, 메인 쓰레드의 보안 속성 BOOL bInheritHandles TRUE면 부모 프로세스의 자윈 사용 가능 FALSE면 부모 프로세스의 자윈 사용 불가능 typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; // 구조체의 크기 LPVOID lpSecurityDescriptor; //소유자,그룹 식별 정보 BOOL bInheritHandle; } SECURITY_ATTRIBUTES; User Object 윈도우 관리와 관련 있는 객체, ex)Window, Menu, Icon GDI Object 그래픽과 관련이 있는 객체, ex)Brush, Pen, Palette, Bitmap, Font, DC Kernel Object 메모리 관리와 관련이 있는 객체, ex) Process, Thread, File, Event 자원=> 계승가능
CreateProcess(4) CreateProcess API의 인자 설명(cont’d) DWORD dwCreationFlags 프로세스의 생성 방법, 우선 순위 정보 설정 가능 CREATE_SUSPEND 실행 준비를 끝낸 상태에서 중지 The primary thread of the new process is created in a suspended state and does not run until the ResumeThread function is called CREATE_NEW_CONSOLE 부모와는 별개로 새로운 콘솔을 가진 child process 생성 CREATE_NEW_PROCESS_GROUP 새로운 프로세스 그룹의 root 프로세스를 생성 DETACHED_PROCESS 콘솔 프로세스에 적용되며, 새로운 프로세스는 부모의 콘솔에 접근할 수 없다. 새로운 프로세스는 이후에 AllocConsole 함수로 콘솔을 생성해서 사용해야 한다.
CreateProcess(5) DEBUG_PROCESS EXCEPTION_DEBUG_EVENT Exception이 발생했을 때 해당 사건이 일어나면 부모 프로세스에게 이벤트를 발생시킴 BOOL WaitForDebugEvent(LPDEBUG_EVENT lpde, DWORD dwTimeout); EXCEPTION_DEBUG_EVENT Exception이 발생했을 때 CREATE_THREAD_DEBUG_EVENT 쓰레드가 생성되었을 때 CREATE_PROCESS_DEBUG_EVENT 프로세스가 실행되기 시작했을 때 EXIT_THREAD_DEBUG_EVENT 쓰레드가 끝날 때 EXIT_PROCESS_DEBUG_EVENT 프로세스가 끝날 때 LOAD_DLL_DEBUG_EVENT 프로세스가 DLL을 load할 때 UNLOAD_DLL_DEBUG_EVENT 프로세스가 DLL을 더 이상 사용하지 않을 때 OUTPUT_DEBUG_STRING_EVENT 프로세스가 OutputDebugString을 호출했을 때 PIR_EVENT 프로세스가 시스템 디버깅 에러를 야기했을 때
CreateProcess(6) 우선 순위 조정 REALTIME_PRIORITY_CLASS HIGH_PRIORITY_CLASS 가장 높은 우선순위 부여 HIGH_PRIORITY_CLASS 사용자 입력에 즉각 반응하는 프로세스에 할당 ex) Window Task Manager(CTRL+ESC) NORMAL_PRIORITY_CLASS Foreground로 실행될 경우 background보다 우선순위가 높다 디폴트 우선순위 IDLE_PRIORITY_CLASS 실행중인 프로세스가 없을 경우에 실행되는 프로세스 우선순위를 알려고 할 때, 변경할 때 DWORD GetPriorityClass(HANDLE hProcess); BOOL SetPriorityClass(HANDLE hProcess, DWORD dwPriorityClass);
CreateProcess(7) CreateProcess API의 인자 설명(cont’d) LPVOID lpEnvironment 새로 생성되는 프로세스의 환경 변수 블록 NULL이면 부로 프로세스의 환경 변수를 그대로 사용 이름1=값1\0이름2=값2\0 …….\0 이름N=값N\0 BOOL SetEnvironmentVariable(LPCTSTR lpName, LPCTSTR lpValue); DWORD GetEnvironmentVariable(LPCTSTR lpName, LPTSTR lpBuffer, DWORD nSize); LPVOID GetEnvironmentStrings(VOID) The GetEnvironmentStrings function returns a pointer to the environment block of the calling process This should be treated as a read-only block LPCTSTR lpCurrentDirectory 새로 생성되는 프로세스의 작업 디렉토리 NULL이면 부모 프로세스와 같은 작업 디렉토리
CreateProcess(8) CreateProcess API의 인자 설명(cont’d) LPSTARTUPINFO lpStartupInfo 윈도우의 좌표나 크기 등의 디폴트 값을 줄 때 사용 VOID GetStartupInfo(LPSTARTUPINFO lpStartupInfo);
CreateProcess(9) typedef struct _STARTUPINFO { DWORD cb; // 이 구조체의 크기 LPSTR lpReserved; // 예약 영역 LPSTR lpDesktop; // 데스크탑의 이름(없을 경우 새로 만듬) LPSTR lpTitle; // 콘솔 프로세스의 윈도우 캡션 DWORD dwX; // 윈도우의 디폴트 시작 X좌표 DWORD dwY; // 윈도우의 디폴트 시작 Y좌표 DWORD dwXSize; // 윈도우의 디폴트 폭 DWORD dwYSize; // 윈도우의 디폴트 높이 DWORD dwXCountChars; // 콘솔 윈도우의 폭을 글자수로 명시 DWORD dwYCountChars; // 콘솔 윈도우의 높이를 글자수로 명시 DWORD dwFillAttribute; // 콘솔의 문자색과 배경색을 명시 DWORD dwFlags; // 이 구조체의 어느 부분을 프로세스가 사용할 것인지 명세 WORD wShowWindow; // GUI 프로세스의 디폴트 윈도우의 표시 플래그 WORD cbReserved2; // 예약 영역 LPBYTE lpReserved2; // 예약 영역 } STARTUPINFO, *LPSTARTUPINFO;
CreateProcess(10) STARTF_USESIZE dwXSize, dwYSize를 사용할 것 STARTF_USESHOWWINDOW dwShowWindow를 사용할 것 STARTF_USEPOSITION dwX, dwY를 사용할 것 STARTF_USECOUNTCHARS dwXCountChars, dwYCountChars를 사용할 것 STARTF_USEFILLATTRIBUTE dwFillAttribute를 사용할 것 STARTF_FORCEONFEEDBACK 프로세스 생성시에 모래시계 커서 사용 STARTF_ FORCEOFFFEEDBACK 프로세스 생성시에 모래시계 커서 사용 중지
CreateProcess(11) CreateProcess API의 인자 설명(cont’d) LPPROCESS_INFORMATION lppiProcInfo 프로세스의 생성이 성공적으로 끝났을 경우에, OS에서 채워준다 hProcess A handle to the newly created process The handle is used to specify the process in all functions that perform operations on the process object. typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThrea; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION;
CreateProcess(12) hThread dwProcessId dwThreadId A handle to the primary thread of the newly created process The handle is used to specify the thread in all functions that perform operations on the thread object. dwProcessId A global process identifier that can be used to identify a process The value is valid from the time the process is created until the time the process is terminated. dwThreadId A global thread identifiers that can be used to identify a thread The value is valid from the time the thread is created until the time the thread is terminated
프로세스의 종료 종료 VOID ExitProcess(UINT fuExitCode); 프로세스와 연결된 모든 DLL의 DllMain 함수 호출 모든 열려진 핸들을 닫는다. 실행중인 모든 프로세스를 종료 ExitProcess() 아래의 코드는 실행되지 않는다. BOOL TerminateProcess(HANDLE hProcess, UINT fuExitCode); 자기 자신이 아닌 다른 프로세스를 종료할 수 있다. DLL에게 종료 사실을 알리지 않는다. 어쩔 수 없는 경우에만 사용
프로세스의 핸들 (1) 커널 객체 프로세스와 스레드 프로세스 객체는 만들 수 있는 권리가 필요 Process Specific 커널 객체를 만든 프로세스만이 자신이 가진 핸들로 해당 객체를 액세스할 수 있다. 핸들을 다른 프로세스로 전달하는 것은 의미가 없다 다른 프로세스가 이용하기 위해서는 ID를 가지고 새로운 핸들값을 부여 받아야 한다 HANDLE OpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessld); 함수가 성공하면 지정한 프로세스에 대한 핸들을 리턴한다 이렇게 구한 핸들은 접근 권한을 가지고 있는 한 wait function과 같이 핸들을 가지고 프로세스에 접근하는 모든 함수에 사용할 수 있다 핸들에 대한 사용이 끝나면 반드시 CloseHandle 함수를 사용하여 닫는다
프로세스의 핸들 (2) ID 핸들과 ID를 구하는 함수 시스템 전역적인 값 HANDLE GetCurrentProcess(VOID) This function returns a pseudohandle for the current process A pseudohandle is a special constant that is interpreted as the current process handle This handle has the maximum possible access to the process object The pseudohandle need not be closed when it is no longer needed HANDLE GetCurrentProcessId(VOID) This function retrieves the process identifier of the calling process
CreateThread(1) HANDLE CreateThread ( LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );
CreateThread(2) CreateThread API의 인자 설명 LPSECURITY_ATTRIBUTES lpThreadAttributes 보안 속성 DWORD dwStackSize, 물리적 메모리에 할당할 스택의 크기 지정 0 이상일 때는 명시한 크기만큼 committed page에 할당 0일 때는 부모 프로세스와 같은 크기의 스택 할당 LPTHREAD_START_ROUTINE lpStartAddress 쓰레드의 실행 함수에 대한 포인터 DWORD WINAPI ThreadFunc(LPVOID lpvThreadParam); LPVOID lpParameter 쓰레드 실행 함수의 인자에 대한 포인터
CreateThread(3) CreateThread API의 인자 설명(cont’d) 쓰레드의 종료 DWORD dwCreationFlags 0을 주면 생성과 동시에 실행 CREATE_SUSPEND를 주면 중지 상태에서 대기 DWORD ResumeThread(HANDLE hThread); DWORD SuspendThread(HANDLE hThread) LPDWORD lpThreadId 생성된 쓰레드의 ID를 저장 NULL을 주면 에러 발생 쓰레드의 종료 VOID ExitThread(DWORD fdwExitCode); BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);
CreateThread(4) Pseudo Handle 기타 API 오직 자신의 문맥에서만 유효 CloseHandle() 같이 프로세스나 스레드 객체에 영향을 주는 함수의 인자로 사용불가! HANDLE GetCurrentProcess(VOID) HANDLE GetCurrentThread(VOID) 진짜 핸들을 얻고자 할 때는 DuplicateHandle API를 이용 This function duplicates an object handle The duplicate handle refers to the same object as the original handle Therefore, any changes to the object are reflected through both handles For example, the current file mark for a file handle is always the same for both handles 기타 API DWORD GetCurrentThreadId(VOID)
CreateThread(5) HANDLE hSourceProcess, // 수도 - 예 DWORD WINAPI ParentThread(LPVOID lpvThreadParam) { DWORD IDThread; HANDLE hThreadParent = GetCurrentThread(); CreateThread(NULL, 0, ChildThread, (LPVOID)hThreadParent, 0, &IDThread); ….. } DWORD WINAPI ChildThread(LPVOID lpvThreadParam) HANDLE hThreadParent = (HANDLE)lpvThreadParam; SetThreadPriority(hThreadParent, THREAD_PRIORITY_NORMAL); - DuplicateHandle 함수를 이용하여 pseudo-to-real 핸들 변환을 해야 함 - BOOL DuplicateHandle( HANDLE hSourceProcess, // 수도 // 핸들이 한정적으로 의미를 가지는 // 프로세스 핸들. HANDLE hSource, // 변환 대상이 되는 // 프로세스 혹은 스레드의 수도 핸들. HANDLE hTargetProcess, // 새로운 // 진짜 핸들이 한정적으로 의미를 갖게 // 될 프로세스의 핸들. LPHANDLE lphTarget, // 새로운 진짜 핸 // 들이 저장될 위치. DWORD fdwAccess, // 핸들 액세스 // 방법 지정. BOOL fInherit, // 새로운 핸들의 상속 속 // 성 지정. DWORD fdwOptions); // 복사 옵션 들.
CreateThread(6) DWORD WINAPI ChildThread(LPVOID lpvThreadParam) - 수정 코드 예 DWORD WINAPI ParentThread(LPVOID lpvThreadParam) { DWORD IDThread; HANDLE hThreadParent; DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hThreadParent, 0, FALSE, DUPLICATE_SAME_ACCESS); CreateThread(NULL, 0, ChildThread, (LPVOID)hThreadParent, 0, &IDThread); ….. } DWORD WINAPI ChildThread(LPVOID lpvThreadParam) { HANDLE hThreadParaent = (HANDLE)lpvThreadParam; SetThreadPriority(hThreadParent, THREAD_PRIORITY_NORMAL); CloseHandle(hThreadParent); ….. } - 위의 수정 코드에서 CloseHandle에 유의: 진짜 핸들의 생성은 커널 객체의 사용 계수 증가를 동반하므로 반드시 닫아 주어야 한다.
우선순위 (1) 스케줄링 우선순위 우선순위 클래스와 우선순위 레벨 값의 조합 설정 BOOL SetThreadPriority(HANDLE hThread, int nPriority); int GetThreadPriority(HANDLE hThread)
우선순위 (2) 프로세스의 우선순위 클래스 스레드의 상대 idle Background Foreground Foreground High Realtime 우선순위 Normal Normal(+1) Normal(+2) (4) (7) (8) (9) (13) (24) --------------------------------------------------------- Time 15 15 15 15 15 31 Critical Highest 6 9 10 11 15 26 Above 5 8 9 10 14 25 Normal Normal 4 7 8 9 13 24 Below 3 6 7 8 12 23 Lowest 2 5 6 7 11 22 Idle 1 1 1 1 1 16
우선순위 (3) 동적 우선순위(Dynamic priority) Base Priority 대부분의 스레드는 7~11 0~31 대부분의 스레드는 7~11 동적 우선순위(Dynamic priority) 실제로 적용되는 우선순위는 계속 변하게 된다. 스케줄러가 쓰레드를 실행할 때 적용하는 값 Priority Boost The system can boost and lower the dynamic priority Only threads with a base priority between 0 and 15 receive dynamic priority boosts To ensure that it is responsive and that no threads are starved for processor time Base Priority보다 낮아지지는 않는다
우선순위(4) 우선 순위 클래스 의미 THREAD_PRIORITY_LOWEST 프로세서의 우선 순위 클래스 보다 두 단계 밑 THREAD_PRIORITY_BELOW_NORMAL 프로세서의 우선 순위 클래스 보다 한 단계 밑 THREAD_PRIORITY_NORMAL 프로세서의 우선 순위 클래스와 동일 THREAD_PRIORITY_ABOVE_NORMAL 프로세서의 우선 순위 클래스 보다 한 단계 위 THREAD_PRIORITY_HIGHEST 프로세서의 우선 순위 클래스 보다 두 단계 위 THREAD_PRIORITY_IDLE 프로세서의 우선 순위가 realtime이 아니면, 우선 순위 레벨을 가장 낮은 1로 설정 THREAD_PRIORITY_TIME_CRITICAL 프로세서의 우선 순위 클래스가 realtime이면, 우선 순위 레벨을 가장 높은 31로 만들고, 나머지 경우는 16으로 만듬
실습 간단한 task manager 2가지 다른 종류의 작업을 하는 worker thread 사용자가 선택한 프로그램을 실행하고, 실행시킨 프로그램을 종료시킨다. 메뉴나 버튼을 선택하면 common file open dialog 생성 실행할 프로그램을 선택하면 파일 경로를 저장 CreateProcess로 선택한 프로그램을 실행 종료 메뉴를 버튼을 선택하면 TerminateProcess로 생성한 프로세스를 종료 2가지 다른 종류의 작업을 하는 worker thread 2가지 종류의 worker thread 생성 Thread procedure를 다르게 정의 하나는 숫자 세기, 하나는 그림 그리기 메뉴로 쓰레스 생성, 중지, 재시작 Thread priority 조정
12. IPC
InterProcess Communication IPC 프로세스간의 통신 방법 Win32 환경에서는 주소공간의 분리로 인해 쉽지 않다.
IPC 사용 고려요소 네트웍 지원이 필요한가? 통신대상이 다른 운영체제에서 실행되는 프로그램인가? 통신 대상이 고정되어 있는가? 통신 속도가 중요한가? 일회적인 교환인가? 지속적인 교환인가?
IPC의 종류 (1) Win32는 다수의 IPC 방법을 제공 Clipboard 중앙 저장소를 통한 대량의 정보 교환 Win32에서는 네트워크 상에서도 동작 DDE (Dynamic Data Exchange) DDE 메시지를 사용한 지속적인 정보 교환 RPC (Remote Procedure Call) 원격 함수 호출 사용 서로 다른 플랫폼 위에서 동작하는 프로세스들간에 주로 사용 메시지 시스템 메시지 및 사용자 정의 메시지로 정보 교환
IPC의 종류 (2) File Mapping Pipe MailSlot 파일 공유 Pipe 파이프 사용 MailSlot 메일슬롯 사용 OLE(Object Linking & Embedding), Netbios
메시지 특징 방법 제약 가장 간단하고 빠른 방법 크기가 크지 않은 정보를 전달 메시지는 메모리를 거치지 않고 운영체제에 의해 직접 전달 방법 두 윈도우의 핸들을 알고 있어야 한다. 기존 메시지를 이용할 것이 아니라면 교환할 사용자 메시지를 정의해야 한다 Message ID (ex. WM_USER+100) wParam, lParam에 들어갈 정보의 의미 약속된 메시지의 wParam, lParam를 통해 정보 교환 제약 문자열이나 구조체같은 큰 데이터는 전달할 수 없다 wParam, lParam 8 byte 일반적으로 사실 통보의 목적으로만 사용됨
WM_COPYDATA (1) 특징 방법 장점 IPC를 위해 정의된 메시지 wParam : 정보를 보내는 윈도우 핸들 lParam : 정보가 저장되어 있는 구조체의 포인터 구조체 typedef struct tagCOPYDATASTURCT { DWORD dwDATA; // 정수값 DWORD cbDATA; // lpDATA의 크기 PVOID lpDATA; // 문자열 } COPYDATASTRUCT; 장점 구조체나 배열 등 크기가 큰 데이터도 전달 가능
WM_COPYDATA (2) 주의 사항 lpData에는 받는 쪽에서 읽을 수 있는 값만 전달 이진 포맷의 데이터의 경우 받는 데이터는 읽기 전용이므로 변경해서는 안된다 SendMessage()를 이용할 것
Memory Map file (1) 정의 하드 디스크에 존재하는 파일의 내용을 프로세스의 주소 공간에 연결(MAP)하는 기법 특징 포인터를 이용해서 파일 I/O 수행 파일을 메모리처럼 조작 프로그램하기가 쉽다 Win32에서 프로세스간 통신에 사용 둘 이상의 프로세스가 File mapping object에 동시에 접근 가능 각 프로세스는 포인터를 사용해 파일 조작해서 정보를 주고 받음 동기화 오브젝트의 사용이 필수적 정보를 갱신하면 이를 알릴 방법이 필요하다 로컬 시스템에서만 사용 가능
Memory Map file(2) 방법 File mapping object 생성 공유 방법 파일 포인터를 얻어서 내용 조작 Inheritance 부모 프로세스가 file mapping 객체를 만든 후에 자식 프로세스에게 넘겨줌 ( 부모- 자식 프로세스에서만 가능) Named File Mapping 이름을 통해 file mapping object를 공유 File mapping object를 open해서 공유 파일 포인터를 얻어서 내용 조작 다른 IPC나 동기화 오브젝트를 통해 변경 사실을 알림
Memory Map file (2) Object 생성 HANDLE CreateFileMapping ( HANDLE hfile, // 파일 맵핑의 대상이 되는 파일의 핸들 LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // 보안 DWORD flProtect, // 맵핑 오브젝트의 보호 속성 DWORD dwMaximumSizeHigh, // 맵핑 오브젝트 (상위 32bit) DWORD dwMaximumSizeLow. // 맵핑 오브젝트 (하위 32bit) LPCTSTR lpName // 파일 맵핑 오브젝트의 이름 }; hFile이 0xffffffff이면 새로운 파일을 열어서 파일 맵핑 오브젝트 생성 flProtext : PAGE_READLYON or PAGE_READWRITE lpName을 지정해야 다른 프로세스에서 이름을 가지고 파일 맵핑 오브젝트를 열어서 사용 가능
Memory Map file (3) Object 열기 HANDLE OpenFileMapping ( 이미 생성된 파일 맵핑 오브젝트를 여는데 사용 HANDLE OpenFileMapping ( DWORD dwDesiredAccess. // FILE_MAP_READ, FILE_MAP_WRITE DWORD bInheritHandle, // 계승 가능 여부 지정 LPCTSTR lpName // 파일 맵핑 오브젝트의 이름 ); lpName CreateFileMapping에서 지정한 이름을 입력해야 두 프로세스간에 파일 맵핑 오브젝트 공유 가능
Memory Map file (4) Object에 대한 포인터 얻기 LPVOID MapViewOfFile( 실제로 파일 맵핑 오브젝트를 조작하는데 사용 LPVOID MapViewOfFile( HANDLE hfileMappingObjet. // 파일 맵핑 오브젝트의 핸들 DWORD dwDesiredAccess, // 접근 속성 DWORD dwFileOffsetHigh, // 접근하려는 오브젝트의 위치( 상위 32bit) DWORD dwFileOffsetLow. // 접근하려는 오브젝트의 위치 ( 하위 32bit) DWORD dwNumberOfByteToMap // 맵핑 하려는 파일의 바이트 수 (0 이면 전체) ); dwDesiredAccess FILE_MAP_READ or FILE_MAP_WRITE
Pipe (1) 종류 정의 특징 One-Way Pipe , Two-Way Pipe 두 프로세스간에 정보를 주고 받을 수 있는 통로 특징 연속적인 바이트 스트림을 교환할 때 사용 종류 One-Way Pipe , Two-Way Pipe Anonymous pipe , Named Pipe(NT)
Pipe (1) 익명 파이프 (Anonymous pipe) 이름이 없는 단방향 파이프 특징 핸들을 이용해 통신하기 때문에 부모-자식 프로세스간의 통신에 적합 네트워크 상의 다른 컴퓨터끼리는 사용할 수 없다 9x 계열에서 사용 가능 파이프 생성 읽기용과 쓰기용의 2개의 핸들 생성 BOOL CreatePipe( PHANDLE hReadPipe, // 읽기 전용의 파이프 핸들 PHANDLE hWritePipe. // 쓰기 전용의 파이프 핸들 LPSECURITY_ATTRIBUTES lpPipeAttributes, // 보안 관련 변수에 대한 포인터 DWORD nSize // 파이프를 할당하는 버퍼의 크기 );
Pipe (2) 파이프의 한쪽끝을 통신하고자 하는 프로세스에게 전달 파일을 보낼 경우 파일을 받을 경우 부모가 자식에게 핸들을 상속 DDE, 메모리 맵 파일등의 다른 IPC를 사용 파일을 보낼 경우 읽기 핸들을 전달, 자신을 쓰기 핸들에 데이터 기록 파일을 받을 경우 쓰기 핸들을 전달, 읽기 핸들로 데이터 받음 읽고 쓰는 방법은 파일과 동일 ReadFile, WriteFile API 이용
Pipe (3) 지명 파이프 (Named Pipe) 특징 구성 문자열로 된 이름을 가짐 이름을 알고 있는 프로세스는 누구나 파이프를 열 수 있다. 핸들 전달 필요 없음 네트워크 상의 프로세스에서도 작동 네트워크 프로토콜에 상관없이 동작 가능 양방향으로 데이터 전달 가능 구성 서버 프로세스와 클라이언트 프로세스로 구성 서버 : 파이프를 만든 프로세스 클라이언트 : 파이프 인스턴스에 접속하려는 프로세스 하나의 서버는 여러 개의 프로세스와 통신 각 파이프 인스턴스는 고유의 핸들과 별도의 버퍼
Pipe (4) 생성 lpName 대소문자 구별 없으며 256자까지 가능 \\시스템 이름\pipe\[path] 파이프 이름 HANDLE CreateNamedPipe( LPCTSTR lpName, // 지명 파이프 이름 DWORD dwOpenMode, // 파이프의 생성 모드 DWORD dwPipeMode, // 파이프의 타입 DWORD nMaxInstances, // 생성될 파이프의 최대 개수 DWORD nOutBufferSize, // 전송 버퍼의 크기 DWORD nInBufferSize, // 수신 버퍼의 크기 DWORD nDefaultTimeOut // 타임아웃 값 LPSECURITY_ATTRIBUTE lpSecurityAttributes ); lpName 대소문자 구별 없으며 256자까지 가능 \\시스템 이름\pipe\[path] 파이프 이름 시스템 이름이 .이면 자신의 로컬 시스템 ex. \\alanis\pipi\mypipe
Pipe Mode
Pipe Type
Pipe (5) 클라이언트의 접속 nDefaultTimeOut CreateFile이나 CallNamedPipe 이용 클라이언트가 서버 파이프에 접속하는데 실패한 경우 이용가능한 파이프가 생길때까지 대기하는 시간 클라이언트의 접속 CreateFile이나 CallNamedPipe 이용 -ex) 서버 HANDLE hPipe = CreateNamedPipe(\\.\\pipe\\mypipe, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE |PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 4096, 4096, 1000, NULL); 클라이언트 HANDLE hPipe = CreateFile((\\alanis\\pipe\\mypipe, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); ReadFile or WriteFile을 이용해 통신
MailSlot (1) 정의 특징 프로세스간의 단방향 통신 방법 메모리에 존재하는 파일 or 우편함 수신 전용 메모리에 존재하는 파일 or 우편함 파일처럼 파일 I/O로 조작하지만 임시적으로만 존재 특징 여러 개의 메시지를 한번에 보낼 수 있음 메시지의 크기는 최대 64K 메일 슬롯의 이름은 문자열 \\.\mailslot\[path]Name 목적에 따라 그룹을 나눌 수 있다. 메모리 상에만 존재, 핸들을 닫으면 저장된 모든 메시지 삭제 Broadcasting 가능 여러 프로세스들이 같은 이름으로 메일슬롯을 만든 경우 그 이름을 사용하면 해당하는 모든 프로세스의 메일슬롯으로 데이터 전송
MailSlot (2) 단점 통신방법 단방향으로만 통신 가능 통신이 제대로 되었는지 확인 불가능 데이터 기록해서 전송
MailSlot (3) 서버 HANDLE CreateMailSlot( 생성 Timeout을 변경 LPCTSTR lpName, DWORD nMaxMessageSize, DWORD lReadTimeout, LPSECURITY_ATTRIBUTES lpSecurityAttributes ); lpName : 파이프와 동일한 형식 생성에 성공하면 핸들을 리턴 메시지를 꺼내거나 메일 슬롯 파괴시 사용 Timeout을 변경 BOOL SetMailslotInfo(HANDLE hMailslot, DWORD IReadTimeout);
MailSlot (4) 읽기 서버 프로세스에서만 가능 데이터를 읽기 위해서는 우선 데이터의 크기를 알아내야 한다 GetMailSlotInfo 이용 BOOL GetMailSlotInfo( HANDLE hMailSlot, // 메일 슬롯 핸들 LPDWORD lpMaxMessageSize, // 읽어올 수 있는 메시지의 최대 크기 LPDWORD lpNextSize, // 다음에 읽어올 메시지 크기 LPDWORD lpMessageCount, // 타임아웃 정보 LPDWORD lpReadTime // 타임아웃 시간 정보 ); 데이터의 크기를 알아낸 다음 ReadFile을 이용해 데이터를 읽어들임 Message count가 0이 될때까지 loop을 돌면서 메시지를 읽어들이면 됨
MailSlot (5) 클라이언트 특징 CreateFile API로 mailslot을 오픈 메일 슬롯에 메시지를 넣는 프로세스 파일 입출력 함수만으로 메시지를 넣을 수 있다 쓸 수 있는 데이터 양 제한 일반적인 경우 : 64K CreateFile API로 mailslot을 오픈 WriteFile API로 데이터 기록
MailSlot (6) ex) 쓰기 // 먼저 메일 슬롯을 open한다. //Wrinkle이란 시스템에 있는 sample이란 이름의 메일 슬롯이다. hFile = CreateFile(\\wrinkle\mailslot\sample, GENERIC_READ, FILE_SHARE_READ, FILE_ATTRIBUTE_NORMAL, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, NULL); // 다음으로 메일 슬롯에 데이터를 써 넣는다. WriteFile(hFile, hey! How about learning API, 31, &cbWritten, NULL); // 메일 슬롯을 닫는다. CloseHandle(hFile); ex) 읽기 fResult = GetMailslotInfo(hSlot1, NULL, &cbMessage, &cMessage, NULL); - while(cMessage != 0) ReadFile(hSlot1, lpszBuffer, cbMessage, &cbRead, NULL); … // 읽어 들인 데이터로 하고 싶은 일을 수행한다. GetMailslotInfo(hSlot1, NULL, &cbMessage, &cMessage, NULL);
PIPE Vs MailSlot
실습 (1) IPC 테스트 다양한 IPC 방법을 사용해본다 IPC 방법대로 메뉴 구성 사용자 정의 메시지 WM_COPYDATA Simple file mapping File mapping with event object 송신 프로그램과 수신 프로그램들 별도의 프로젝트로 생성 사용자 메시지 정의 SendMessage로 송신하고 수신하면 받았다는 메시지 박스를 화면에 출력
실습 (2) WM_COPYDATA Simple file mapping File mapping with event object COPYDATASTRUCT 구성 Common color dialog와 edit를 올린 다이얼로그를 사용해서 color와 text를 입력받은 후, 이 내용으로 COPYDATASTRUCT 구성 SendMessage(수신 윈도우, WM_COPYDATA, ~~) 수신 윈도우는 수신후 받은 내용과 색으로 화면에 내용 출력 Simple file mapping 송신 윈도우는 file mapping object를 생성하고 내용 기록 수신 윈도우는 file mapping object를 열어서 내용을 화면에 출력 File mapping with event object 송신 윈도우는 thread를 하나 생성한 후, file mapping object와 event object 생성 WaitForSingleObject로 수신 윈도우가 데이터를 기록해서 이벤트가 일어날 때까지 기다림 수신 윈도우는 file mapping object와 이벤트를 열고 데이터를 기록한 후 이벤트 발생시킴 송신 윈도우는 이벤트를 받으면 받은 내용을 메시지 박스로 출력
13. Synchronization
MultiThread (1) 특징 동시에 여러 개의 작업 수행
MultiThread (2) 문제점 복수개의 코드가 같은 주소 영역에서 실행 공유 자원을 보호하기 어렵다 서로 간섭하고 영향을 줄 수 있다 공유 자원을 보호하기 어렵다 메모리 영역의 전역변수 동일한 프로세스의 스레드는 전역변수를 공유 Race condition 발생 The situation where several processes access and manipulate shared data concurrently The final value of the shared data depends upon which process finishes last count++ could be implemented as register1 = count (LOAD R1, COUNT) register1 = register1 + 1 (ADD R1, 1) count = register1 (STORE R1, COUNT) count-- could be implemented as register2 = count (LOAD R2, COUNT) register2 = register2 - 1 (SUB R2, 1) count = register2 (STORE R2, COUNT)
MultiThread (3) 해결방법 쓰레드간의 실행 순서를 제어하기 힘듬 동기화 Deadlock이 발생할 수 있다 Two or more processes are waiting indefinitely for an event that can be caused by only one of the waiting processes Let S and Q be two semaphores initialized to 1 P0 P1 wait (S); wait (Q); wait (Q); wait (S); . . signal (S); signal (Q); signal (Q); signal (S); Starvation Indefinite blocking 해결방법 동기화
Thread Synchronization(1) Thread A Thread B 동기화 오브젝트 특정한 사건이 발생했음을 알린다 사건이 발생한 것을 통보 받고 실행된다 쓰레드 동기화의 예 1. Event 2. Critical Section 3. Mutex 4. Semaphore
Thread Synchronization(2) Event 쓰레드간의 동기화에 가장 많이 사용 서로 다른 프로세스에 속한 쓰레드 간의 동기화에도 사용 Critical Section 다수가 공유하는 데이터에 대해 한번에 하나의 쓰레드만 접근할 수 있도록 제한 단 하나의 프로세스 속한 쓰레드에서만 사용 가능 Mutex(Mutual Exclusion) 여러 프로세스에 속한 쓰레드간에도 사용 가능 Semaphore 공유 데이터에 대해 동시에 N(N≧1)개의 쓰레드가 접근할 수 있도록 허용
Thread Synchronization(3) Primary Thread Other Threads Primary Thread 쓰레드를 생성하고 그 쓰레드가 종료되어야 다음 작업을 진행한다면 쓰레드를 사용하는 의미가 없다 단일 쓰레드 프로그램과 잘못 작성한 멀티 쓰레드 프로그램의 비교
Event Object(1) Event T1 T2 Tn Event Object WaitForSingleObject 쓰레드간의 작업 순서나 시기를 조정하기 위해 사용 하나 이상의 쓰레드에게 특정 사건이 일어났음을 알려준다. Signal state, Non-signal state Auto-reset event vs Manual-reset event auto-reset event : signal 상태가 되면 자동으로 non-signal 상태가 된다 manual-reset event : signal 상태가 되었을 경우, ResetEvent를 이용해서 non-signal 상태로 만든다 Event WaitForSingleObject T1 T2 Tn 대기중인 쓰레드들 이벤트 오브젝트의 역할
Event Object(2) Creating Event Object HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, // 접근 권한 정보 BOOL bManualReset, // 수동 리셋 이벤트인지, 자동 리셋 이벤트 표시 BOOL bInitialState, // 초기 상태, True->Signal, False->Non-signal LPCTSTR lpName // 이벤트 오브젝트의 이름 ); 이벤트 오브젝트의 이름은 다른 프로세스에 속한 이벤트에 대한 핸들을 얻고자 할 때(OpenEvent API 사용)사용
Event Object(3) Opening Event Object HANDLE OpenEvent( DWORD dwDesiredAccess, // 이벤트 오브젝트 핸들에 대한 접근 권한 제어 BOOL bInheritHandle, // 계승 가능 여부, TRUE->계승가능, FALSE->계승불허 LPCTSTR lpName // open할 이벤트의 이름 ); SYNCHRONIZE Open한 이벤트의 상태를 바꿀 수는 없고, 동기화를 위해서만 사용가능, SetEvent나 ResetEvent를 호출할 수는 없고, 단지 WaitForSingleObject와 같은 API를 호출해 동기화 가능 EVENT_MODIFY_STATE SYNCHRONIZE와는 반대로 open한 이벤트의 상태를 바꾸는 것만이 가능하고, 동기화를 위해서 사용할 수는 없다 EVENT_ALL_ACCESS 위의 두가지가 다 가능하다 dwDesiredAccess 의 미
Event Object(4) Changing Event Object’s State SetEvent Signal 상태가 되면, 하나의 쓰레드가 방출되고, 다시 non-signal이 된다. Signal 상태로 변경한다. 대기 중인 쓰레드가 모든 방출된다. ResetEvent 아무런 영향이 없다. 상태를 non-signal 상태로 만든다. PulseEvent SetEvent와 동일하다. 대기중인 모든 쓰레드를 방출하고, 다시 non-signal 상태로 만든다.
Critical Section(1) Critical section 1 2 3 n serialize 공용 자원(파일이나 메모리)에 두 개 이상의 쓰레드가 접근하여 자원의 완전성을 파괴하는 것을 방지하기 위해 사용 같은 프로세서에 속한 쓰레드들에서만 사용 가능 공용 자원 Critical section 1 2 3 n serialize
Critical Section(2) ex) Critical Section으로 구현한 자원 접근의 일렬화 do { entry section critical section exit section } while (1); CRITICAL_SECTION CSObject; InitializeCriticalSection(&CSObject); ……………… EnterCriticalSection(&CSObject); [공용 자원에 대한 접근이 이루어지는 코드] LeaveCriticalSection(&CSObject);
Critical Section(3) 방법 단점 공유 자원이 있는 곳을 크리티컬 섹션으로 둘러싸준다. 초기화 및 파괴 VOID InitializeCriticalSection(LPCRITICAL_SECTION lpcsCSObject); VOID DeleteCriticalSection(LPCRITICAL_SECTION lpcsCSObject); Critical_SECTION형의 변수 지정 반드시 전역변수로 지정해야 한다. 크리티컬 섹션 구성 VOID EnterCriticalSection(LPCRITICAL_SECTION lpcsCSObject); VOID LeaveCriticalSection(LPCRITICAL_SECTION lpcsCSObject); 단점 잘못할 경우 deadlock이 발생 할 수 있다. 쓰레드끼리 작업 시기를 제어하기에는 적당하지 않다 주로 공유 변수나 자원에 대한 접근 제한
Synchronization Object 동기화 객체 동기화에 사용되는 객체 일정 시점에서 한가지 상태 유지 Signaled : 쓰레드의 실행을 허가하는 상태 Nonsignaled : 쓰레드의 실행을 허가하지 않는 상태 대기함수(Wait Function)와 같이 사용 일정한 조건에 따라 쓰레드의 실행을 블록 또는 허가 조건이 만족할 때까지 대기 대기 중에는 CPU 시간을 거의 소비하지 않는다. DWORD WaitForSignalObject(HANDLE hHandle, DWORD dwMilliseconds); 핸들이 지정하는 동기화 객체가 신호상태가 되기를 기다림 리턴하기 전의 동기화 객체의 상태를 변경 예) 신호상태의 동기화 객체 비신호상태로
Mutex (1) Mutex Object 특징 Mutex Object 생성 이름을 가진다. 프로세스간에도 사용가능 오직 한 쓰레드에 의해서만 소유될 수 있다. 소유시 비신호 상태로 변경 Mutex Object 생성 HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, // 보안 관련 구조체 BOOL bInitialOwner, // 생성과 동시에 소유 여부 지정 LPCTSTR lpName // 뮤텍스 오브젝트의 이름 }; // NULL일 경우 다른 객체에서 open할 수 없다
Mutex (2) Mutex Object 열기 Mutex Object 파괴 Mutex Object 양도 HANDLE OpenMutex( DWORD dwAccess, // SYNCHRONIZE, MUTEX_ALL_ACCESS BOOL bInherit, // 계승 여부 표시 LPTSTR lpszMutexName // 뮤텍스 오브젝트의 이름 ); 리턴되는 핸들값은 프로세스에 한정적이다. Mutex Object 파괴 BOOL CloseHandle( HANDLE hMutex ); Mutex Object 양도 BOOL ReleaseMutex( HANDLE hMutex );
EX) Mutex로 구현한 자원 접근의 일렬화 HANDLE hMutex; hMutex = CreateMutex(……..); …………. WaitForSingleObject(hMutex, INFINITE); [공용 자원에 대한 접근이 이루어지는 코드] ReleaseMutex(hMutex); EX) Mutex로 구현한 자원 접근의 일렬화
Semaphore Object(1) Semaphore Object Semaphore Object의 생성 최대 N(N≧1)개의 쓰레드가 동시에 작업을 수행할 수 있도록 함 Mutex Object와 사용 방법이 동일 Semaphore Object의 생성 HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 보안 관련 구조체 LONG lInitialCount, // 세마포어의 시작카운트 LONG lMaximumCount, // 세마포어를 소유할 수 있는 최대 개수 LPCTSTR lpName // 세마포어의 이름 );
Semaphore Object(2) Semaphore Object 열기 HANDLE OpenSemaphore( DWORD dwDesiredAccess, // 접근 권한 BOOL bInheritHandle, // 계승 여부 표시 LPCTSTR lpName // 세마포어 오브젝트의 이름 ) ; SYNCHRNIZE Open한 세마포어를 동기화하는 데에만 사용 SEMAPHORE_MODIFY_STATE 세마포어의 상태를 변경하기 위해서 사용 SEMAPHORE_ALL_ACCESS 세마포어를 위의 둘다 가능하게 open 가능 dwDesiredAccess 의 미
Semaphore Object (3) Semaphore Object의 양도 BOOL ReleaseSemaphore( HANDLE hSemaphore, // 양도 대상이 되는 세마포어 핸들 LONG lReleaseCount, // release하는 개수 LPLONG lpPreviousCount // 이전까지의 세마포어 카운트(비어 있던 자리수) );
14. An Overview of TCP/IP Networking
Packet Forwarding Name, Address & Routing Name: What you are Address: Where you are Routing: How to reach you
Address IP Address Each internet host has universally unique IP address 4 bytes Type Net ID Host ID 7 24 0 Net ID Host ID Class A 14 16 1 0 Net ID Host ID Class B 21 8 1 1 0 Net ID Host ID Class C 1 1 1 0 Multicast Address Class D
Address Ex) All hosts on a network have the same network prefix gateway 128.211 128.10 128.211.6.5 gateway 128.10.0.1 128.10.0.2 192.5.48.3 10 gateway 192.5.48 10.0.0.37 10.0.0.49
DNS IP address ↔ Name(Character string for human use) etc/hosts file Distributed Name Server Setup : /etc/resolve.conf /etc/hosts 127.0.0.1 localhost localhost.snu.ac.kr 147.46.59.101 popeye.snu.ac.kr popeye loghost 147.46.59.102 brutus.snu.ac.kr brutus 147.46.59.103 olive.snu.ac.kr olive 147.46.59.104 wimpy.snu.ac.kr wimpy /etc/resolv.conf domain snu.ac.kr nameserver 147.46.80.1 nameserver 147.46.60.18 nameserver 147.46.80.2
cheltenham.cs.arizona.edu, DNS 2 Root name cheltenham.cs.arizona.edu server 3 arizona.edu,128.196.128.233 1 4 cheltenham.cs.arizona.edu Local cheltenham.cs.arizona.edu Arizona Client name name server cs.arizona.edu,192.12.69.5 server 192.2.69.60 5 cheltenham.cs.arizona.edu 6 CS cheltenham.cs.arizona.edu, 192.12.69.60 7 name server
IP Packet IP Packet Format Max = 64 KB 4 4 8 16 ID VER HLEN Service Type Total Length ID Flags Fragment Offset TTL Protocol Header Checksum SA DA IP Options (If Any) Padding Data .
Fragmentation & Reassembly MTU (Maximum Transfer Unit) Maximum frame size that a physical network can transmit Different physical networks have different MTUs Ethernet - 1500 Byte FDDI - 4500 Byte, TR - 8000 Byte Fragmentation Partitioning of a datagram into multiple smaller fragments Sizes <= MTU of next physical network Reassembly Concatenation of fragments into the original datagram Protocol principle 2018-11-14
Fragmentation & Reassembly MTU = 1500 MTU = 532 Original = 20 + 1400 Byte Fragments = 20 + 512 Byte Fragments = 20 + 512 Byte Fragments = 20 + 376 Byte 2018-11-14
Fragmentation & Reassembly Start of header ID=x 1 Offset=0 Rest of header 512 data bytes Start of header ID=x Offset=0 Rest of header 1400 data bytes Start of header ID=x 1 512 Rest of header 512 data bytes Start of header ID=x 1024 Rest of header 376 data bytes
Protocol TCP UDP Reliable End-to-end Protocol Application Physical IP TCP UDP Data Link TCP Reliable End-to-end Protocol Connection-based UDP Best-effort datagram service Connectionless TCP Header Data TCP Segment IP Header Data IP Header IP Datagram Network header Data Network-Level Packet
Port Application Program using TCP/IP Well-known port Use port 16 bit number Well-known port port numbers below 256 reserved for standard services (RFC 1700) ex. FTP : port 21, TELNET: port 23 /etc/services # Network services, Internet style netstat 15/tcp ftp 21/tcp telnet 23/tcp smtp 25/tcp .
Physical Address (MAC Address) : 6 Bytes Physical Network Transport & Upper layers Network Physical Router Logical Use Logical Route over Network Address internet Physical Use Physical Route within Network Address Physical net Physical Address (MAC Address) : 6 Bytes
How to Find Mac Address? Linux uses ARP(Address Resolution Protocol) Send ARP request packet( with IP address) Responds with an ARP reply( with Physical hardware address) RARP Physical address IP address Used by gateways
Client ↔ Server Server Client 작업 순서 Client 에게 서비스를 하기 위해 기다림 서버는 자신의 통신 포트를 열고 기다림 클라이언트는 연결 요청 요청이 수락되면 서비스 요청 서버가 서비스
소켓 프로그램 시나리오
기본 네트워크 API (1) Socket SOCKET PASCAL FAR socket (int family, int type, int protocol); // 16bit 정수를 반환 (socketfd) Family 유형 Socket 유형 AF_UNIX 1 Local to host AF_INET 2 TCP,UDP AF_IMPLIMK 3 Arpanet AF_IPX 6 IPX SOCKET_STREAM 1 Stream socket SOCKET_DGRAM 2 Datagram socket SOCKET_RAW 3 Raw-Protocol interface SOCKET_RDM 4 Reliably-delivered Message
기본 네트워크 API (2) <프로토콜 사용가능 조합> Family Type Protocol Actual Protocol AF_INET SOCKET_DGRAM IPPROTO_UDP UDP SOCKET_STREAM IPPROTO_TCP TCP SOCKET_RAW IPPROTO_ICMP ICMP IPPROTO_RAW raw
기본 네트워크 API (3) Bind System call Connect system call Int PASCAL FAR bind (SOCKET s, const struct sockaddr FAR *addr, int namelen); 서버 시스템의 알려진 주소를 등록 클라이언트 스스로 자신의 주소를 등록 Connect system call Int PASCAL FAR connect (SOCKET s, const struct socketaddr FAR *name, int namelen); 연결 설정 성공 값을 반환
기본 네트워크 API (4) Listen System call Accept System call Int Pascal FAR listen(SOCKET s, int backlog); Blacklog : 얼마나 많은 클라이언트를 받아들일 것인가? 클라이언트의 연결을 기다리게 하는 함수 Accept System call 실제 연결 요청이 오면 서버가 그 연결을 받아들이라는 명령으로 사용 SOCKET PASCAL FAR accept (SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen); 반환값은 클라이언트와 통신한 새로운 socketfd 이고 클라이언트와의 통신은 새로운 socketfd을 통해서 이루어진다.
기본 네트워크 API (5) send, recv System call Closesocket System call Int PASCAL FAR send (SOCKET s, const char FAR *buf, int len, int flags); Int PASCAL FAR recv(SOCKET s, char FAR *buf, int len, int flags); Closesocket System call Int PASCAL FAR closesocket (SOCKET s); 프로세스에서 사용한소켓은 마지막으로 닫아 주고 프로세스를 종료해야 한다
기본 네트워크 API (6) 주소 변환 실제 컴퓨터가 인식하는 주소 In_addr 형태 Unsigned long PASCAL FAR inet_addr(const char FAR * cp); Char FAR * PASCAL FAR inet_ntoa (struct in_addr in);
Network in Windows 95 최근의 32bit OS는 네트워크를 기본으로 내장 WNet API 윈도우 95나 NT에서 네트워크를 통해 다른 시스템에 있는 파일, 프린터를 공유할 때 사용 Winsock처럼 네트워크 환경에 무관한 인터페이스 제공
<Wnet API로 검색해 볼 수 있는 네트워크 구성> 네트워크나 프로토콜에 관계없는 단일한 인터페이스 제공 네트워크 루트 도메인 #1 도메인 #2 도메인 #3 호스트#1 호스트#2 공유 포인터 #1 공유 포인터 #2 <Wnet API로 검색해 볼 수 있는 네트워크 구성>
WNetConnectionDialog의 실행 대화상자를 통해 연결-해제 DWORD dwResult; DWORD dwError, dwNameBufSize; char ErrorBuf[128], NameBuf[64]; dwResult = WNetConnectionDialog(hWnd, RESOURCETYPE_DISK); if(dwResult != NO_ERROR) { WNetGetLastError(&dwError, &ErrorBuf, 128, &NameBuf, 64); } WNetConnectionDialog의 실행 WNetDisconnect의 실행
네트워크 리소스를 검색하기 NETRESOURCE 구조체 필드명 DWORD dwScope 의 미 탐색할 네트워크 리소스의 범위를 구한다. - RESOURCE_CONNECTED : 현재 연결된 리소스들만 탐색 -RESOURCE_GLOALNET : 모든 리소스를 탐색 -RESOURCE_REMEMBERED : 로그온할 때마다 자동으로 연결되는 리소스들을 대상으로 검색 DWORD dwType 어떤 종류의 리소스를 탐색할 것인지를 나타냄 - RESOURCETYPE_ANY : 모든 리소스 - RESOURCETYPE_DISK : 디스크 리소스 - RESOURCETYPE_PRINT : 프린트 리소스 DWORD dwDisplayType 검색된 리소스가 어떤 리소스인지 나타낸다 - RESOURCEDISPLAYTYPE_DOMAIN : 도메인이다. - RESOURCEDISPLAYTYPE_SERVER : 서버이다. - RESOURCEDISPLAYTYPE_SHARE : 공유포인터이다. - RESOURCEDISPLAYTYPE_GENERIC : 모든 리소스
필드명 LPTSTR lpLocalName 의 미 검색 API에서 채워 주는 부분, dwScope의 값이 RESOURCE_CONNECTED, RESOURCE_REMEMBERED이면, 로칼 디바이스 이름 DWORD dwUsage dwScope의 값이 RESOURCE_GLOBALNET인 경우에만 의미 있음 - RESOURCEUSAGE_CONNECTABLE : 공유 포인터 - RESOURCEUSAGE_CONTAINER : 컨테이너 리소스 LPTSTR lpRemoteName dwScope의 값이 RESOURCE_CONNECTED이거나 RESOURCE_REMEMBERED이면, 리소스의 이름을 나타낸다. LPTSTR lpComment 네트워크 제공자가 제공한 설명 문자열을 가리킨다. 대부분 NULL 이 사용된다. LPTSTR lpProvider 이 네트워크 리소스를 제공한 네트워크 제공자의 이름을 나타낸다 이름이 알려져 있지 않으면 NULL이 쓰인다.
DWORD WNetOpenEnum(DWORD dwScope, DWORD dwType, DWORD dwUsage, LPNETRESOURCE ipNetResource, LPHANDLE iphEnum); 네트워크 리소스에 대한 검색을 수행하기 위해 네트워크 검색 핸들을 open하는 데 사용 인자 dwScope dwType dwUsage lpNetResource lphEnum 의 미 탐색할 범위를 결정, NETRESOURCE의 dwScope와 같다. 어떤 리소스를 탐색할 지 결정, NETRESOURCE의 dwType와 같다 dwScope가 RESOURCE_GLOBALNET일 때만 의미가 있다. - 0 : 모든 리소스를 연결 대상으로 삼는다 - RESOURCEUSAGE_CONNECTABLE : 연결 가능한 모든 리소스를 대상 - RESOURCEUSAGE_CONTAINER : 모든 컨테이너 리소스를 대상 네트워크 계층도상의 노드 중에 어디서부터 검색해야 할지를 나타낸다. lpNetResource가 NULL이면 루트부터 검색한다. 이 필드가 가리키는 영역으로 네트워크 리소스 검색 핸들이 리턴된다. WNetEnumResource API에 이 핸들을 인자로 사용하여 원하는 노드에 포함된 네트워크 리소스를 검색
DWORD WNetEnumResource( HANDLE hEnum, // 검색하고자 하는 네트워크 리소스의 검색핸들 LPDWORD lpcCount; // 그 리소스에 포함된 리소스의 개수 LPVOID lpBuffer, // 포함된 리소스에 대한 데이터를 얻어올 버퍼 LPDWORD lpBufferSize); // 버퍼의 크기 hEnum 인자 lpcCount lpBuffer lpBufferSize WNetOpenEnum API에 의해 리턴된 리소스의 검색 핸들 의미 가능한 모든 리소스를 검색하려면 0xFFFFFFFF, 검색된 리소스의 개수가 리턴된다. 검색 결과가 리턴되는 버퍼, NETRESOURCE로 표현, 보통 16K 버퍼 lpBuffer가 가리키는 버퍼의 크기
BOOL FAR PASCAL EnumAllResource (HWND hWnd, HDC Hdc, LPNETRESOURCE lpnr, HTREEITEM hItem) { DWORD dwResult, dwResultEnum; HANDLE hEnum; DWORD cbBuffer = 16384; DWORD cbEntries = 0xFFFFFFFF; // 모든 포함된 리소스를 검색 LPNETRESOURCE lpnrLocal; // lpnr이 가리키는 네트워크 리소스를 open하여, hEnum으로 네트워크 // 검색 핸들을 얻어온다. dwResult = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCE_ANY, 0, lpnr, &hEnum); if(dwResult != NO_ERROR) { DWORD dwError; char ErrorBuf[128], NameBuf[128]; WNetGetLastError(&dwError, &ErrorBuf, 128, &NameBuf, 64); MessageBox(hWnd, ErrorBuf, NameBuf, MB_OK); }
do { // 검색 결과를 돌려 받을 버퍼를 할당한다. Lpnr = (LPNETRESOURCE) GlobalAlloc(GPTR, cbBuffer); // 검색을 수행한다. dwResultEnum = WNetEnumResource (hEnum, &cbEntries, lpnrLocal, &cbBuffer); if(dwResultEnum == NO_ERROR) { // 네트워크 리소스를 display한다. for(DWORD i=0; i<cbEntries; i++) DisplayNetResource(hDC, &lpnrLocal[i], hItem); } else if(dwResultEnum != ERROR_NO_MORE_ITEMS) break; } while(dwResultEnum != ERROR_NO_MORE_ITEMS) dwReuslt = WNetCloseEnum(hEnu); if(dwResult != NO_ERROR) return FALSE; return TRUE;
네트워크 리소스에 연결-해제 WNetAddConnection WNetAddConnection2 WNetCancelConnection WNetCancelConnection2 WNetGetConnection 네트워크 리소스에 연결하기 네트워크 리소스와의 연결 해제 연결된 네트워크 리소스에 대한 정보 얻기 DWORD WNetAddConnection( LPTSTR lpRemoteName, // 공유 포인터의 이름 LPTSTR lpPassword, // 패스워드 LPTSTR lpLocalName // 지정한 로컬 디바이스로 첫번째 네트워크의 ); // 공유 포인터 추가 DWORD WNetAddConnection2( LPNETRESOURCE lpNetResource, LPCSTR lpPasswd, LPCSTR lpUsername, DWORD dwFlags );
DWORD WNetCancelConnection( LPTSTR lpName, BOOL fForce ); DWORD WNetConnection2( DWORD dwFlags DWORD WNetGetConnection( LPTSTR lpLocalName, LPTSTR lpRemoteName, LPDWORD lpnLength;