Windows Progrmming

Windows Progrmming

February 17, 2020 ( last updated : February 16, 2020 )
winapi windows programming

https://github.com/gmm117/gmm117.github.io


Abstract

    윈도우즈 프로그래밍 기초
    유니코드
    윈도우즈와 메시지
    텍스트 출력
    그리기 기초
    키보드
    마우스
    타이머
    자식 윈도우 컨트롤
    메뉴와 그밖에 리소스
    메모리
    동기화

윈도우즈 프로그래밍 기초

#include

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { MessageBox(NULL, TEXT(“Hello Windows!”), TEXT(“HelloMsg”), 0);

return 0; }

  1. WINNT.H : 유니코드 지원을 위한 타입 정의

  2. WINBASE.H : Kernel 함수

  3. WINUSER.H : 사용자 인터페이스 함수

  4. WINGDI.H : 그래픽 장치 인터페이스 함수

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)

  1. #define WINAPI __stdcall
  1. 컴파일 단계에서 컴파일러는 C소스 코드 파일로부터 .OBJ 파일을 생성한다.

  2. 링크 단계에 링커는 .OBJ 파일과 .LIB 파일을 겹합하여 .EXE 파일을 만든다.

-> KERNEL32.LIB, USER32.LIB, GDI32.LIB 파일은 윈도우즈 서브시스템을 사용하기 위한 ‘임포트 라이브러리’임.

  1. 비주얼 C++에서는 Debug, Release 환경 설정을 이용해서 프로그램을 컴파일 및 링크할 수 있다.

유니코드

1) int WINAPI MessageBox(HWND, LPCSTR, LPCSTR, UINT)

- WINUSERAPI int WINAPI MessageBoxA(HWND hWnd , LPCSTR lpText,    LPCSTR lpCaption,    UINT uType);
   WINUSERAPI int WINAPI MessageBoxW(HWND hWnd , LPCWSTR lpText, LPCWSTR lpCaption,   UINT uType);
  #ifdef UNICODE
  #define MessageBox  MessageBoxW
  #else
  #define MessageBox  MessageBoxA
  #endif // !UNICODE

WINBASEAPI int WINAPI lstrlenA( LPCSTR lpString ); WINBASEAPI int WINAPI lstrlenW( LPCWSTR lpString ); #ifdef UNICODE #define lstrlen lstrlenW #else #define lstrlen lstrlenA #endif // !UNICODE

윈도우즈와 메시지

// hInstance : 인스턴스 핸들, hPrevInstance : WIN16와 호환성을 위해 남겨둠,

// lpCmdLine : 프로그램에 전달되는 명령행 인자, nCmdShow : 처음 뜰 때 메인 윈도우 화면에 표시하는 방법(SW_SHOWNOMAL 등)

#include

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); HINSTANCE g_hInst; LPCTSTR lpszClass=TEXT(“First”);

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance ,LPSTR lpszCmdParam,int nCmdShow) { HWND hWnd; MSG Message; WNDCLASS WndClass; g_hInst=hInstance;

WndClass.cbClsExtra=0; WndClass.cbWndExtra=0; WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); WndClass.hCursor=LoadCursor(NULL,IDC_ARROW); WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION); WndClass.hInstance=hInstance; WndClass.lpfnWndProc=WndProc; WndClass.lpszClassName=lpszClass; WndClass.lpszMenuName=NULL; WndClass.style=CS_HREDRAW | CS_VREDRAW; //윈도우 사이즈 변경시 WM_PAINT 호출 RegisterClass(&WndClass);

hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, NULL,(HMENU)NULL,hInstance,NULL); 1번째 : 윈도우 클래스 이름을 나타내는 포인터

2번째 : 윈도우 이름을 나타내는 포인터

3번째 : 윈도우 스타일

ShowWindow(hWnd,nCmdShow);

UpdateWindow(hWnd);

while (GetMessage(&Message,NULL,0,0)) { TranslateMessage(&Message); DispatchMessage(&Message); } return (int)Message.wParam; }

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam) { switch (iMessage) { case WM_DESTROY: PostQuitMessage(0); return 0; } return(DefWindowProc(hWnd,iMessage,wParam,lParam)); }

while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }

- 메시지 처리 함수 : 메시지가 발생할 때 프로그램의 반응을 처리하는 일을 하며 WinMain 함수와는 별도로 WndProc이라는 이름으로

                            존재한다.

- WndProc은 WinMain에서 호출하는 것이 아니라 윈도우즈에 의해 호출된다.

- WinMain내의 메시지 루프는 메시지를 메시지 처리 함수로 보내주기만 할 뿐이며 WndProc은 메시지가 입력되면 윈도우즈에 의해

  호출되어 메시지를 처리한다.

- 운영체제에 의해 호출되는 응용 프로그램내의 함수를 콜백(CallBack) 함수라고 한다.

- 메시지 큐에 쌓이지 않고 곧바로 윈도우 프로시저에 전달되는 것은 ?

typedef struct tagWNDCLASSA { UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCSTR lpszMenuName; LPCSTR lpszClassName; } WNDCLASSA, *PWNDCLASSA, NEAR *NPWNDCLASSA, FAR *LPWNDCLASSA;

1) 2번째 필드(lpfnWndProc)

  - WndProc을 윈도우 클래스의 윈도우 프로시저로 설정한다.

 예) wndclass.lpfnWndProc = WndProc

  - WndProc은 이 윈도우 클래스를 기반으로 생성한 모든 윈도우에 전달되는 메시지를 처리한다.

if(!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT(“This program requires Windows NT!”), szAppName, MB_ICONERROR);

return 0; }

//프로그램 컴파일시 UNICODE 식별자를 정의하고 WINDOWS NT계열에서 RegisterClassW() 구현되어 있어서 돌아감.

// 하지만 Windows 0x 계열에서는 RegisterClassW() 구현안되므로 위에 코드처럼 사용자에게 알려주고 0을 리턴하게 함.

예) 푸시 버튼 윈도우 > 공통된 윈도우 클래스와 연관된 윈도우 프로시저는 윈도우즈 내부에 존재함.

    동일한 동작에 대한 정의 : 윈도우 프로시저/ 문자열, 화면위치 변경 등 이런 특성은 윈도우 정의에 일부분으로 설정

hwnd = CreateWindow(szAppName, //생성하고자 하는 윈도우의 클래스를 지정하는 문자열 TEXT(“The Hello Program”), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

-> CreateWindow는 HWND 타입에 윈도우 핸들을 리턴하고 프로그램들은 핸들을 이용하여 윈도우를 참조할 수 있다

ShowWindow(hwnd, iCmdShow); : 윈도우가 화면에 드러나게 한다.

  1. hwnd : CreateWindow()로 방금 생성한 윈도우 핸들

  2. iCmdShow : 초기에 윈도우가 화면에 떠허게 표시될지를 결정함(정상, 최소화, 최대화 중 하나)

  3. 윈도우 클래스에 지정한 배경 브러시로 윈도우 클라이언트 영역 지워짐

  1. 같은 점 : WM_PAINT 메시지를 윈도우 프로시저로 보냄

  2. 다른 점 : UpdateWindow를 메시지 큐에 저장하지 않고 바로 윈도우 프로시저로 보냄으로써 윈도우를 빠르게 화면에 출력하게 함.

             과거에는 컴퓨터 처리속도가 느려서 ShowWindow 하나만 했을 경우에 느리게 나오는 문제가 있어서 UpdateWindow를 쓰게
    
             되었지만 현재는 컴퓨터 속도이 빨라져서 안써도 상관은 없다.
    
  1. 윈도우즈는 현재 수행되고 있는 각 윈도우즈 프로그램마다 ‘메시지 큐’를 유지한다.

    -> 입력 이벤트가 발생시 윈도우즈는 이벤트를 ‘메시지로 바꾸어 프로그램의 메시큐에 저장해 둔다.

typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; // 메시지가 메시지 큐에 들어간 시간 POINT pt; // 메시지가 메시지 큐에 들어갈때 좌표. } MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;

  1. GetMessage(&msg, NULL, 0, 0) : 메시지 필드가 WM_QUIT이 아니라면 0이 아닌 값을 리턴함. 맞다면 0을 리턴해서 메시지 수신을

                                                못하게 함.
    
  1. TranslateMessage(&msg)
  1. DispatchMessage(&msg)

WndProc가 리턴을 해야지 DispatchMessage도 리턴하게 되고 그 이후에 GetMessage로 넘어가게 됨

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch(message)

{

case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }

  1. WM_SYSCOMMAND : 창 사용자 (시스템 또는 컨트롤 메뉴 라고도 함)는 창 메뉴에서 명령을 선택 하는 경우에 메시지를 수신 또는

                                최대화 단추를 선택 하는 경우 최소화 단추, 복원 단추, 닫기 단추 -> 매개변수에 SC_CLOSE 호출될 경우
    
  2. WM_CLOSE : 윈도우가 닫히기 전에 메시지가 전달 된다. 아직 윈도우가 파괴된 것은 아니므로 윈도우가 파괴되는 것을

                     중간에 제어 할 수 있다. -> DestroyWindow() 호출(윈도우 프로시저 : WM_DESTROY 호출)
    
  3. WM_DESTROY : 윈도우가 메모리에서 파괴될 때 발생한다. -> PostQuitMessage() 호출함으로써 메시키큐에 WM_QUIT 넣고

  4. WM_QUIT : 프로그램을 끝낼 때 발생하는 메시지이다. -> WinMain()의 메시지 루프가 종료되고 프로그램이 끝난다.

텍스트 출력

case WM_PAINT:

 BeginPaint(hwnd, &ps);                           

 EndPaint(hwnd, &ps);                                                        

 return 0;

Windows 응용 프로그램 프로그래밍 인터페이스 (API) 함수 GetSystemMetrics()를 사용 하 여 너비 및 높이 창 디스플레이의 다양한 요소의 얻을 수 있습니다.

예) int x = GetSystemMetrics(SM_CXSCREEN); Width of screen int y = GetSystemMetrics(SM_CYSCREEN); Height of screen

그리기 기초

hPen(이전 디바이스컨텍스트) = SelectObject(hdc, hPen);

=> 한 문자으로 요약가능 SelectObject(hdc, GetStockObject(WHITE_PEN))

1) 생성한 모든 GDI 객체는 결국 삭제해야한다.

2) 유효한 디바이스 컨텍스트에 선택되어 있는 GDI 객체는 삭제해서는 안된다.

CreatePen - // hPen = SelectObject // SelectObject(hdc, hPen) // DeleteObject

3) 내장 객체는 삭제해서는 안되다.

1) CreatePenIndirect에서 서로 다른 펜을 직접 초기화하여 사용할 경우 더욱 효과적이다.(LOGPEN 구조체를 이용)

1) GetObject(hPen, sizeof(LOGPEN), (LPVOID)&logpen);

1) hPen = GetCurrentObject(hdc, OBJ_PEN);

1) SelectObject() : 디바이스 컨텍스트 선택

 - 리전 핸들로 FillRgn, FramRgn, InvertRgn, PaintRgn 쓰이게 된다.

키보드

* 큐의 동기화

사용자가 키를 누르고 뗄 경우 -> 하드웨어 스캔 코드(디바이스드라이버) -> 시스템 메시지 큐 -> 애플리케이션 메시지 큐

* 키누름 메시지

1) 반복카운트(0~15 : 16비트) : 키누름 메시지가 나타내는 키누름의 개수다.

2) OEM 스캔코드(16~23 : 8비트) : 키보드 하드웨어가 발생시키는 코드

3) 확장키 플래그(24) : IBM 확장 키보드에 있는 추가적인 키에 누름이 발생했을 1로 세팅

4) 컨텍스트코드(29) : Alt키가 눌려져 있으면 1로 세팅

5) 이전키상태(30) : 키를 이전에 뗀 상태라면 0이고, 키를 이전에 누른 상태라면 1이다.(KeyUp은 항상 1로 세팅됨)

6) 전환상태(31) : WM_KEYDOWN : 0, WM_KEYUP : 1(키를 누르면 0, 키를 떼는 중이면 1)

0x8000 은 현재 키가 눌려진 상태를 뜻하고, 0x0001은 지난번 호출과 이번 호출사이에 키가 눌려진 적이 있었다라는 것을 뜻한다.

예) if( GetAsyncKeyState(VK_RETURN) & 0x8000 ) // 하는 이유는 정확한 시점에서 키의 상태를 확인하기 위해서

1) GetKeyState 함수의 리턴값이 0x8xxx 경우에는 해당키가 눌린 상태이고,0x8xxx가 아닐 경우는 해당키가 눌리지 않은 상태이다.

1) GetAsyncKeyState : 메시지큐와 상관없이 입력되는 순간 즉각적으로 읽어들여서 동작한다.

  GetKeyState: 메시지큐에 저장된 메시지에 따라 반환되는 값이 다르다.

2) GetKeyState는 해당키가 눌렸으면 음수값(0xffffff80, oxffffff82) 아닐 경우 0을 반환, 이전에 누르고 호출시점에 안눌릴경우 1로 남는다.

1) GetMessage로 문자 메시지 - TranslateMesage : 키누름메시지(WM_KEYDOWN, WM_SYSKEYDOWN),

                                                                        Shift key(Shift, Ctrl, Alt), 토글키(Caps Lock, Num Lock, Scroll Lock)

  => 메시지 큐에 WM_CHAR( 문자메시지를 넣게 된다)

case WM_CHAR:

if(wParam == ‘\b’) // if(wParam == ‘\t’)

WM_KEYDOWN WM_DEADCHAR WM_KEYUP WM_KEYDOWN WM_CHAR WM_KEYUP

1) BOOL CreateCaret(HWND, HBITMAP, nWidth, nHeight)

2) BOOL DestroyCatret(VOID)

3) BOOL ShowCaret(hwnd) // 주기적으로 계속 깜박이며 다음 삽입 위치를 기다린다.

4) BOOL HideCaret(hwnd)

5) BOOL SetCaretPos(int X, int Y) : 캐럿의 위치를 지정(캐럿의 숨겨져 있어도 위치 변경가능)

6) BOOL GetCaretPos(LPPOINT)

마우스

  1. 키보드 메시지, 마우스 메시지 다른점
  1. 마우스 위치값
  1. wParam : 마우스 버튼, Shift, Ctrl 키의 상태를 나타낸다.

MK_CONTROL, MK_LBUTTON, MK_RBUTTON, MK_MBUTTON, MK_SHIFT 조합키 상태를 알려줌.

GetCursorPos(&point) // point 값은 스크린 좌표값이다. 클라이언트로 영역으로 바꿀경우 ScreenToClient 함수를 써야함.

SetCursorPos(x, y) //x ,y 값도 클라이언트 좌표가 아닌 스크린 좌표값이다.(클라이언트 좌표값이면ClientToScreent 함수를 이용해야함

타이머

타이머 CH8.타이머 / Windows 프로그래밍

      1. 20:33 수정 삭제 복사https://blog.naver.com/ssinga1030/90171478579 통계보기
        • 윈도우즈 타이머의 활용

1) 멀티태스킹 : 많은 프로그램이 많은 양을 처리해야한다면, WM_TIMER 메시지를 받을 때마다 각 조각들을 처리가능

2) 실시간 갱신 : 시스템 리소스 표시같은 끊임없이 변하는 정보를 타이머로 갱신한다.

3) 자동저장 : 특정 주기에 맞게 사용자 작업을 저장한다.

4) ‘데모’버전 프로그램 종료 : 데모버전 전달 후 일정시간이 지나서 종료하도록 설계한다.

5) 움직임 속도 조절 : 자동으로 연속된 화면을 보여줄때 속도를 조절할 때.

6) 백그라운드에서 작업중인 시각적인 정보를 갱신할 때 타이머를 사용

1) 첫번째 방법

2) 두번째 방법(콜백함수)

1번째 인자 : 윈도우 핸들/ 2번째 인자 : 항상 WM_TIMER / 3번째 인자 : 타이머 ID/4번째 인자 : 윈도우즈가 시작후 경과된 시간

3) 세번째 방법

typedef struct _SYSTEMTIME { // st WORD wYear; WORD wMonth; WORD wDayOfWeek; WORD wDay; WORD wHour; WORD wMinute; WORD wSecond; WORD wMilliseconds; } SYSTEMTIME;

자식 윈도우 컨트롤

CreateWindow { TEXT(“button), //클래스 이름

         TEXT("PUSHBUTTON);               // 윈도우 텍스트

                   WS_CHILD|WM_VISIBLE             // 윈도우 스타일

                   cxChar                                      // x위치

                   cyChar                                      // y위치   

                   20*cxChar                                 // 폭            

                   70*cyChar/4                             // 높이

                   hwnd                                       // 부모윈도우            

                   (HMENU) i                                 // 자식윈도우 ID            

                   ((LPCTREATESTRUCT) lParam)->hIstance  // 인스턴스 핸들           

                   NULL                                        // 여분의 매개변수

hwndChild = GetDlgItem(hwndParent, id);

1) BM_GETCHECK, BM_SETCHECK

- 체크박스와 라디오 단추의 체크표시를 설정하기 위해서 보낸다.

2) BM_GETSTATE, BM_SETSTATE

- 한 윈도우를 마우스로 누르거나 Space Bar를 눌렀을 때의 상태를 의미한다.

3) BM_SETSTYLE

- 단추가 만들어진 후 단추의 스타일을 변경할 수 있게 한다.

4) BM_CLICK, BM_GETIMAGE, BM_SETIMAGE

1) 부모윈도우 -> 자식윈도우 포커스 움직일경우

case WM_KILLFOCUS : ( 부모에 KILLFOCUS -> wParam에 입력포커스를 얻은 자식윈도우 핸들이 들어가 있음

for( i=0; i< NUM; i++)

{

    if(hwndChild[i] == (HWND)wParam)

    {

SetFocus(hwnd)

break;

    }

}

2) 자식 윈도우 -> 부모 윈도우 포커스 움직일경우

      case WM_KILLFOCUS:

      if(hwnd == GetParent((HWND)wParam)

      {

                  SetFocus(hwnd);

                  break;

       }

LRESULT CALLBACK WinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {

switch (msg) {
    case WM_CTLCOLORSTATIC: {
        if ((HWND)lParam == filterNameOff) {
        static HBRUSH hBrushColor;

            if (!hBrushColor) {
                hBrushColor = CreateSolidBrush(RGB(0xFF, 0xFF, 0xFF)); // White background is returned
                SetBkColor((HDC)wParam, RGB(0xFF, 0xFF, 0xFF));          // White background for text
            }

            // Background color that is used to repaint the control (doesn't affect text background)
            return (LRESULT)hBrushColor;
        }
    }

예) SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG)CreateSolidBrush(RGB(color[0], color[1], color[2]));

메뉴와 그밖에 리소스

1) 디자이너와 프로그래머가 분담하여 작업을 하기가 편리하다. 프로그래머는 열심히 프로그램의 논리를 만들고 디자이너는 리소스만

 예쁘게 만들어 결합시키면 되기 때문이다.

2) 리소스를 수정하더라도 프로그램을 일일이 다시 컴파일하지 않아도 되므로 컴파일 속도가 현저히 빨라진다.

3) 한번 만들어 놓은 리소스를 다른 프로젝트에 쉽게 가져다 쓸 수 있어 리소스의 재사용에 유리하다.

4) 리소스는 교체가 가능한 모듈이므로 상황에 따라 다른 형태의 리소스를 사용할 수 있다.(다국어 버전, 스킨기능 등)

1) RC파일 : 사용하고자 하는 리소스의 종류, 모양 등을 작성한 파일

2) RES : RC파일을 리소스 컴파일러(RC.exe)로 컴파일 하면 RES라는 이진 파일이 생성됨.

-> 링크과정에서 obj, res 파일이 합쳐져서 .exe 실행파일이 생성됨.

3) Resource.h : RC파일에서 지정한 Define된 값을 이용하여서 Resource.h파일을 그 리소스에 ID값을 부여함.

 예) *.rc             : MENUITEM "&New",                        ID_MENUITEM40020

      Resource.h  : #define ID_MENUITEM40020                40020

● 알게된 내용 - DISCARDABLE(윈도우즈가 (필요하다면) 추가적인 공간을 얻기 위해 리소스를 메모리에서 제거할 수 있음을 나타낸다.)

  1. 리소스를 만들고 프로젝트에 포함시키는 과정을 정리

  2. 프로그램에 아이콘 추가하기

-> *.rc : IDI_ICON ICON DISCARDABLE "icondemo.ico"

  *.h :  #define IDI_ICON                        101

#define MAKEINTRESOURCEA(i) ((LPSTR)((ULONG_PTR)((WORD)(i))))

-> 정수타입의 리소스ID를 문자열 포인터에 대입할 수 없으므로 캐스팅을 할 때 쓰이는 매크로가 MAKEINTRESOURCE 이다.

● LoadIcon(NULL, IDI_APPLICATION) - hInstance가 NULL 이므로 윈도우즈가 미리 정의된 아이콘임을 알려준다.

                                                      IDI_APPLICATION은 WINUSER.H 미리 정의되어 있다.

                                                      #define IDI_APPLICAION MAKEINTRESOURCE(32512)



  1. 사용자 맞춤 커서 추가하기
  1. 문자열 리소스

1) 우선 첫째로 문자열 자체가 코드와 분리됨으로써 문자열만 따로 관리할 수 있으며 프로젝트를 유지하는데도 큰 도움을 준다.

2) 문자열 리소스를 사용하는 두번째 이점은 다국어 버전을 쉽게 만들 수 있다는 점이다

3) 문자열이 소스와 분리되어 있으면 문자열을 고쳐도 소스를 다시 컴파일할 필요가 없어 개발 기간도 빨라진다.

  1. 메뉴

    • 메뉴 추가

      Insert/Resource - Menu 항목 추가

    • 메뉴 속성 변경

      Caption : 사용자에게 보여질 메뉴의 이름 / ID : 프로그램에서 이 메뉴 항목을 지칭하는 이름이다.

    • 메뉴항목의 속성 : ID_상위메뉴_캡션 : ID_FILE_MENU

    • 윈도우 클래스를 정의

      wndclass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1)

    • 메뉴의 특성

      1) 1번째 특성은 메뉴에 보이는 것 : 텍스트 문자열 or 비트맵

      2) 2번째 특성은 WM_COMMAND에 wParam 하위비트에 각 메뉴의 ID값을 전달해서 메뉴의 선택을 윈도우 프로시저에게 알려줌

      3) 3번째 특성은 메뉴 항목의 속성(비활성, 활성, 체크등을 표시해줌)

● 알게 된 내용

  - HMENU GetMenu(hwnd) - 메뉴의 핸들얻음

  - CheckMenuItem(hMenu, menuID, uCheck)

    예) CheckMenuItem(hMenu, LOWORD (wParam) - ID, MF_CHECKED) ;

  - EnableMenuItem(hMenu, menuID, nEnable)

    예) EnableMenuItem(hMenu, LOWORD (wParam), MF_GRAYED/MF_ENABLED)
  1. 키보드 가속기

HACCEL hAccel;

hAccel=LoadAccelerators(hInstance,MAKEINTRESOURCE(IDR_ACCELERATOR1)); while(GetMessage(&Message,0,0,0)) { if (!TranslateAccelerator(hWnd,hAccel,&Message)) { TranslateMessage(&Message); DispatchMessage(&Message); } }

대화상자

WndClass.cbClsExtra=0; WndClass.cbWndExtra=0; WndClass.hbrBackground=(HBRUSH)(COLOR_WINDOW+1); WndClass.hCursor=LoadCursor(NULL,IDC_ARROW); WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION); WndClass.hInstance=hInstance; WndClass.lpfnWndProc=WndProc; WndClass.lpszClassName=lpszClass; WndClass.lpszMenuName=NULL; WndClass.style=CS_HREDRAW | CS_VREDRAW; RegisterClass(&WndClass);

BOOL CALLBACK AboutDlgProc(HWND hDlg,UINT iMessage,WPARAM wParam,LPARAM lParam) { switch (iMessage) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: EndDialog(hDlg,IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg,IDCANCEL); return TRUE; } break; } return FALSE; }

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam) { switch (iMessage) { case WM_LBUTTONDOWN: DialogBox(g_hInst,MAKEINTRESOURCE(IDD_DIALOG1),hWnd,AboutDlgProc); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return(DefWindowProc(hWnd,iMessage,wParam,lParam)); }

1) 대화상자 프로시저 정의

- 윈도우 프로시저가 윈도우에서 발생하는 메시지를 처리하는 것과 마찬가지로 대화상자 프로시저는 대화상자에서 발생하는 메시지를

   처리함.

2) 대화상자 프로시저 역할

- 대화상자 생성 시 자식 윈도우 컨트롤 초기화, 자식 윈도우 컨트롤이 발생시킨 메시지 처리, 대화상자의 종료 처리

- WM_PAINT, 키보드와 마우스 메시지 처리하지 않음

3) 대화상자, 윈도우 프로시저 다른점

1) 윈도우 : LRESULT / 대화상자 : BOOL형 리턴 ( 대화상자가 FALSE를 리턴할 경우 그 메시지 나머지는 운영체제가 처리함)

2) 윈도우 : 특정 메시지 처리하지 않을 경우(DefWindowProc()호출) / 대화상자 : 메시지 처리하면 TRUE, 그렇지 않으면 FALSE

3) 윈도우 : WM_CREATE / 대화상자 : WM_INITDIALOG 호출됨< 초기화 >

                                     -> WM_PAINT, WM_DESTROY 메시지 처리 안함.

4) WM_COMMAND 호출

 - 대화상자 프로시저에서 주로 처리하는 메시지

 - LOWORD(wParam)에 메시지를 보낸 컨트롤의 ID가 전달, HIWORD(wParam) 통지코드가 전달됨.

int DialogBox(HINSTANCE hInstance, LPCTSTR lpTemplate, HWND hWndParent, DLGPROC lpDialogFunc)

BOOL EndDialog(HWND hDlg, int nResult)

  1. 핸들과 ID

1) 윈도우 핸들 얻는 방법(차일드 ID 알경우)

HWND GetDlgItem(HWND hDlg, int nIDDlgItem) : 1번째 인수는 대화상자의 핸들, 두번째 인수로 컨트롤의 ID주면 핸들값을 얻을 수 있다.

=> 컨트롤을 이동시키거나 숨길 때 핸들값이 사용됨(ShowWindow, MoveWindow)

2) 컨트롤의 윈도우 핸들로부터 ID를 구하는데 사용

int GetDlgCtrlID(HWND hwndCtl)

id = GetWindowLong(HWND, GWL_ID);

=>  윈도우 핸들로부터 ID값을 구하는데 사용함.

SendDlgItemMessage(HWND, hDlg, int nID, UNIT MSG, WPARAM wParam, LPARAM lPrame)

=> SendMessage(GetDlgItem(hDlg, ID) ~ 2개의 함수를 합쳐놓은 API 제공

=> SendDlgItemMessage(hdlg, id, BM_SETCHECK, 1,0);

  1. 문자열을 읽고 출력하는함수
  1. 정수를 읽고 출력하는 함수

1. SendMessage(GetDlgItem(hDlg, id), BM_SETCHECK, i == LOWORD(wParam), 0);

for(int I=IDC_BLACK; I<=IDC_WHITE; I++)

SendMessage(GetDlgItem(hDlg, I), BM_SETCHECK, I==LOWORD(wParam), 0);

  1. SendDlgItemMessage(hDlg, id, BM_SETCHECK, i == LOWORD(wParam), 0);

for(int I=IDC_BLACK; I<=IDC_WHITE; I++)

SendDlgItemMessage(hDlg, I, BM_SETCHECK, I==LOWORD(wParam), 0);

  1. CheckRadioButton(hDlg, idFirst, idLast, idCheck)

-> idFirst부터 idLast 끼지 모든 라이오 버튼 컨트롤의 체크 표시를 끄되, 예외적으로 ID가 idCheck인 라디오 버튼만 체크표시한다.

1.CheckDlgButton(hDlg, idCheckbox, iCheck);

-> iCheck가 1이면 버튼을 체크, 0이면 체크가 제거됨.

  1. iCheck = IsDlgButtonChecked(hDlg, idCheckBox) : 체크 박스의 상태를 얻을 수 있다.
  1. DialogBoxParam(hInstance, lpTemplateName, hWndParent, lpDialogFunc, dwInitParam)

예)

typedef struct

{

int iColor, iFigure;

} ABOUTBOX_DATA

static ABOUTBOX_DATA ad = { IDC_BLACK, IDC_RECT };

if(DialogBoxParam(hInstance, TEXT(“AboutBox”), hwnd, AboutDlgProc, &ad))

-> WM_INITDIALOG 메시지를 받을 때 lParam으로 전달된다.

pad = (ABOUTBOX_DATA*) lParam;

ad = *pad;

}

}

BOOL IsWindow(HWND) -> 유효한 윈도우 핸들인지 조사

BOOL IsDialogMessage(hDlg, lpMsg)

DestroyWindow(hDlg) -> 모달은 EndDialog지만 모달리스는 DestroyWindow를 호출하게 된다.

CreateDialog에서 리턴한 핸들을 NULL로 초기화해줘야함.

1) 파일열기 대화상자

2) 색상 대화상자

3) 폰트 선택 대화상자

4) 찾기 대화상자

클립보드

HGLOBAL hmem;

TCHAR *ptr;

hmem=GlobalAlloc(GHND, lstrlen(str)+1); ptr=(TCHAR *)GlobalLock(hmem); memcpy(ptr,str,lstrlen(str)+1); GlobalUnlock(hmem);

if (OpenClipboard(hWnd)) { // 클립보드 독점 EmptyClipboard(); SetClipboardData(CF_TEXT,hmem); CloseClipboard();

}

int CountClipboardFormats(void) // 등록된 포멧의 개수

UINT EnumClipboardFormats(UINT format); // 각 포멧 열거함

int GetClipboardFormatName(UINT format, LPTSTR lpszFomatName, int cchMaxCount); // 포멧의 등록 이름을 구함.

프린터

프로그램 - GDI - 스풀러 - 디바이스드라이버 - 프린터

  1. 스풀러 : 적절한 프린터 디바이스 드라이버를 메모리 로딩 - 고수준 출력 명령을 저널 레코드로 변환 - 디스크에 파일로 저장

    • 저장된 출력 명령을 바탕으로 백그라운드에서 인쇄 - 저널레코드 (디바이스드라이버 알수있도록 DDI(Device Driver Interface) 변환

pd.lStructSize = sizeof(PRINTDLG); //구조체 크기 pd.Flags = PD_RETURNDC; // 프린터 DC 구함 pd.hwndOwner = hWndMain; // 윈도우 핸들 pd.nFromPage = 1; pd.nToPage = 1; pd.nMinPage = 1; pd.nMaxPage = 1; pd.nCopies = 1; PrintDlg(&pd); hPrtdc = pd.hDC; // 프린트 DC 사용을 위해 HDC 대입

  1. HDC CreateDC(LPCTSRT lpszDriver, LPCTSTR lpszDevice, LPCTSTR lpszOutput, CONST DEVMODE *lpInitData)

      1. driver name, 2. device name, 3. NULL 4. 디바이스 드라이버에 특정 초기화 데이터
    • 특정 이름의 디바이스의 DC를 구할 때 사용함.

PRINTER_INFO_4 *pi4;

EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &cbNeed, &cbReturn); pi4=(PRINTER_INFO_4 *)malloc(cbNeed); EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, (PBYTE)pi4, cbNeed, &cbNeed, &cbReturn);

hdc = CreateDC(NULL, pi4->pPrinterName, NULL, NULL);

  1. 인쇄의 시작
- int StartDoc(HDC hdc, Const DOCINFO *lpdi);

doc.cbSize = sizeof(DOCINFO); doc.lpszDocName = TEXT(“TEST Document”); doc.lpszOutput = NULL; // 출력파일 이름, NULL 일 경우 프린터 DC로 출력 doc.lpszDatatype = NULL; StartDoc(hPrtdc, &doc);

  1. 프린터 드라이버에게 데이터를 받아들이도록 준비시킴(새로운 페이지 시작)
  1. 한 페이지 출력을 완료 후 새 용지를 로드

EndPage(hPrtdc);

  1. 인쇄가 마무리

EndDoc(hPrtdc);

DeleteDC(hPrtdc);

xpage = GetDeviceCaps(hPrtdc, HORZRES); // 장치크기의 픽셀수 ypage = GetDeviceCaps(hPrtdc, VERTRES); Rectangle(hPrtdc,0,0,xpage,ypage);

dpiY = GetDeviceCaps(hPrtdc, LOGPIXELSY); // 인치당 픽셀수

// 화면 해상도와 프린터의 해상도가 높기 때문에 아래와 같은 계산식이 필요함.

// 1 inch = 72 point = 인치당 픽셀수 구한값(pixel)

 point = n*1/72*dpiY 

MyFont = CreateFont(20*dpiY/72, 0,0,0,FW_NORMAL, FALSE,FALSE,FALSE,HANGEUL_CHARSET,3,2,1,

                           VARIABLE_PITCH|FF_ROMAN,TEXT("궁서"));

Result=GetDeviceCaps(hPrtdc, RASTERCAPS) & RC_BITBLT; if (!Result) goto end; hbit=(HBITMAP)LoadImage(g_hInst, MAKEINTRESOURCE(IDB_BITMAP1), IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION); .. MemDC=CreateCompatibleDC(hPrtdc); OldBitmap=(HBITMAP)SelectObject(MemDC, hbit); StretchBlt(hPrtdc,dpiX,dpiY,4dpiX,4by/bx*dpiY,MemDC,0,0,bx,by,SRCCOPY);

if (pd.Flags & PD_PAGENUMS) { nFirstPage=pd.nFromPage; nFinalPage=pd.nToPage; } else { nFirstPage=pd.nMinPage; nFinalPage=pd.nMaxPage; }

  1. 시작페이지와 끝페이지 인쇄(for문으로 돌면서 인쇄해야함)

for(nPage = nFirstPage; nPage <= nFinalPage; nPage++)

 {

      StartPage()

      EndPage()

  }

  EndDoc();
  1. 취소 프로시저와 취소 대화상자 필요함.

// 취소대화상자

g_hDlgCancel=CreateDialog(g_hInst, MAKEINTRESOURCE(IDD_DIALOG1), hWndMain, (DLGPROC)AbortDlgProc); // 모달리스로 대화상자 만듦

LRESULT CALLBACK AbortDlgProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: g_bPrint=FALSE; // 취소 버튼 누를시 FALSE 세팅 EnableWindow(hWndMain, TRUE); DestroyWindow(g_hDlgCancel); g_hDlgCancel=NULL; return TRUE; } return FALSE; }

// 최소 프로시저. 이 함수에서 TRUE를 리턴하면 인쇄를 계속하고 FALSE를 리턴하면 인쇄를 취소한다.

SetAbortProc(hPrtdc, (ABORTPROC)AbortProc); // 취소 프로시저 추가

EnableWindow(hWndMain, FALSE); // 인쇄하려는 동안 프로그램을 종료, 다른 작업을 못하게 하기위해서

BOOL CALLBACK AbortProc(HDC hPrtdc, int iError) { MSG msg; while (g_bPrint && PeekMessage(&msg, NULL,0,0,PM_REMOVE)) { // g_bPrint 가 FALSE 가 될시 인쇄가 취소된다. if (!IsDialogMessage(g_hDlgCancel, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); }

return g_bPrint;

}

  1. 프린터 열거

typedef struct _PRINTER_INFO_4A { LPSTR pPrinterName; LPSTR pServerName; DWORD Attributes; } PRINTER_INFO_4A, *PPRINTER_INFO_4A, *LPPRINTER_INFO_4A;

PRINTER_INFO_4 *pi4;

EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &cbNeed, &cbReturn); pi4=(PRINTER_INFO_4 )malloc(cbNeed); EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, (PBYTE)pi4, cbNeed, &cbNeed, &cbReturn); for (i=0;i<cbReturn;i++) { wsprintf(Mes,”프린터 이름: %s, 종류:%s “, pi4[i].pPrinterName, pi4[i].Attributes==PRINTER_ATTRIBUTE_LOCAL ? “로컬”:”네트워크”); TextOut(hdc,10,y++20,Mes,lstrlen(Mes)); } free(pi4);

비트맵과 Bitblt함수

-> 메모리 DC에 그려두고 화면 DC에 고속복사해서 보여주게 하기 위해서 사용됨.

  1. BOOL PatBlt(HDC hdc, int nXLeft, int nYLeft, int nWidth, int nHeight, DWORD dwRop) - 래스터 연산 : 16가지

    • 지정한 사각영역을 현재 DC에 선택된 브러시로 채움

    • FillRect는 사각영역으로 브러시로 채우지만 PatBlt는 dwRop에 따라서 바뀔 수가 있다.(검정, 흰색으로 채울 경우)

  1. DDB 비트맵 생성함수
얻음  -> 내부적으로 CreateBitmap 호출함.

장치독립적인비트맵

  1. BITMAPFILEHEADER 구조체

    • 비트맵 파일 자체에 관한 정보를 가진다.

typedef struct tagBITMAPFILEHEADER { WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; } BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;

  1. BITMAPINFOHEADER 구조체

    • BITMAPFILEHEADER 구조체 바로 뒤에 위치하면서 DIB의 크기(가로 폭, 세로 높이)와 색상 포멧에 관한 정보등을 가진 구조체이다.

typedef struct tagBITMAPINFOHEADER{ DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;

  1. RGBQUAD 구조체

typedef struct tagRGBQUAD { BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; } RGBQUAD;

  1. DIB 출력

    1) SetDIBitsToDevice

BYTE* pRaster;

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);

int StretchDIBits(HDC hdc,int XDest,int YDest,int nDestWidth,int nDestHeight,int XSrc, int YSrc,int nSrcWidth,int nSrcHeight

                   CONST VOID *lpvBits,CONST BITMAPINFO *lpbmi,UINT iUsage,DWORD dwRop);
  1. DDB 변환

HBITMAP CreateBitmap(HDC hdc, CONST BITMAPINFOHEADER *lpbmih, DWORD fdwInit, CONST VOID *lpbInit

                               CONST BITMAPINFO* lpbmi, UINT fuUsage)'

{

if(hdc)

hBitmap = CreateCompatibleBitmap(hdc, cx, cy);

else

hBitmap = CreateBitmap(cx,cy,1,1,NULL);

if(fdwInit)

{

hdcMem = CreateCompatibleDC(hdc);

SelectObject(hdcMem, hBitmap);

SetDIBitsToDevice(hdcMem,0,0,cx,cy,0,0,0,cy,lpbInit,lpbmi,fuUsage);

DeleteDC(hdcMem);

}

return hBitmap;

}

  1. DIB 섹션
  1. DIB로 변환

텍스트와 폰트

  1. SetTextAlign(HDC hdc, UINT fMode)

    ● fMode : 지정하는 정렬 Mode를 세팅함.

    TA_UPDATECP : TextOut에서 지정한 x, y 좌표를 무시하고 현재 CP(current position)를 이용한다.

    TA_NOUPDATECP : CP를 사용하지 않고 지정한 좌표를 사용하며 CP를 변경하지 않는다.

  2. TabbedTextOut(hdc, xStart, yStart, pString, iCount, iNumTabs, piTabStops, xTabOrigin)

    • 텍스트 문자열이 탭 문자(‘\t’, 0x09)를 포함한다면,TabbedTextOut()은 인자로 전달된 정수 배열에 기초하여 탭을 공백으로 확장함.

예) int tabstop[4] = {8,16,24,32}

  wsprintf(temp, "tabbedTextOut\t를\t이용한\t출력");

  1) TabbedTextOut(hdc,0,20,temp,strlen(temp),4,tabstop,0);

  => iNumTabs : 탭의 개수,  piTabStops : 탭 위치를 가지는 배열 

  => 첫 번째 탭에서는 설정된 문자열의 평균 가로크기의 8배, 두번째 탭에서는 16배 공백으로 띄워줌.

  2) TabbedTextOut(hdc,0,20,temp,strlen(temp),1,tabstop,0);

  => 탭키를 만날 때마다 tabstop의 첫 번째 값 즉 8값만큼 균일적으로 공백으로 띄어 주게 된다.
  1. ExtTextOut(hdc, x, y,

                  fuOptions, - 텍스트 출력 옵션
    
                  &rect  - 사각영역
    

lpString,

                   cbCount,

                   lpDx ) - 문자사이 공백값 배열 포인터(int pDx[]={5,6~~} pDx[i] 문자와 오른쪽문자 자간

 - 현재 선택된 글꼴, 배경색, 글자색을 이용하여 문자열을 그린다. 

 - &rect, lpDx 둘다 NULL인 경우 TextOut과 같은 기능이다.(TextOut을 내부적으로 보믄 ExtTextOut 호출함.)

1) fuOptions : 어플리케이션이 정의한 사각형을 사용하는 방법을 지정한다.

  ● ETO_CLIPPED : 텍스트가 주어진 사각형으로 클리핑 된다. 영역 안쪽에만 문자열이 출력된다.

  ● ETO_OPAQUE : 현재의 배경 색상으로 채워질 배경 직사각형
  1. DrawText(hdc, pString, iCount, &rect, iFormat);
- iFormat을 0으로 설정 : 윈도우즈는 텍스트를 캐리지 리턴문자('\r'), 라인피드('\n')로 분리된 일련의 행으로 해석함

                                  DT_WORDBREAK : 사각영역의 오른쪽 끝에서 자동 개행되도록 한다.

                                  DT_NOCLIP : 사각영역의 경계를 벗어나도 문자열을 자르지 않고 그대로 출력

- DrawTextEx(hdc, pString, iCount, &rect, iFormat, &drawtextparams)

typedef struct tagDRAWTEXTPARAMS { UINT cbSize; // 구조체의 크기 int iTabLength; // 각 탭 정지점의 크기 int iLeftMargin; //왼쪽 여백 int iRightMargin; //오른쪽 여백 UINT uiLengthDrawn; // 처리된 문자들의 개수를 받는다. } DRAWTEXTPARAMS, FAR *LPDRAWTEXTPARAMS;

typedef struct tagLOGFONTW { LONG lfHeight; LONG lfWidth; LONG lfEscapement; // 문자열 각도 LONG lfOrientation; // 개별 문자의 각도 LONG lfWeight; // 문자의 굵기 BYTE lfItalic; // 기울기 BYTE lfUnderline; // 밑줄 BYTE lfStrikeOut; // 취소선 BYTE lfCharSet; // 문자셋 BYTE lfOutPrecision; // 출력정확도(폰트의 높이, 폭, 피치등에 근접한 폰트를 찾을지 지정함) BYTE lfClipPrecision; //클리핑 영역을 벗어날 때의 처리방법 BYTE lfQuality; // 출력품질(글꼴, 폰트의 외형을 중시) BYTE lfPitchAndFamily; // 피치 : 개별 글자의 폭, 폰트의 패밀리 WCHAR lfFaceName[LF_FACESIZE]; //타입페이스 : 폰트의 이름 } LOGFONTW, *PLOGFONTW, NEAR *NPLOGFONTW, FAR *LPLOGFONTW;

-> CreateFont의 인자값과 같다. LOGFONT 구조체는 CreateFontIndirect의 인자로 쓰이게 된다.

  1. 폰트 열거 함수

1) int EnumFontFamilies(HDC hdc, LPCTSTR lpszFamily, FONTENUMPROC lpEnumFontFamProc, LPARAM lParam)

   lpszFamily : 조사하고자 하는 폰트 패밀리 지정함(값이 NULL이면 모든 패밀리를 조사한다.)

   lpEnumFontFamProc : 발견된 폰트의 특성을 알려줌

2) int CALLBACK EnumFontFamProc(ENUMLOGFONT FAR *lpelf, NEWTEXTMETRIC FAR *lpntm, int FontType, LPARAM lParam)

   ENUMLOGFONT - LOGFONT 구조체, ElFullName - 폰트타입 문자열,

   NEWTEXTMETRIC : 폰트의 여러가지 특성값을 가지고 있음.

   FontType : 폰트의 종류(장치, 래스터, 트루타입)
  1. PenStyle : PS_SOLID, PS_JOIN_ROUND등 스타일 지정

  2. dwWidth : 선의 굵기

  3. LOGBRUSH : 구조체안에서 선의 무늬, 스타일 지정

  4. dwStyleCount, lpStyle : PS_USERSTYLE 지정(사용자 정의 선을 그을 때 사용 - 안쓸경우 : 인수 두개는 0, NULL로 처리)

- dwStyleCount : lpStyle 배열의 크기

- lpStyle : 5픽셀 찍고 다음 5픽셀 찍지말고.

예) DWORD Style[] = {5,5,4,4,3,3,2,2,1,1};

     hGeo = ExtCreatePen(PS_USERSTYLE|PS_GEOMETRIC, 1, &logBrush, 10, Style)

다중문서 인터페이스

  1. CreateWindow에 스타일

    • WS_CLIPCHILDREN 클라이언트 윈도우가 다시 그려질 시 자식 윈도우에 WM_PAINT 메시지를 보내지 못하게 하기 위해 세팅함.
  1. 자식 윈도우 바둑판식 정렬
  1. 자식 윈도우 계단식 정렬
  1. 자식 윈도우 아이콘 정렬
  1. 마지막은 DefMDIChildProc를 불러서 처리해야함(DefWindowProc에 인수와 동일함.)

index : 윈도우에 여분 메모리가 있을 경우 여분 메모리의 오프셋을 지정할 수도 있다. 이 값은 반드시 양수여야 하며 cbWndExtra-4보다는 작아야 한다. 예를 들어 여분 메모리가 32바이트 지정되어 있으면 nIndex는 0~28까지 지정할 수 있다.

=> GetWindowLong(hWnd, 0)을 통해서 저장된 값을 확인할 수 있다.

멀티태스킹과 멀티스레딩 & 동기화

1번째 인수 : 스레드 보안속성 지정(핸들끼리 상관하지 않는 한 NULL로 지정됨)

2번째 인수 : 스레드의 스택의 크기(기본 1M 잡힘)

3번째 인수 : 스레드의 시작주소

4번째 인수 : 스레드로 전달하는 작업 내용

5번째 인수 : 스레드 특성 지정(CREATE_SUSPENDED 플래그 지정시 스레드 만들고 실행X - ResumeThread 함수를 호출해야함)

6번째 인수 : 스레드 ID

2 스레드 특성

- 같은 프로세스 내의 스레드끼리는 주소공간, 전역변수, 코드를 공유함.
  1. 스레드 상태 확인
- BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)

  1번째 인수 : 대상 스레드 핸들

  2번째 인수 : 스레드의 종료 코드 조사해 리턴
  1. 스레드 종료
1) Return & ExitThread 차이점과 ExitThread 문제점

   - 차이점 :

2) 문제점 : A,B 함수에 C++객체가 존재할 경우 C함수에서 ExitThread 함수로 종료시 A,B 함수의 소멸자가 호출되지 않아서 메모리 유출현상이 발생되므로 가급적이면 쓰지 않고 Return 되도록 해야한다.

2) ExitThread & TerminateThread 차이점

  - VOID ExitThread(DWORD dwExitCode) / BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode)

    1. ExitThread : 실행중인 스레드의 특정 위치에서 스레드를 종료 

       TerminateThread 는 인수로 전달받은 스레드를 강제로 종료시킬 때 사용된다.

예)

RegisterClass(~)

DWORD WINAPI ThreadFunc(LPVOID temp)

{

  CreateWindow(~);

while(GetMessage(~))

{

TranslateMessage

DispatchMessage

}

}

LRESULT CALLBACK DeCompProc(~)

{

}

  1. _beginthread, _beginthreadex 차이점

    _beingthread는 새로운 스레드 생성하고 난 후 내부적으로 CloseHandle 함수 호출되서 생성된 스레드 핸들러 제거시킴

    _beingthreadex 핸들을 리턴(형변환 해야함), CloseHandle 함수가 내부호출을 안하므로 명시적으로 함수를 호출해 줘야한다.

예)

unsigned __stdcall SecondThreadFunc(void *pArg)

{

~

_endthreadex(0);

}

void main()

{

HANDLE hThread;

hThread = (HANDLE)_beginthreadex(NULL, 0, SecondThreadFunc, null, 0, &threadID);

CloseHandle(hThread);

}

  1. TLS(Thread Local Storage)
  1. TLS 함수
  1. DeadLock(교착상태) - 대기 상태가 종료되지 않아 무한정 대기만 하는 비정상적인 상태

ThreadFunc1

EnterCriticalSection(&crit1); - 1

EnterCriticalSection(&crit2); - 3

LeaveCriticalSection(&crit2);

LeaveCriticalSection(&crit1);

ThreadFunc2

EnterCriticalSection(&crit2);

EnterCriticalSection(&crit1); - 2

LeaveCriticalSection(&crit1);

LeaveCriticalSection(&crit2);

  1. 크리티컬 섹션/ 뮤텍스/ 세마포어 차이점

    1) 크리티컬 섹션 : 유저모드 객체, 가볍고 빠르다.

                          같은 Process 내의 Thread 동기화 지원
    

    2) 뮤텍스, 세마포어 : 커널모드 객체, 상대적으로 무겁고 느린편

                              여러 Process의 Thread간의 동기화를 지원
    

    3) 뮤텍스, 세마포어 차이점 : 뮤텍스는 임계구역 접근 Thread는 한개

                                        세마포어 임계구역 접근 Thread 갯수 조절 
    
  2. CriticalSection(임계영역)

    • 중단해서는 안되는 코드 블록

    • CRITICAL_SECTION 함수

      1) InitializeCriticalSection, DeleteCriticalSection - 초기화 / 파괴

      2) EnterCriticalSection, LeaveCriticalSection - 구간의 시작을 명시 / 섹션을 빠져나올 구간

  3. 뮤텍스

    • WaitForSingleObject, WaitForMultiIObject

    • CreateMutex, OpenMutex, ReleaseMutex

  4. 세마포어
    • CreateSemaphore, OpenSemaphore, ReleaseSemaphore CreateSemaphore 2번째 인자 : 임계영역 갯수, 3번째 인자 : 임계영역 접근 갯수
  5. 이벤트
    • CreateEvent, OpenEvent, SetEvent, ResetEvent
    1. 2번째 인자 TRUE : 수동리셋 이벤트 FALSE 자동리셋 이벤트
    2. 3번째 인자 TRUE : 이벤트를 생성함과 동시에 신호상태를 만든다.

동적링크라이브러리

  1. 여러 프로그램이 동시에 사용하기 때문에 메모리 절약

  2. DLL을 사용하는 프로그램은 크기가 작다.(정정링크 실행파일이 커진다.)

  3. DLL을 교체하여 프로그램의 성능을 향상시키기 쉽다.(교체시 DLL만 바꾸면됨)

  4. 리소스 교체가 가능(다국어버전)

  5. 디버깅 용이(DLL 버그 없다는 것을 가정)

  6. 혼합 프로그래밍 가능(어떤 개발툴과도 호환가능)

  7. 프로그래머끼리 분담 작업이 용이하며 재사용도 뛰어나다

  1. DLL 없다거나 손상된 경우 실행이 불가능

  2. DLL 버전 변경 시 프로그램 호환이 안될 경우 발생

  1. extern “C” __declspec(dllexport) 함수 원형 -> __declspec : 함수에 대한 정보를 제공

    extern “C” __declspec(dllimport) 함수 원형

메모리

동기화

  1. 동기화 필요성
  1. 동기화함수

1) 크리티컬 세션 : 유저레벨 동기화(커널 객체x), 동일한 프로세스내에서만 사용, 속도 높음

2) 뮤텍스 : 커널객체 사용(CreateMutex, OpenMutex, ReleaseMutex, CloseHandle), 하나의 공유자원 보호

- 포기된 뮤텍스 : 뮤텍스 점유 중 스레드를 죽이는 Terminatethread, ExitThread 사용할 경우 -> waitforsingleobject return 값이 WAIT_ABANDONED 리턴함.

3) 세마포어 : 사용가능한 자원의 갯수를 카운트 하는 동기화 객체( CreateSemaphore, OpenSemaphore, ReleaseSemaphore)

  1. 이벤트

1) 스레드간의 작업순서나 시기조정, 신호를 보냄

2) CreateEvent(. bManualReset,,) bManualReset = True 수동(ResetEvent, SetEvent 사용해야함) False 자동

Originally published February 17, 2020
Latest update February 16, 2020

Related posts :