cfile25 uf 116739214AA46D6820A4CE by 66YHCIR8

VIEWS: 0 PAGES: 424

									Win32 API


    강사 : 이준근
(egenie@korea.com)
     1.
   윈도우
프로그래밍의
    이해
1. API의 Positioning


                   Application

      MFC    VB   ATL/COM    owl   Delphi
                                            DirectX
                                            OPEN/GL
                  API
                                            HAL
             Windows Kernel

                  HardWare
2. Windows의 구성
• Windows의 특징
  – 32bit 선점형 멀티태스킹과 멀티스레딩을 지원하는 그래픽 운영
    체제이다.
    • 비선점형 :
      – 프로그램이 스스로 제어를 다른 프로그램에게 넘겨 주어야 한다.
  – GUI환경이다.
    • “GUI는 개인용 컴퓨터 산업의 가장 중요하고 유일한 ‘대중적 합
      의’라는 점은 이제 명백해 졌다.” - Charles Simonyi
    • GUI의 특징
      – 1. 더 이상 컴퓨터나 새로운 프로그램의 사용법을 배우는데 많은 시
        간을 소비하지 않아도 된다.
      – 2. 일관된 사용자 인터페이스를 이용한다는 것은 메뉴와 대화상자를
        구성할 때 Windows에 내장된 루틴을 이용한다는 것을 의미한다.
  – ‘동적 연결’이라는 개념을 중심으로 동작한다.
    • Windows에서 실행되는 프로그램은 ‘동적 연결 라이브러리’라고 하
      는 파일에 존재하는 루틴을 공유한다.
2. Windows의 구성
 – 프로그램은 시스템에 연결되어 있는 장치의 종류를 몰라도 된다.
   • 화면 및 프린터와 같은 그래픽 디스플레이 장치의 하드웨어를 직접
     사용하지 않는다.
3. 첫 번째 API프로그램
#include <windows.h>

int WINAPI WinMain (HINSTANCE hInstance,HINSTANCE hPrevInstance,
                        PSTR szCmdLine, int CmdShow)
{
        MessageBox (NULL, "Hello, Windows 98!","HelloMsg", MB_OK) ;
        return 0 ;
}

• #include <windows.h>
   – 다른 Windows헤더 파일을 포함하는 마스터 include 파일이다.

   –   WINDEF.H 기본형식 정의
   –   WINNT.H 유니코드지원을 위한 형식 정의
   –   WINBASE.H Kernel함수
   –   WINUSER.H 사용자 인터페이스 함수
   –   WINGDI.H 그래픽 장치 인터페이스 함수
4. 프로그램 진입점
int WINAPI WinMain (HINSTANCE hInstance,HINSTANCE hPrevInstance,
                        PSTR szCmdLine, int CmdShow)

    – WINAPI windef.h에 정의되어 있다.
        • #define WINAPI __stdcall
    – Parameters
        • HINSTANCE hInstance :
            – 프로그램의 인스턴스 핸들
        • HINSTANCE hPrevInstance :
            – 항상 NULL이다.
        • PSTR szCmdLine :
            – 프로그램을 실행할 때 사용된 명령줄
        • int iCmdShow :
            – 최대화할 것인지 또는 아이콘화 하여 작업 표시줄에 표시할 것인지 지
              정


__stdcall은 함수를 호출하면 호출된 함수쪽에서 리턴하기 전에 스텍에 저장된
인자들을 정리하는 방식으로 16비트시절의 PASCAL호출규약과 동일하다.
4. 프로그램 진입점
 – MessageBox :
    • 메시지 박스를 나타내는 함수
4. 사고의 전환을 위하여
• 도스 프로그래밍과 윈도우 프로그래밍의 차이점
 – 도스 프로그램 : 절차적 (또는 순차적)
   • 프로그램의 실행 흐름이 프로그래머가 기술한 코드의 순서에 따라
     진행한다.
   • 사용자의 키보드 조작이나 마우스의 움직임을 프로그래머가 일일이
     알아서 직접 제어해야 한다.
   • 외부에서 무슨 일(이벤트)이 일어났는지 프로그래머가 알아내야 하고
    이에 따른 처리까지 맡아서 해야 한다.


 – 윈도우 프로그램 :
   • 이벤트 구동(Event-Driven)방식 또는 메시지 처리 방식
      – 프로그램의 실행 흐름을 윈도우 시스템과 일을 분담하여 처리한다.
      – 외부에서 일어나는 일을 윈도우 시스템이 감지하여 해당 프로그램에 메
        시지를 전달하여 알려준다.
      – 프로그램은 이에 대한 처리만 한다.
      – 프로그래밍이 한결 수월해 진다.
   • Ex) Spy++ Test
4. 사고의 전환을 위하여
     도스 프로그래밍             윈도우 프로그래밍

프로그래머가 제어하는 데로 순차적   프로그램의 실행 흐름을 윈도우시스템
으로 실행.               과 일을 분담하여 처리한다.
프로그래머가 외부에서 어떤 일이    외부에서 발생하는 모든 일을 윈도우
발생했는지를 알아내야 하고 이에    시스템이 감지하여 Message를
따른 처리까지 맡아서 한다.      전달하여 알려 준다
                     ( 프로그램은 이에 대한 처리만 해주면
                       된다 )
5. 윈도우 프로그래밍과 친해지자
이벤트(Event)와 메시지(Message)   메시지 루프(Message Loop)

메시지 큐 (Queue)              핸들(Handle)

윈도우 프로시져                   리소스(Resource)

인스턴스(Instance)

하드웨어 운용 방식
5. 윈도우 프로그래밍과 친해지자
• 이벤트(Event)와 메시지(Message)
  – 사용자가 키보드의 특정 키를 누르거나 마우스의 좌측 버튼을
    클릭하는 등의 일을 할 때 이벤트가 발생한다.
  – 이벤트가 발생하면 윈도우 OS는 이름 감지하여 해당 프로그램
    으로 메시지를 전달한다.
  – 메시지에는 마우스의 좌측 버튼을 눌렀을 때 커서의 위치등과
    같은 부가 정보가 함께 포함되어 있다.
  – WinUser.h


   [마우스에 관련된 Message들]

   #define WM_MOUSEMOVE       0x0200
   #define WM_LBUTTONDOWN     0x0201
   #define WM_LBUTTONUP       0x0202
   #define WM_LBUTTONDBLCLK   0x0203
   #define WM_RBUTTONDOWN     0x0204
   #define WM_RBUTTONUP        0x0205

                                        ( Winuser.h)
5. 윈도우 프로그래밍과 친해지자
typedef struct tagMSG
{
          HWND          hwnd;      // 메시지를 받을 윈도우의 핸들
          UINT          message;   //전달되는 메시지 유형
          WPARAM        wParam;    //메시지와 관련된 부가적인 정보
          LPARAM        lParam;    //메시지와 관련된 부가적인 정보
          DWORD         time;      // 메시지가 전달된 시간
          POINT         pt;        //메시지가 큐에 저장될 때의 마우스 위치
} MSG;


typedef struct tagMSG
{
          HWND          hwnd;               WM_LBUTTONDOWN
          UINT          message;
          WPARAM        wParam;             Key Flag
          LPARAM        lParam;              LOWORD(lParam)
          DWORD         time;
          POINT         pt;                  HIWORD(lParam)
}MSG;
5. 윈도우 프로그래밍과 친해지자
• 메시지 큐(Message Queue)
  – 사용자의 컴퓨터 조작에 의해 발생한 이벤트는 메시지형태로 만
    들어져 윈도우 OS가 관리하는 “메시지 큐”에 저장된다.
  – 윈도우 시스템 전체를 관리하기 위한 [시스템 메시지 큐]와 응용
    프로그램마다 별도 갖고 있는 [프로그램 메시지 큐]가 있다.
5. 윈도우 프로그래밍과 친해지자
        시스템 메시지 큐      윈도우 OS

         시스템 분배기
                      (RIT:Raw Input Thread)


 프로그램 메시지 큐   프로그램 메시지 큐




   프로그램 A           프로그램 B
5. 윈도우 프로그래밍과 친해지자
• 메시지 루프
   – OS로부터 전달된 메시지를 보관하는 큐에 어떤 메시지가 들어
     왔는지를 지속적으로 감시하고 분석해주는 부분이 필요하다.
While( GetMessage( &msg,NULL,0,0) )
{
        TranslateMessage( &msg );
        DispatchMessage( &msg );
}
5. 윈도우 프로그래밍과 친해지자
• 윈도우 프로시저(Window procedure)
    – 자신이 만든 함수이지만 자신이 직접 호출하지 않고 운영체제에
      서 호출하는 함수를 콜백함수라고 한다.
    – 함수 앞에 CALLBACK으로 선언
    – 윈도우 프로시저는 콜백함수이다.
    – 모든 윈도우는 해당 윈도우 프로시저를 갖고 있다.
    – 메시지 루프에서 해석한 메시지를 구체적으로 처리하는 기능을
      수행
    – Call Back 함수는 윈도우에서 자동으로 불려지는 함수이다.그러므
      로 Language independent 해야 한다.
        • (호출형식을 통일해야 한다)
    – windef.h

LRESULT CALLBACK WndProc( HWND hWnd, UNIT uMgs,
                             WPARAM wParam,LPARAM lParam);

#define CALLBACK __stdcall
#define WINAPI  __stdcall
    5. 윈도우 프로그래밍과 친해지자
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
  HDC     hdc ;
  PAINTSTRUCT ps ;
  RECT    rect ;

    switch (iMsg)
      {
      case WM_CREATE :
          PlaySound ("hellowin.wav", NULL, SND_FILENAME | SND_ASYNC) ;
          return 0 ;
      case WM_PAINT :
          hdc = BeginPaint (hwnd, &ps) ;
          GetClientRect (hwnd, &rect) ;

          DrawText (hdc, "Hello, Windows 95!", -1, &rect,
                                            DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
          EndPaint (hwnd, &ps) ;
          return 0 ;
       case WM_DESTROY :
          PostQuitMessage (0) ;
          return 0 ;
       }
    return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}
5. 윈도우 프로그래밍과 친해지자
• 핸들 (Handle)
      – 핸들은 프로그램에서 현재 사용중인 객체들을 식별하기 위해 윈
        도우 OS가 부여하는 고유 번호이다.
      – 그러면 왜 포인터를 이용하지 않고 핸들을 이용할까?
Ex)
      HWND      윈도우에 대한 핸들
      HCURSOR   커서에 대한 핸들
      HICON     아이콘에 대한 핸들
      HMENU     메뉴에 대한 핸들

총 메모리가 80KB라고 가정하자. 이때 30KB의 그림판, 40KB를 차지
하는 Visual C++을 실행시키면
      그림판           비주얼 C++   여유공간
      (30KB)         (40KB)   (10KB)
그림판을 종료하고 20KB를 필요로 하는 메모장을 실행시키면
      메모장       여유공간     비주얼 C++       여유공간
      (20KB)    (10KB)    (40KB)       (10KB)
5. 윈도우 프로그래밍과 친해지자
=> 내부적으로 윈도우는 메모리 블록을 이리저리 이동시켜 필요
   공간을 확보한다. (윈도우OS의 메모리 관리 기법)
Ex)
HGLOBAL GlobalAlloc( UINT uFlags, // allocation attributes
 DWORD dwBytes // number of bytes to allocate);

GMEM_FIXED    Allocates fixed memory. The return value is a pointer.
GMEM_MOVEABLE Allocates movable memory.

 메모리 블록이 이리저리 이동되는 상황에서 도스에서처럼 포인터를
직접 활용한다면 OS가 할당된 메모리 블록을 함부로 이동시킬 수 없게
되고, 이동시킨다고 해도 프로그램쪽에서 이동되는 상황을 알 수 없기
때문에 엉뚱한 메모리 주소를 참조하게 될 것이다.
5. 윈도우 프로그래밍과 친해지자
• 인스턴스
 – 프로그램은 명령들이 나열된 코드 영역(Code Segment)과 데이
   터를 보관하는 데이터 영역(Data Segment)으로 구분
 – 동일한 프로그램에 코드 영역까지 별도의 메모리를 할당하면 메
   모리만 낭비하게 된다.
 – 실제 메모리 상에 할당된 객체를 인스턴스(Instance)라 한다.
 – 코드 영역에 대한 모듈 인스턴스(Module Instance)
 – 데이터 영역에 대한 데이터 인스턴스(Data Instance)두 개의 인
   스턴스가 있다.

               코드영역
             (모듈 인스턴스)
  메모장1       데이터 영역_1        메모장2
            (데이터 인스턴스)

             데이터 영역_2
            (데이터 인스턴스)
5. 윈도우 프로그래밍과 친해지자
• 리소스(Resource)
   – 메뉴,아이콘,커서,비트맵 등 사용자 인터페이스를 구성하는 자원
     들의 정적 데이터를 말한다.
   – 프로그램 실행 중 변경되지 않는 정형화된 데이터로 C나 C++같은
     언어로 기술하지 않고 리소스 스크립트에 의해 정의된다.
IDR_MAINFRAME MENU PRELOAD DISCARDABLE
BEGIN
  POPUP "파일(&F)"
  BEGIN
   MENUITEM "새 파일(&N)\tCtrl+N",          ID_FILE_NEW
   MENUITEM "열기(&O)...\tCtrl+O",         ID_FILE_OPEN
   MENUITEM "저장(&S)\tCtrl+S",            ID_FILE_SAVE
   MENUITEM "다른 이름으로 저장(&A)...",         ID_FILE_SAVE_AS
   MENUITEM SEPARATOR
   MENUITEM "인쇄(&P)...\tCtrl+P",         ID_FILE_PRINT
   MENUITEM "인쇄 미리 보기(&V)",              ID_FILE_PRINT_PREVIEW
   MENUITEM "인쇄 설정(&R)...",              ID_FILE_PRINT_SETUP
   MENUITEM SEPARATOR
   MENUITEM "최근 파일",                     ID_FILE_MRU_FILE1,GRAYED
   MENUITEM SEPARATOR
   MENUITEM "종료(&X)",                    ID_APP_EXIT
  END
END
5. 윈도우 프로그래밍과 친해지자
 – 리소스는 프로그램 코드와 분리하여 작성되며 자체 컴파일과정을
   갖는다.
 – 리소스 스크립트 파일(.RC)은 리소스 컴파일러(RC.EXE)에 의해
   이진화된 리소스 파일 (.RES)이 만들어 진다.
 – 리소스를 별도로 정의하는 이유는 메모리를 효율적으로 사용하기
   위함이다.
 – 리소스에는 정적인 데이터가 있기 때문에 일반 변수와는 다른 메
   모리 관리를 한다.
 – 보통 리소스 데이터는 필요한 시점에 파일로부터 로딩이 되며 여
   러 개의 프로그램이 실행되어 메모리가 부족시 리소스 데이터가
   할당된 메모리 블록을 이동(Moveable)기키거나 폐기
   (Discardable)한다.
5. 윈도우 프로그래밍과 친해지자
                GUI

아이콘,커서,다이얼로그등 중복/증가로 파일크기가 커진다.

     리소스를 프로그램과 분리, 필요 시에 Call

       링크 시 소스코드와 리소스가 결합

  리소스는 메모리 부족 시 폐기 가능(DISCARDABLE)
      필요 시EXE나 DLL파일로부터 로딩

 프로그램이 차지하는 메모리가 감소되어 메모리 효율성이
높아지고 소스 코드와 분리되어 작성되므로 프로그램 작성이
              편리하다.
5. 윈도우 프로그래밍과 친해지자
• 하드웨어 운용 방식(장치 독립적인 그래픽 인터페이스)
 – 도스에서는 비디오 메모리나 프린터 포트 등을 직접 제어함으로
   써 특정한 하드웨어 장치에 종속된 프로그램을 작성하였다.
 – 장치 종속적
   • 비디오 카드나 프린터 등과 같은 장치의 종류나 모델에 따라 출력방
     법이 달라진다.
   • 특정 장치에서만 제한적으로 실행되기 때문에 프로그램에서 지원하
     지 않는 장치는 사용할 수 없다.
   • 프로그램에서 필요한 디바이스 드라이버를 제공해야 한다.
 – 장치 독립적
   • 장치의 종류와 모델에 상관없이 일관된 처리 방법으로 하드웨어 장
     치들을 다룰 수 있다.
   • 출력하고자 하는 출력장치의 종류에 상관없이 출력 방법이 동일하
     다.
   • 필요한 디바이스 드라이버를 윈도우 OS가 내장하고 있으면 어떤 하
     드웨어 장치건 상관없이 동일하게 작동되기 때문에 프로그래머는
     중요한 기능에 보다 많은 시간을 투자할 수 있다.
   • 출력 장치에 대한 정보를 포함하는 구조체 디바이스 컨텍스트를 가지
     고 GDI함수를 호출하여 출력 작업을 수행
5. 윈도우 프로그래밍과 친해지자
case WM_PAINT :
      hdc = BeginPaint (hwnd, &ps) ;
     GetClientRect (hwnd, &rect) ;
     DrawText (hdc, "Hello, Windows 95!", -1, &rect,
                     DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
     EndPaint (hwnd, &ps) ;
     return 0 ;

                                            Display.drv
                 GDI32.DLL                  Printer.drv

                                           Keyboard.drv
                                            Mouse.drv
윈도우              USER32.DLL                 Sound.drv
프로그램
                                            System.drv      Timer H/W
                                            Comm.drv        RS-232
                                          Ms-Dos file I/O
               KERNEL32.DLL
                                          Memory Management
5. 윈도우 프로그래밍과 친해지자
커널모듈     KRNL386.EXE   KERNEL32.DLL Windows OS의 핵심
                                    메모리관리,파일 입출력,
                                    프로그램의 로드와 실행 등
                                    운영체제의 기본기능 수행

GDI 모듈     GDI.EXE      GDI32.DLL   화면이나 프린터 같은
                                    장치의 출력을 관장하며
                                    메시지를 관리
                                    (펜,브러시,폰트,비트맵..)

 사용자      USER.EXE     USER32.DLL   윈도우,다이얼로그,메뉴,
인터페이스                               커서,아이콘 등과 같은
  모듈                                인터페이스 객체들을
                                    관리한다.
     2.
Windows
     와
  메시지
1. 첫번째 API프로그램
#include <windows.h>
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int
iCmdShow)
{
    static TCHAR szAppName[] = TEXT ("HelloWin") ;
    HWND             hwnd ;
    MSG              msg ;
    WNDCLASS         wndclass ;

   wndclass.style               = CS_HREDRAW | CS_VREDRAW ;
   wndclass.lpfnWndProc         = WndProc ;
   wndclass.cbClsExtra          =0;
   wndclass.cbWndExtra          =0;
   wndclass.hInstance           = hInstance ;
   wndclass.hIcon               = LoadIcon (NULL, IDI_APPLICATION) ;
   wndclass.hCursor             = LoadCursor (NULL, IDC_ARROW) ;
   wndclass.hbrBackground       = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
   wndclass.lpszMenuName        = NULL ;
   wndclass.lpszClassName       = szAppName ;
   if (!RegisterClass (&wndclass))
   {
           MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName,
                                MB_ICONERROR) ;
           return 0 ;
   }
    hwnd = CreateWindow (szAppName,             // window class name
                         "The Hello Program",   // window caption
                         WS_OVERLAPPEDWINDOW,    // window style
                         CW_USEDEFAULT,         // initial x position
                         CW_USEDEFAULT,         // initial y position
                         CW_USEDEFAULT,         // initial x size
                         CW_USEDEFAULT,         // initial y size
                         NULL,                  // parent window handle
                         NULL,                  // window menu handle
                         hInstance,             // program instance handle
                         NULL) ;                // creation parameters
    ShowWindow (hwnd, iCmdShow) ;
    UpdateWindow (hwnd) ;
    while (GetMessage (&msg, NULL, 0, 0))
    {
               TranslateMessage (&msg) ;
               DispatchMessage (&msg) ;
    }
    return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
         HDC                  hdc;
         PAINTSTRUCT          ps ;
         RECT                 rect ;
         switch (message)
         {
         case WM_CREATE:
                   PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME |SND_ASYNC) ;
                   return 0 ;
         case WM_PAINT:
                   hdc = BeginPaint (hwnd, &ps) ;
                   GetClientRect (hwnd, &rect) ;
                   DrawText (hdc, "Hello, Windows 98!", -1, &rect,DT_SINGLELINE |
                                DT_CENTER | DT_VCENTER) ;
                   EndPaint (hwnd, &ps) ;
                   return 0 ;
         case WM_DESTROY:
                   PostQuitMessage (0) ;
                   return 0 ;
         }
         return DefWindowProc (hwnd, message, wParam, lParam) ;
}
2. 첫 번째 API프로그램 분석
• 헝거리언 표기법
 – MS사의 전설적인 프로그래머 Charles Simonyi를 기리는 뜻으로
   붙여진 이름)
 – char szAddress[50];
       해당 변수의 데이터 타입을 나타내는 접두어 표기


 접두어            데이터 타입   접두어           데이터 타입
  a     Array             i    index
  b     BOOL              l    long int
  ch    Character         lp   long pointer
  cb    Count of bytes    n    integer
  dw    unsigned long    sz    NULL로 끝나는 문자열

  h     Handle            w    unsigned int
2. 첫 번째 API프로그램 분석
• 접두어
 CS    클래스 스타일      CW    윈도우 생성 옵션
 DT    문자열 그리기 옵션   IDI   아이콘에 대한 ID
 IDC   커서에 대한 ID    MB    메시지 상자 옵션
 SND   사운드 옵션       WM    윈도우 메시지
 WS    윈도우 스타일
2. 첫 번째 API프로그램 분석
• 새로운 데이터 형식들
  UINT      부호 없는 정수
 LRESULT    LONG형

HINSTANCE   unsigned int 프로그램 자신의 인스턴스 핸들

  HWND      윈도우 핸들

  HDC       장치 컨텍스트에 대한 핸들

  PSTR      char *


16비트 시절          WPARAM   16bit unsigned int인 WORD형
                 LPARAM   32bit signed long형인 LONG형
32비트 시절          WPARAM   32bit unsigned int인 WORD형
                 LPARAM   32bit signed long형인 LONG형
2. 첫 번째 API프로그램 분석
• 새로운 데이터 형식들
   MSG     메시지 구조체
WNDCLASS   윈도우 클래스 구조체

PAINTSTRUCT Paint구조체

   RECT    Rectangle구조체


• 윈도우 클래스 등록하기
   – 윈도우는 항상 윈도우 클래스를 기반으로 하여 생성된다.
   – 모든 Button윈도우는 동일한 윈도우 클래스를 기반으로 하여 생
     성된다.
   – 윈도우 클래스는 윈도우의 특성들을 정의한다.
   – 응용 프로그램은 윈도우를 생성하기 전에 반드시 RegisterClass
     를 호출하여 윈도우 클래스를 등록해야 한다.
2. 첫 번째 API프로그램 분석
typedef struct
{
        UINT        style;
        WNDPROC     lpfnWndProc;
        int         cbClsExtra;
        int         cbWndExtra;
        HINSTANCE   hInstance;
        HICON       hIcon;
        HCURSOR     hCursor;
        HBRUSH      hbrBackground;
        LPCTSTR     lpszMenuName;
        LPCTSTR     lpszClassName;
} WNDCLASS, * PWNDCLASS,NEAR * NPWNDCLASSA, FAR * LPWNDCLASSA;
   – wndclass.style=CS_HREDRAW|CS_VREDRAW;
      • 수평 또는 수직윈도우가 변경될 때마다 완전히 새로 클래스의 Brush로
        다시 칠하게 된다.
      • winuser.h에 정의되어 있다.
2. 첫 번째 API프로그램 분석
/* Class styles */
#define CS_VREDRAW           0x0001
#define CS_HREDRAW           0x0002
#define CS_DBLCLKS           0x0008
#define CS_OWNDC             0x0020
#define CS_CLASSDC           0x0040
#define CS_PARENTDC          0x0080
#define CS_NOCLOSE           0x0200
#define CS_SAVEBITS          0x0800
#define CS_BYTEALIGNCLIENT   0x1000
#define CS_BYTEALIGNWINDOW   0x2000
#define CS_GLOBALCLASS       0x4000
#define CS_IME               0x00010000

   – wndclass.lpfnWndProc = WndProc ;
      • 윈도우와 연결되는 윈도우 함수를 연결한다.
   – wndclass.cbClsExtra = 0;
   – wndclass.cbWndExtra = 0;
      • 윈도우 클래스에 여유공간을 두거나, 윈도우에 여유공간을 예약하는
        데 사용
2. 첫 번째 API프로그램 분석
 – cbClsExtra
    • 윈도우 클래스에서 사용하고자 하는 여분의 메모리 양
    • 바이트 수로 지정한다.
    • 운영체제는 윈도우 클래스를 등록할 때 이 멤버가 지정하는 만큼의
      메모리를 추가로 할당해 준다
    • SetClassLong, GetClassLong함수로 이 메모리를 사용한다.
 – cbWndExtra
    • 개별 윈도우에서 사용하고자 하는 여분의 메모리 양을 지정한다.
    • 운영체제는 개별 윈도우가 만들어질 때마다 이 멤버가 지정하는 만
      큼의 메모리를 추가로 할당해 준다.
    • GetWindowLong,SetWindowLong함수로 이 메모리를 사용한다.
2. 첫 번째 API프로그램 분석
 – wndclass.hInstance = hInstance
    • 프로그램의 인스턴스 핸들
 – wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION)
    • 이 윈도우 클래스를 기반으로 하여 생성된 모든 윈도우에 대한 아이콘
      을 설정한다.
    • 프로그램이 실행될 때 아이콘은 Windows작업 표시줄에 나타난다.
    • 프로그램에 있는 ICON을 지정하려면 NULL에 hInstance를 넣는다.
 – wndclass.hCursor = LoadCursor(NULL,IDI_APPLICATION)
    • 윈도우에서 사용하는 커서를 지정한다.
 – wndcalss.hbrBackground = GetStocObject(WHITE_BRUSH) ;
    • Window의 배경색을 지정한다.
    • Windows에는 여러 가지의 표준 혹은 ‘stock’브러쉬가 있다.
 – wndclass.hMenu = NULL;
    • 윈도우의 메뉴를 지정한다.
 – wndclass.lpszClassName=szAppName;
    • 클래스는 반드시 이름을 부여 받아야 한다.
 – RegisterClass를 호출하여 윈도우 클래스를 등록
2. 첫 번째 API프로그램 분석
if (!RegisterClass(&wndclass))
{
          MessageBox(NULL,TEXT(“This program requires Windows NT!”,
                    szAppName,MB_ICONERROR);
          return 0;
}
2. 첫 번째 API프로그램 분석
• 윈도우 생성하기
hwnd = CreateWindow (
       szAppName,             //   window class name
       "The Hello Program",   //   window caption
       WS_OVERLAPPEDWINDOW,   //   window style
       CW_USEDEFAULT,         //   initial x position
       CW_USEDEFAULT,         //   initial y position
       CW_USEDEFAULT,         //   initial x size
       CW_USEDEFAULT,         //   initial y size
       NULL,                  //   parent window handle
       NULL,                  //   window menu handle
       hInstance,             //   program instance handle
       NULL);                 //   creation parameters

   – CreateWindow를 호출하여 윈도우 생성
   – CreateWindow시에는 윈도우의 개별적인 특징을 지정한다.
      • 윈도우의 크기, 윈도우의 위치,…
2. 첫 번째 API프로그램 분석
   – WS_OVERLAPPENDWINDOW
      • 제목표시줄,시스템메뉴,두꺼운 윈도우 크기 변경 경계, 제목표시줄
        의 최소화,최대화,닫기 단추
      • (WINUSER.H에서 확인)
#define WS_OVERLAPPEDWINDOW ( WS_OVERLAPPED | WS_CAPTION | \
              WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | \
              WS_MAXIMIZEBOX)
  – 부모 윈도우 핸들
      • 부모 윈도우를 만드는 경우는 NULL
      • 자식 윈도우를 만드는 경우는 부모 윈도우의 핸들값
  – 윈도우 메뉴 핸들
      • 윈도우 클래스에서 지정한 메뉴를 사용하려면 NULL
      • 윈도우 클래스에서 지정한 메뉴를 사용하지 않으려면 메뉴의 핸들을
        지정한다.
  – 프로그램 인스턴스 핸들
      • WinMain의 매개변수로 프로그램에 전달되는 인스턴스의 핸들을 지
        정한다.
  – Return값
2. 첫 번째 API프로그램 분석
 – CreateWindow()후에는 윈도우가 내부적으로 생성되어 있다.
    • 화면에는 보이지 않는 상태
 – 화면에 출력하려면
    • ShowWindow(hwnd,iCmdShow)
        – hwnd : CreateWindow()의 리턴 값.
        – iCmdShow : 초기에 화면에 윈도우가 어떻게 표시되는 지를 나타낸다.
        – 윈도우를 화면에 나타내며 지정된 브러쉬로 윈도우의 Client영역을 칠한다.

 매크로 상수           의미
 SW_HIDE          윈도우를 숨긴다.
 SW_MINIMIZE      윈도우를 최소화시키고 활성화시키지 않는다.
 SW_RESTORE       윈도우를 활성화시킨다.
 SW_SHOW          윈도우를 활성화시켜 보여준다.
 SW_SHOWNORMAL    윈도우를 활성화시켜 보여준다.
2. 첫 번째 API프로그램 분석
• 메시지 루프
 – 메시지 루프는 세 개의 함수 호출로 이루어져 있으며 전체 루프는
   while문으로 싸여져 있어 무한히 반복되는 구조를 가지고 있다.
 – 이벤트가 발생하면 Windows는 이벤트를 메시지로 변환하여 프로
   그램의 메시지 큐에 저장한다.
 – GetMessage( &msg,NULL,0,0)
   • 메시지 큐로부터 메시지를 읽어 온다.
   • 읽어들인 메시지는 첫 번째 인수가 지정하는 MSG 구조체에 저장된다.
   • WM_QUIT 메시지인 경우 0을 Return한다. 그 이외의 메시지이면
     TRUE를 리턴한다.
   • 나머지 세 개의 인수는 읽어 들일 메시지의 범위를 지정한다. 잘 사용
     하지 않는다.
2. 첫 번째 API프로그램 분석
while (GetMessage (&msg, NULL, 0, 0))
{
        TranslateMessage (&msg) ;
        DispatchMessage (&msg) ;
}
typedef struct tagMSG
{
        HWND    hwnd;
        UINT    message;//어떤 종류의 메시지인가를 나타낸다.
        WPARAM wParam; // 전달된 메시지의 부가적인 정보
        LPARAM lParam; //
        DWORD   time;   // 메시지가 메시지 큐에 저장된 시간
        POINT   pt;     //메시지 큐에 저장된 시간의 마우스 좌표
} MSG , * PMSG

typedef struct tagPOINT
{
        LONG            x;
        LONG            y;
} POINT, * PPOINT;
2. 첫 번째 API프로그램 분석
    – TranslateMessage(&msg);
        • 키보드 번역
    – DisplatchMessage(&msg);
        • 메시지 큐에서 꺼낸 메시지를 윈도우 프로시저로 전달한다.

• 윈도우 프로시저
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,
                                LPARAM lParam)

    – 윈도우 프로시저는 항상 RegisterClass를 호출하여 등록한 특정
      한 윈도우 클래스와 연결되어 있다.
    – SendMessage라는 함수를 통해서 자신의 윈도우 프로시저를
      간접적으로 호출한다.
    – Message는 WINUSER.H에 define되어 있다.
    – 윈도우 프로시저가 메시지를 처리할 때에 반드시 윈도우 프로시
      저로부터 0이 반환되어야 한다.
    – 윈도우 프로시저가 처리하지 않는 모든 메시지들은 반드시
      DefWindowProc라는 이름의 Windows함수에 전달되어야 한다.
2. 첫 번째 API프로그램 분석
• WM_PAINT 메시지

  – 윈도우 클라이언트 영역의 일부가 무효화 영역이 되면 발생한다.
  – 윈도우가 다시 그려져야 함을 알린다.
  – CS_HREDRAW,CS_VREDRAW는 윈도우의 크기가 변경될 때
    WM_PAINT를 발생시킨다.
  – WM_PAINT는 항상 hdc=BeginPaint(hwnd,&ps)
  –   EndPaint( hwnd, &ps)를 이용하여 처리한다.
  – BeginPaint는 dc의 handle를 반환한다.
  – BeginPaint호출중에 Windows는 클라이언트 영역의 배경을 윈도우
    클래스에 지정한 브러쉬로 지운다.
  – GetClientRect(hwnd,&rect)는 클라이언트의 크기를 Return한다.

  – DrawText (hdc, "Hello, Windows 98!",-1, &rect,DT_SINGLELINE
    | DT_CENTER | DT_VCENTER) ;
     • 텍스트를 그려준다.
     • 세 번째 인자 –1은 문자열이 NULL로 종료됨을 의미
2. 첫 번째 API프로그램 분석
    – DC란
       • 출력에 필요한 모든 정보를 가지는 데이터 구조체이다.
       • 어떤 폰트를 사용할 것이지, 선의 색상과 굵기, 채움 무늬와 색상, 출력방법 과
         같은 정보를 담고 있다.
       • 모든 출력은 윈도우를 기준으로 하며 이러한 원점에 대한 정보도 DC에 있다.
       • 현재 상황에서 어떤 영역이 출력이 허가된 영역인가를 보고 허가된 영역에만
         출력을 내 보낸다.
       • GDI모듈에 의해 관리된다.

    – DrawText(HDC uDC, LPCTSTR lpString, int nCount, LPRECT lpRect,
      UINT uFormat);
값                설명
DT_LEFT          수평 왼쪽 정렬한다.
DT_RIGHT         수평 오른쪽 정렬한다.
DT_CENTER        수평 중앙 정렬한다.
DT_BOTTOM        사각영역의 바닥에 문자열을 출력한다.
DT_VCENTER       사각영역의 수직 중앙에 문자열을 출력한다.
DT_WORDBREAK     사각영역의 오른쪽 끝에서 자동 개행되도록 한다.
DT_SINGLELINE    한 줄로 출력한다.
DT_NOCLIP        사각영역의 경계를 벗어나도 문자열을 자르지 않고 그대로 출력한다.
2. 첫 번째 API프로그램 분석
    – int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR
      lpCaption, UINT uType)
        • hWnd : 메시지 박스의 오너 윈도우 핸들

값                  설명
MB_ABORTRETRYIGN   Abort, Retry, Ignore 세 개의 버튼이 나타난다.
ORE
MB_OK              OK 버튼 하나만 나타난다.
MB_OKCANCEL        OK, Cancel 두 개의 버튼이 나타난다.
MB_RETRYCANCEL     Retry, Cancel 두 개의 버튼이 나타난다.
MB_YESNO           Yes, No두 개의 버튼이 나타난다.
MB_YESNOCANCEL     Yes, No, Cancel 세 개의 버튼이 나타난다.

값          설명                값          설명
IDABORT    Abort 버튼 클릭       IDOK       OK 버튼 클릭
IDCANCEL   Cancel 버튼 클릭      ID_RETRY   Retry 버튼 클릭
IDIGNORE   Ignore 버튼 클릭      ID_YES     Yes 버튼 클릭
IDNO       No 버튼 클릭
2. 첫 번째 API프로그램 분석
• WM_DESTROY메시지

  – 윈도우가 종료되고 있음을 나타낸다.
  – 사용자가 종료단추를 클릭하거나 시스템 메뉴의 종료 메뉴를 선택
    하면 WM_DESTOROY를 발생시킨다.
  – PostQuitMessage(0);
    • 메시지큐에 WM_QUIT를 삽입한다.
    • GetMessage()는 메시지가 WM_QUIT면 0을 리턴한다.

  – 종료 버튼 클릭시 종료순서
    •   =>   WM_SYSCOMMAND
    •   =>   WM_CLOSE
    •   =>   DestoryWindow()
    •   =>   WM_DESTORY
    •   =>   PostQuitMessage()를 call
    •   =>   메시지큐에 WM_QUIT를 넣는다.
2. 첫 번째 API프로그램 분석
• 큐에 저장되는 메시지와 큐에 저장되지 않는 메시지
 – 큐에 저장되는 메시지
   • 키 스트로크 메시지
        – WM_KEYDOWN,WM_KEYUP,WM_CHAR
   • 마우스 메시지
        – WM_LBUTTONDOWN,WM_RBUTTONDOWN,WM_MOUSEMOVE
   • 타이머 메시지 : WM_TIMER
   • 다시 그리기 메시지 : WM_PAINT
   • 종료 메시지 : WM_QUIT

 – 큐에 저장되지 않는 메시지
   •   특정한 Window함수 호출의 결과이다.
   •   CreateWindow() : WM_CREATE
   •   ShowWindow() : WM_SIZE,WM_SHOWWINDOW
   •   UpdateWindow() : WM_PAINT메시지를 윈도우 프로시저에 보낸다.
   •   WM_COMMAND

 – 메시지 큐와 윈도우 프로시저는 동시에 수행되지 않는다.
 – DispatchMessage()는 윈도우 프로시저가 Window에게 컨트롤을
   넘기기 전까지는 반환되지 않는다.
   3.
문자출력
1. 문자출력
• 클라이언트 영역
 – 전체윈도우에서 제목표시줄이나 윈도우의 경계선,메뉴 바,도구 모
   음,상태 바,스코롤 바를 제외한 영역
 – 프로그램이 자유로이 그릴 수 있는 부분
 – 프로그램은 다른 윈도우와 비디오 디스플레이를 공유해야 한다.


• 그리기와 다시 그리기
 – 윈도우에서는 윈도우의 클라이언트 영역에만 텍스트와 그래픽을
   그릴 수 있다.
 – Windows는 다른 응용 프로그램이 덮어버린 윈도우를 저장하지 않
   는다.
 – 윈도우는 WM_PAINT메시지를 전달하여 윈도우의 클라이언트 영역
   의 일부가 그려질 필요가 있다는 것을 윈도우 프로시저에 알린다.
2. WM_PAINT메시지
 – 다음과 같은 이벤트가 발생할 때 WM_PAINT가 발생한다.
   • 윈도우를 옮기거나 제거했을 때 이전에 감추어졌던 영역이 보이게 될
     때
   • 윈도우의 크기를 조절할 때
   • 클라이언트의 일부를 Scroll했을 때
   • InvalidateRect나 InvalidateRgn을 사용했을 때
   • Windows가 윈도우의 일부가 겹친 대화상자나 메시지 상자를 제거했
     을때
   • 메뉴가 나타났다가 사라질때
   • 풍선 도움말이 나타났을 때

 – 저장 후 복구할 때
   • 마우스 커서가 클라이언트 영역을 지나갈 때
   • 아이콘이 클라이언트 영역을 지나서 드래그될 때
3. 유효 영역과 무효 영역
 – 무효화 영역
    • 만약에 대화상자가 제거되었을 경우에는 대화상자가 있던 영역만 복원
      되면 된다. 이 영역을 무효화영역 또는 업데이트 영역이라고 한다.
    • 무효 영역은 Windows가 작업의 메시지 큐에 WM_PAINT메시지를 보
      내게 된다.

 – 페인트 정보 구조체
    • Windows는 내부적으로 각 윈도우를 위한 ‘페인트 정보 구조체를 가지
      고 있다.
    • 이 구조체는 무효 영역을 둘러싸고 있는 가장 작은 사각형의 좌표를 가
      지고 있다.
 – InvalidateRect
    • InvalidateRect를 호출하여 자기 자신의 클라이언트 영역에서 지정한
      사각형을 무효로 만들 수 있다
 – GetUpdateRect()
    • 무효화 사각형의 좌표를 얻을 수 있다.
 – BeginPaint를 호출한 후 전체 클라이언트 영역이 유효화된다.
 – ValidateRect()
    • 클라이언트 영역에 있는 모든 사각형을 유효화한다.
    • 메시지 큐에 있는 모든 WM_PAINT는 제거된다.
4. GDI
 – 윈도우의 클라이언트 영역에 그리기를 하려면 Windows의 GDI함
   수를 사용해야 한다.
     • TextOut (hdc,x,y,psText,iLength);
     • 화면에 가장 일반적으로 사용하는 출력함수는 이다.
     • 널 종료 문자열을 사용하지 않으므로 문자열의 길이를 인수로 반드시
       입력해 주어야 한다.
     • SetTextAlign(HDC hdc, UINT fMode);
     • 문자열의 정렬 방법을 변경하는 함수이다.

 값             설명
 TA_TOP        지정한 좌표가 상단 좌표가 된다.
 TA_BOTTOM     지정한 좌표가 하단 좌표가 된다.
 TA_CENTER     지정한 좌표가 수평 중앙 좌표가 된다.
 TA_LEFT       지정한 좌표가 수평 왼쪽 좌표가 된다.
 TA_RIGHT      지정한 좌표가 수평 오른쪽 좌표가 된다.
 TA_UPDATECP   지정한 좌표대신 CP를 사용하며 문자열 출력 후에 CP를
               변경한다.
 TA_NOUPDATE   CP를 사용하지 않고 지정한 좌표를 사용하며 CP를 변경
 CP            하지 않는다.
4. GDI
    • 디폴트 정렬 상태는 TA_TOP | TA_LEFT로 되어 있으며 지정한 좌표를
      좌 상단으로 하여 문자열이 출력된다.


 – 장치 컨텍스트
    • 장치 컨텍스트 핸들은 GDI함수들에 대한 허가권이다.
    • 핸들이 있어야 클라이언트 영역에 그래픽을 그릴 수 있다.
    • GDI에 의해 내부적으로 관리되는 데이터 구조체이다.
    • 프로그램이 그리기가 필요할 때 hDC를 먼저 얻어야 한다.
    • 핸들을 얻을 때 Windows는 내부 장치 컨텍스트를 기본 속성값으로 채
      운다.
    • 프로그램이 클라이언트 영역에 그리기를 종료했으면 장치 컨텍스트 핸
      들을 제거 해야 한다.
5. 장치 컨텍스트 핸들 얻기 : 1
 – WM_PAINT시
   • BeginPaint를 이용하여 얻는다.
   • WM_PAINT시에는 꼭 BeginPaint()와 EndPaint()를 해야 한다.

       case WM_PAINT:
                 hdc = BeginPaint (hwnd,&ps);
                 [GDI함수 이용]
                 EndPaint(hwnd,&ps);
                 return 0;

    case WM_PAINT:
         return 0; =>절대 안 된다.

   • 이렇게 하면 클라이언트의 무효화 영역이 유효화가 되지 않으므로
     WM_PAINT를 메시지 큐에 다시 집어 넣는다.
5. 장치 컨텍스트 핸들 얻기 : 1
• 페인트 정보 구조체
  –   BeginPaint를 호출할 때 Window는 이 구조체의 필드를 채운다.
  –   처음 3개만 사용할 수 있다.
  –   나머지는 Windows가 내부 적으로 사용
  –   fErase는 대부분 FALSE(0)이다.
      • FALSE : Windows가 이미 유효하지 않는 영역의 배경을 지웠다는 것
        을 의미

typedef struct tagPAINTSTRUCT
{
        HDC       hdc;
        BOOL fErase;
        RECT rcPaint;
        BOOL fRestore;
        BOOL fIncUpdate;
        BYTE rgbReserved[32];
} PAINTSTRUCT;
5. 장치 컨텍스트 핸들 얻기 : 1
 – Windows는 WNDCLASS구조체의 hbrBackground에서 지정된 브
   러쉬를 사용하여 배경을 지운다.
   • Wndclass.hbrBackground=
          (HBRUSH)GetStockObject(WHITE_BRUSH);

 – InvalidateRect를 호출하여 배경을 지울 것인지를 결정할 수 있다.
   • InvalidateRect(hwnd,NULL,FALSE)
      – 무효한 영역을 지우지 않고 화면에 그대로 남긴다.
   • InvalidateRect(hwnd,NULL,TRUE)
      – 무효한 영역을 지운다.
5. 장치 컨텍스트 핸들 얻기 : 2
• WM_PAINT이외의 Message에서 DC얻기
  – GetDC와 ReleaseDC는 언제나 같은 메시지 핸들러 안에 있어야
    한다.
  – GetDC는 어떤 무효화 영역도 유효화 하지 않는다.
  – 전체 클라이언트 영역을 유효하게 하려면
     • ValidateRect(hwnd,NULL)
  – 전체 윈도우를 무효화 하려면
     • Invalidate(hwnd,NULL);
  – 전체 윈도우를 대상으로 작업을 하려면
     • GetWindowDC

hdc = GetDC(hwnd);
       [GDI 함수 사용]
ReleaseDC(hwnd,hdc);
6. 문자의 크기
Typedef struct tagTEXTMETRIC
{
        LONG tmHeight;
        LONG tmAscent;
        LONG tmDescent;
        LONG tmInternalLeading;
        LONG tmExternalLeading;
        LONG tmAveCharWidth;
        LONG tmMaxCharWidth;
} TEXTMETRIC, * PTEXTMETERIC;

   TEXTMETRIC          tm;
   hdc = GetDC(hwnd);
   GetTextMetrics(hdc,&tm);
   ReleaseDC(hwnd,hdc);
    7. 실습
/*-----------------------------------------------
    SYSMETS.H -- System metrics display structure
    -----------------------------------------------*/
#define NUMLINES ((int) (sizeof sysmetrics / sizeof sysmetrics [0]))
struct
{
      int   iIndex ;
      TCHAR * szLabel ;
      TCHAR * szDesc ;
}
sysmetrics [] =
{
      SM_CXSCREEN,                                      TEXT ("SM_CXSCREEN"),       TEXT ("Screen width in pixels"),
      SM_CYSCREEN,                                      TEXT ("SM_CYSCREEN"),       TEXT ("Screen height in pixels"),
      SM_CXVSCROLL,                                     TEXT ("SM_CXVSCROLL"),      TEXT ("Vertical scroll width"),
      SM_CYHSCROLL,                                     TEXT ("SM_CYHSCROLL"),      TEXT ("Horizontal scroll height"),
      SM_CYCAPTION,                                     TEXT ("SM_CYCAPTION"),      TEXT ("Caption bar height"),
      SM_CXBORDER,                                      TEXT ("SM_CXBORDER"),       TEXT ("Window border width"),
      SM_CYBORDER,                                      TEXT ("SM_CYBORDER"),       TEXT ("Window border height"),
      SM_CXFIXEDFRAME,                                  TEXT ("SM_CXFIXEDFRAME"),   TEXT ("Dialog window frame width"),
      SM_CYFIXEDFRAME,                                  TEXT ("SM_CYFIXEDFRAME"),   TEXT ("Dialog window frame height"),
      SM_CYVTHUMB,                                      TEXT ("SM_CYVTHUMB"),       TEXT ("Vertical scroll thumb height"),
      SM_CXHTHUMB,                                      TEXT ("SM_CXHTHUMB"),       TEXT ("Horizontal scroll thumb width"),
      SM_CXICON,                                        TEXT ("SM_CXICON"),         TEXT ("Icon width"),
      SM_CYICON,                                        TEXT ("SM_CYICON"),         TEXT ("Icon height"),
      SM_CXCURSOR,                                      TEXT ("SM_CXCURSOR"),       TEXT ("Cursor width"),
SM_CYCURSOR,            TEXT ("SM_CYCURSOR"),            TEXT ("Cursor height"),
SM_CYMENU,              TEXT ("SM_CYMENU"),              TEXT ("Menu bar height"),
SM_CXFULLSCREEN,        TEXT ("SM_CXFULLSCREEN"),        TEXT ("Full screen client area width"),
SM_CYFULLSCREEN,        TEXT ("SM_CYFULLSCREEN"),        TEXT ("Full screen client area height"),
SM_CYKANJIWINDOW,       TEXT ("SM_CYKANJIWINDOW"),       TEXT ("Kanji window height"),
SM_MOUSEPRESENT,        TEXT ("SM_MOUSEPRESENT"),        TEXT ("Mouse present flag"),
SM_CYVSCROLL,           TEXT ("SM_CYVSCROLL"),           TEXT ("Vertical scroll arrow height"),
SM_CXHSCROLL,           TEXT ("SM_CXHSCROLL"),           TEXT ("Horizontal scroll arrow width"),
SM_DEBUG,               TEXT ("SM_DEBUG"),               TEXT ("Debug version flag"),
SM_SWAPBUTTON,          TEXT ("SM_SWAPBUTTON"),          TEXT ("Mouse buttons swapped flag"),
SM_CXMIN,               TEXT ("SM_CXMIN"),               TEXT ("Minimum window width"),
SM_CYMIN,               TEXT ("SM_CYMIN"),               TEXT ("Minimum window height"),
SM_CXSIZE,              TEXT ("SM_CXSIZE"),              TEXT ("Min/Max/Close button width"),
SM_CYSIZE,              TEXT ("SM_CYSIZE"),              TEXT ("Min/Max/Close button height"),
SM_CXSIZEFRAME,         TEXT ("SM_CXSIZEFRAME"),         TEXT ("Window sizing frame width"),
SM_CYSIZEFRAME,         TEXT ("SM_CYSIZEFRAME"),         TEXT ("Window sizing frame height"),
SM_CXMINTRACK,          TEXT ("SM_CXMINTRACK"),          TEXT ("Minimum window tracking width"),
SM_CYMINTRACK,          TEXT ("SM_CYMINTRACK"),          TEXT ("Minimum window tracking height"),
SM_CXDOUBLECLK,         TEXT ("SM_CXDOUBLECLK"),         TEXT ("Double click x tolerance"),
SM_CYDOUBLECLK,         TEXT ("SM_CYDOUBLECLK"),         TEXT ("Double click y tolerance"),
SM_CXICONSPACING,       TEXT ("SM_CXICONSPACING"),       TEXT ("Horizontal icon spacing"),
SM_CYICONSPACING,       TEXT ("SM_CYICONSPACING"),       TEXT ("Vertical icon spacing"),
SM_MENUDROPALIGNMENT,   TEXT ("SM_MENUDROPALIGNMENT"),   TEXT ("Left or right menu drop"),
SM_PENWINDOWS,          TEXT ("SM_PENWINDOWS"),          TEXT ("Pen extensions installed"),
SM_DBCSENABLED,         TEXT ("SM_DBCSENABLED"),         TEXT ("Double-Byte Char Set enabled"),
SM_CMOUSEBUTTONS,       TEXT ("SM_CMOUSEBUTTONS"),       TEXT ("Number of mouse buttons"),
SM_SECURE,         TEXT ("SM_SECURE"),         TEXT ("Security present flag"),
SM_CXEDGE,         TEXT ("SM_CXEDGE"),         TEXT ("3-D border width"),
SM_CYEDGE,         TEXT ("SM_CYEDGE"),         TEXT ("3-D border height"),
SM_CXMINSPACING,   TEXT ("SM_CXMINSPACING"),   TEXT ("Minimized window spacing width"),
SM_CYMINSPACING,   TEXT ("SM_CYMINSPACING"),   TEXT ("Minimized window spacing height"),
SM_CXSMICON,       TEXT ("SM_CXSMICON"),       TEXT ("Small icon width"),
SM_CYSMICON,       TEXT ("SM_CYSMICON"),       TEXT ("Small icon height"),
SM_CYSMCAPTION,    TEXT ("SM_CYSMCAPTION"),    TEXT ("Small caption height"),
SM_CXSMSIZE,       TEXT ("SM_CXSMSIZE"),       TEXT ("Small caption button width"),
SM_CYSMSIZE,       TEXT ("SM_CYSMSIZE"),       TEXT ("Small caption button height"),
SM_CXMENUSIZE,     TEXT ("SM_CXMENUSIZE"),     TEXT ("Menu bar button width"),
SM_CYMENUSIZE,     TEXT ("SM_CYMENUSIZE"),     TEXT ("Menu bar button height"),
SM_ARRANGE,        TEXT ("SM_ARRANGE"),        TEXT ("How minimized windows arranged"),
SM_CXMINIMIZED,    TEXT ("SM_CXMINIMIZED"),    TEXT ("Minimized window width"),
SM_CYMINIMIZED,    TEXT ("SM_CYMINIMIZED"),    TEXT ("Minimized window height"),
SM_CXMAXTRACK,     TEXT ("SM_CXMAXTRACK"),     TEXT ("Maximum draggable width"),
SM_CYMAXTRACK,     TEXT ("SM_CYMAXTRACK"),     TEXT ("Maximum draggable height"),
SM_CXMAXIMIZED,    TEXT ("SM_CXMAXIMIZED"),    TEXT ("Width of maximized window"),
SM_CYMAXIMIZED,    TEXT ("SM_CYMAXIMIZED"),    TEXT ("Height of maximized window"),
SM_NETWORK,        TEXT ("SM_NETWORK"),        TEXT ("Network present flag"),
SM_CLEANBOOT,      TEXT ("SM_CLEANBOOT"),      TEXT ("How system was booted"),
SM_CXDRAG,         TEXT ("SM_CXDRAG"),         TEXT ("Avoid drag x tolerance"),
SM_CYDRAG,         TEXT ("SM_CYDRAG"),         TEXT ("Avoid drag y tolerance"),
SM_SHOWSOUNDS,     TEXT ("SM_SHOWSOUNDS"),     TEXT ("Present sounds visually"),
SM_CXMENUCHECK,    TEXT ("SM_CXMENUCHECK"),    TEXT ("Menu check-mark width"),
/*----------------------------------------------------
   SYSMETS1.C -- System Metrics Display Program No. 1
            (c) Charles Petzold, 1998
  ----------------------------------------------------*/
#define WINVER 0x0500
#include <windows.h>
#include "sysmets.h"
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
               static TCHAR szAppName[] = TEXT ("SysMets1") ;
               HWND                        hwnd;
               MSG                                      msg;
               WNDCLASS        wndclass;

             wndclass.style                               = CS_HREDRAW | CS_VREDRAW ;
             wndclass.lpfnWndProc           = WndProc ;
             wndclass.cbClsExtra            =0;
             wndclass.cbWndExtra            =0;
             wndclass.hInstance             = hInstance ;
             wndclass.hIcon                 = LoadIcon (NULL, IDI_APPLICATION) ;
             wndclass.hCursor               = LoadCursor (NULL, IDC_ARROW) ;
             wndclass.hbrBackground         = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
             wndclass.lpszMenuName          = NULL ;
             wndclass.lpszClassName         = szAppName ;
             if (!RegisterClass (&wndclass))
             {
                              MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;
                              return 0 ;
             }
             hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics No. 1"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
                              CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ;
             ShowWindow (hwnd, iCmdShow) ;
             UpdateWindow (hwnd) ;
             while (GetMessage (&msg, NULL, 0, 0))
             {
                              TranslateMessage (&msg) ;
                              DispatchMessage (&msg) ;
             }
             return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
              static int     cxChar, cxCaps, cyChar ;
              HDC            hdc ;
              int            i;
              PAINTSTRUCT ps ;
              TCHAR          szBuffer [10] ;
              TEXTMETRIC tm;
              switch (message)
              {
              case WM_CREATE:
                             hdc = GetDC (hwnd) ;
                             GetTextMetrics (hdc, &tm) ;
                             cxChar = tm.tmAveCharWidth ;
                             cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;
                             cyChar = tm.tmHeight + tm.tmExternalLeading ;
                             ReleaseDC (hwnd, hdc) ;
                             return 0 ;
              case WM_PAINT :
                             hdc = BeginPaint (hwnd, &ps) ;
                             for (i = 0 ; i < NUMLINES ; i++)
                             {
                                               TextOut (hdc, 0, cyChar * i, sysmetrics[i].szLabel,lstrlen (sysmetrics[i].szLabel)) ;
                                               TextOut (hdc, 22 * cxCaps, cyChar * i, sysmetrics[i].szDesc, lstrlen (sysmetrics[i].szDesc)) ;
                                               SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;
                                               TextOut (hdc, 22 * cxCaps + 40 * cxChar, cyChar * i, szBuffer, wsprintf (szBuffer, TEXT
("%5d"),GetSystemMetrics (sysmetrics[i].iIndex))) ;
                                               SetTextAlign (hdc, TA_LEFT | TA_TOP) ;
                             }
                             EndPaint (hwnd, &ps) ;
                             return 0 ;
              case WM_DESTROY :
                             PostQuitMessage (0) ;
                             return 0 ;
              }
              return DefWindowProc (hwnd, message, wParam, lParam) ;
}
       4.
기본적인 그리기
1. GDI 철학
 – 비디오 출력과 프린터에 그래픽을 책임지고 있는 Windows의 하
   위 시스템은 그래픽 디바이스 인터페이스(GDI)이다.
 – Windows 98과 Microsoft NT에서의 그래픽은 주로 동적 링크 라
   이브러리 GDI32.DLL로부터 얻어진 함수들에 의해 처리된다.
 – GDI의 목적 중의 하나는 장치에 구애 받지 않고 그래픽을 지원하
   는 것이다.
 – Windows는 디폴트로 픽셀을 기반으로 하는 좌표계를 사용
 – 가로축과 세로축이 0에서 32,767까지 가능한 가상 좌표계를 사
   용
2. GDI함수 호출
 – 장치 컨텍스트를 얻고 해제하는 함수들
   •   BeginPaint
   •   EndPaint
   •   GetDC
   •   ReleaseDC
 – 장치 컨텍스트에 대한 정보를 획득하는 함수들
   • GetTextMetrics
 – 그리는 함수들
   • TextOut
 – 장치 컨텍스트의 속성을 설정하고 얻어내는 함수들
   • SetTextColor
   • SetTextAlign
 – GDI개체를 다루는 함수들
   • CreatePen
   • CreatePenIndirect
3. 장치 컨텍스트 핸들 얻기
 – WM_PAINT

    hdc = Beginpaint(hwnd,&ps)
    EndPaint(hwnd,&ps);

 – WM_PAINT가 아닌 다른 메시지에서
   hdc = GetDC(hwnd)
   ReleaseDC(hwnd,hdc);

 – 전체 윈도우에 대한 hDC얻기
   hdc = GetWindowDC(hwnd)
   ReleaseDC(hwnd,hdc);
 – 화면전체에 출력을 하려면
   hdc = CreateDC (pszDriver,pszDevice,pszOutput,pData);
                   [다른 명령들]
   DeleteDC(hdc);
3. 장치 컨텍스트 핸들 얻기
 hdc=CreateDC(“DISPLAY”,NULL,NULL,NULL);

 Ex)
   hdc=CreateDC(TEXT("DISPLAY"),NULL,NULL,NULL);
   TextOut(hdc,0,0,TEXT("여기는 처음"),12);
   DeleteDC(hdc);



 hdcMem=CreateCompatibleDC(hdc);
 [다른 명령들]
 DeleteDC(hdc);
4. 점과 선 그리기
 – 픽셀 정하기
     • SetPixel(hdc,x,y,crColor);
          – crColor는 COLORREF형식으로 색상을 지정
     • crColor = GetPixel(hdc,x,y)
          – 지정된 좌표의 픽셀 색상을 반환한다.
 – 직선
     •   LineTo : 직선을 그린다.
     •   Polyline와 PolylineTo : 연결된 직선을 그린다.
     •   PolyPolyline : 여러 개의 Polyline을 그린다.
     •   Arc : 호를 그린다.
     •   PolyBezier와 PolyBezierTo : 베지어 스플라인을 그린다.
 –   Rectangle : 사각형을 그린다.
 –   Ellipse : 타원을 그린다.
 –   RoundRect : 모서리가 둥근 사각형을 그린다.
 –   Pie : 파이모양을 그린다.
 –   Chord : 현 형태의 다원 부분을 그린다.
5. 직선 그리기
 – 직선을 그리려면
   • MoveToEx(hdc,xBeg,yBeg,NULL);
       – MoveToEx는 펜의 위치는 옮긴다.
       – MoveToEx의 마지막 인자는 POINT 구조체에 대한 포인터이다.
       – 함수로부터 반환 시에 POINT구조체의 x,y 필드는 이전의 위치를 체운
         다.
       – Windows 98에서의 좌표값이 32bit이지만 오직 하위 16비트만 사용한
         다.
       – Windows NT는 32비트를 모두 사용한다.
   • LineTo(hdc,xEnd,yEnd);
       – LineTo함수는 현재의 위치로 부터 xEnd,yEnd까지 선을 그린다.
   • GetCurrentPositionEx(hdc,&pt);
       – 현재의 위치를 알아 내려면
       – pt는 POINT구조체이다.
5. 직선 그리기
GetClientRect(hwnd,&rect);
for (x = 0; x < rect.right; x+=100)
{
          MoveToEx (hdc,x,0,NULL);
          LineTo(hdc,x,rect.bottom);
}
for (y = 0; y < rect.bottom; y+=100)
{
          MoveToEx (hdc,0,y,NULL);
          LineTo(hdc,rect.right,y);
}

POINT apt[5] = {100,100,200,100,200,200,100,200,100,100};
MoveToEx(hdc,apt[0].x,apt[0].y);
for (I = 1; I < 5;I++)
{
          LineTo(hdc,apt[I].x,apt[I].y);
}

    Polyline(hdc,apt,5);
    MoveToEx(hdc,apt[0].x,apt[0].y,NULL);
    PolylineTo(hdc,apt+1,4);
5. 경계 상자 함수
 – Rectangle(hdc,xLeft,yTop,xRight,yBottom);
    • 채워진 사각형을 그린다.
 – Ellipse(hdc,xLeft,yTop,xRight,yBottom);
    • 채워진 타원을 그린다.
 – RoundRect(hdc,xLeft,yTop,xRight,yBottom,xCornerEllipse,yC
   ornerEllipse);
    • 모서리가 둥근 채워진 사각형을 그린다.
 – Arc(hdc,xLeft,yTop,xRight,yBottom,xStart,yStart,xEnd,yEnd);
    • 호를 그린다.
 – Chord(hdc,xLeft,yTop,xRight,yBottom,xStart,yStart,xEnd,yEn
   d);
    • 현을 그린다.
 – Pie(hdc,xLeft,yTop,xRight,yBottom,xStart,yStart,xEnd,yEnd);
    • 파이 모양의 도형을 그린다.
5. 경계 상자 함수
   case WM_LBUTTONDOWN:
           hdc=GetDC(hwnd);
           LineTo(hdc,LOWORD(lParam),HIWORD(lParam));
           m_OldPT.x = LOWORD(lParam);
           m_OldPT.y = HIWORD(lParam);
           ReleaseDC(hwnd,hdc);
           return 0;
   case WM_MOUSEMOVE:
           if (wParam & MK_LBUTTON)
           {
                     hdc=GetDC(hwnd);
                     SelectObject(hdc,GetStockObject(WHITE_PEN));
                     LineTo(hdc,m_OldPT.x,m_OldPT.y);

                        MoveToEx(hdc,0,0,NULL);
                        SelectObject(hdc,GetStockObject(BLACK_PEN));
                        LineTo(hdc,LOWORD(lParam),HIWORD(lParam));

                        m_OldPT.x = LOWORD(lParam);
                        m_OldPT.y = HIWORD(lParam);
                        ReleaseDC(hwnd,hdc);
            }
            return 0;
6. 스톡 펜 사용하기
 – Windows가 기본적으로 제공하는 스톡 펜
   • BLACK_PEN,WHITE_PEN,NULL_PEN
 – 디폴트 장치 컨텍스트 펜
   • BLACK_PEN
 – PEN 정의
   • HPEN hPen;
 – 윈도우 스톡에 있는 Pen가져오기
   • hPen = GetStockObject(WHITE_PEN);
 – 장치 컨텍스트에 지정하려면
   • SelectObject(hdc,hPen)
   • SelectObject(hdc,GetStockObject(WHITE_PEN));
7. 펜의 생성과 선택, 그리고 삭제
 – 펜의 사용
    • 펜은 CreatePen과 CreatePenIndirect를 이용하여 펜을 생성한다.
    • SelectObject을 이용하여 만들어진 이 펜을 사용할 수 있다.
    • 새로운 펜으로 그린다.
         – 오직 하나의 펜만이 장치 컨텍스트에서 선택될 수 있다.
    • SelectObject를 이용하여 이전의 펜으로 되돌린다.
    • DeleteObject를 이용하여 만들어진 펜을 삭제한다
 – GDI객체 사용 규칙
    • 마지막에 항상 생성한 GDI객체를 삭제해야 한다.
    • 유효한 장치 컨텍스트에서 선택되어 있는 GDI객체를 삭제하지 않는다.
    • 스톡 객체는 삭제하지 않는다.
 – hPen = CreatePen(iPenStyle,iWidth,crColor);
    • iPenStyle :PS_SOLID,PS_DASH,PS_DOT,PS_DASHDOT,PS_DASH
      DOTDOT,PS_NULL,PS_INSIDEFRAMER
    • iWidth : 펜의 두께 , crColor : COLORREF의 펜의 Color
 – CreatePenIndirect
    •   먼저 LOGPEN형식의 구조체를 정의한다.
    •   LOGPEN logpen;
    •   ( lopnStyle => pen의 스타일, lopnWidht => pen의 두께,
    •     lopnColor => pen의 칼라 )
    •   hPen = CreatePenIndirect(&logpen);
7. 펜의 생성과 선택, 그리고 삭제
-프로그램에서 3개의 Pen을 사용하면
 HPEN    hPen1,hPen2,hPen3;
 hPen1   = CreatePen(PS_SOLID,1,0);
 hPen2   = CreatePen(PS_SOLID,1,RGB(255,0,0));
 hPen3   = CreatePen (PS_DOT,0,0);

 SelectObject(hdc,hPen1);
     –[선그리기]
 SelectObject(hdc,hPen2);
     –[선그리기]
 SelectObject(hdc,hPen3);
     –[선그리기]

 DeleteObject (hPen1);
 DeleteObject (hPen2);
 DeleteObject (hPen3);
8. 틈새 채우기
 – 도트 펜과 대쉬 펜 사이의 틈새는 어떻게 처리할까?
   • 틈새의 색은 Windows디폴트 배경색인 흰색으로 틈을 칠한다.
     (OPAQUE)
 – 틈새의 색을 바꾸려면
   • SetBkColor(hdc,crColor);
 – 틈새를 칠하지 않게 할 수도 있다
   • 배경모드를 TRANSPARENT로 지정한다.
   • SetBkMode(hdc,TRANSPARENT);
9. 그리기모드
 – 디스플레이에 그려진 선의 외형은 장치 컨텍스트에서 정의된 그
   리기 모드에 영향을 받는다.
   • 펜을 사용하여 선을 그릴 때 실제로는 펜의 픽셀과 목표가 되는 디
     스플레이 픽셀의 bitwise 2진 연산을 수행한다.
   • 픽셀을 가지고 2진 연산을 수행하는 것을 “래스터 연산” 또는 “ROP”
     라고 한다.
 – R2_COPYPEN
   • 디폴트 그리기 모드
   • 단순히 펜의 픽셀을 목표에 복사하는 것을 나타낸다.
 – R2_BLACK
   • 항상 검은 선을 그린다.
 – R2_NOTMERGEPEN
   • 펜과 배경이 검정색일 때 선은 흰색으로 그린다.
 – R2_NOT
   • 펜의 색상과는 상관없이 항상 목표 색상을 반전하여 선의 색을 결정
     한다.
 – 그리기 모드 지정
   • SetROP2(hdc,iDrawMode);
 – 그리기 모드를 읽어 온다.
   • iDrawMode = GetROP2(hdc)
9. 그리기모드



   원본         그림     COPY   OR      AND    XOR

그리기 모드          설명
R2_BLACK        항상 검정색이다.
R2_WHITE        항상 흰색이다.
R2_NOP          아무런 그리기도 하지 않는다.
R2_NOT          원래의 그림을 반전시킨다.
R2_COPYPEN      원래의 그림을 덮어버리고 새 그림을 그린다.
R2_NOTCOPYPEN   새 그림을 반전시켜 그린다.
R2_MERGEPEN     OR연산으로 두 그림을 합친다.
R2_MASKPEN      AND연산으로 겹치는 부분만 그린다.
R2_XORPEN       XOR연산으로 겹치는 부분만 반전 시킨다.
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            HDC                            hdc;
            static int xPos, yPos, xOldPos, yOldPos;
            static bool bNowDraw = FALSE;
            switch (message)
            {
            case WM_LBUTTONDOWN:
                             xPos = LOWORD(lParam);
                             yPos = HIWORD(lParam);
                             xOldPos = xPos;
                             yOldPos = yPos;
                             bNowDraw = true;
                             return 0;
            case WM_LBUTTONUP:
                             bNowDraw = false;
                             hdc = GetDC(hwnd);
                             MoveToEx(hdc,xPos,yPos,NULL);
                             LineTo(hdc,xOldPos,yOldPos);
                             ReleaseDC(hwnd,hdc);
                             return 0;
            case WM_MOUSEMOVE:
                             if (bNowDraw)
                             {
                                           hdc = GetDC(hwnd);
                                           SetROP2(hdc,R2_NOT);
                                           MoveToEx(hdc,xPos,yPos,NULL);
                                           LineTo(hdc,xOldPos,yOldPos);
                                           int xNewPos = LOWORD(lParam);
                                           int yNewPos = HIWORD(lParam);
                                           MoveToEx(hdc,xPos,yPos,NULL);
                                           LineTo(hdc,xNewPos,yNewPos);
                                           xOldPos = xNewPos;
                                           yOldPos = yNewPos;
                                           ReleaseDC(hwnd,hdc);
                             }
                             return 0;
            case WM_DESTROY:
                             PostQuitMessage (0) ;
                             return 0 ;
            }
            return DefWindowProc (hwnd, message, wParam, lParam) ;
}
10. 채워진 영역 그리기
 –   Rectangle : 직각 모서리를 가지는 사각형
 –   Ellipse : 타원
 –   RoundRect :둥근 모서리를 가지는 사각형
 –   Chord : 종료점은 현에 의해 연결되는 타원 외곽의 호
 –   Pie : 타원 외곽선으로 정의되는 Pie
 –   Polygon : 다변체 그림
 –   PolyPolygon : 다중 다변체 그림
 –   현재 장치 컨텍스트에서 선택된 브러쉬로 채워진다.
     • 디폴트 : WHITE_BURSH
     • 6개의 스톡 브러쉬 :
        – WHITE_BRUSH , LTGRAY_BRUSH,GRAY_BRUSH,
          DKGRAY_BRUSH,BLACK_BRUSH,NULL_BRUSH
10. 채워진 영역 그리기
  HBRUSH hBrush,hOldBrush;
  hBrush = GetStockObject(GRAY_BRUSH);
  hOldBrush = SelectObject(hdc,hBrush);
  [도형 그리기]
  SelectObject(hdc, hOldBrush);

  경계가 없는 그림을 그리려면
  SelectObject(hdc,GetStockObject(NULL_PEN));

  내부를 채우지 않고 그림의 외곽선 그리려면
  SelectObject(hdc,GetStockObject(NULL_BRUSH));
11. 내부를 브러쉬로 채우기
  – 단색의 브러쉬를 만든다.
     • hBrush = CreateSolidBrush(crColor);
  – 수평,수직,혹은 대각선으로 구성되는 hatch표시를 이용하여 브
    러쉬를 생성
     • hBrush = CreateHatchBrush(iHatchStyle,crColor);
           – PS_HORIZONTAL,PS_BDIAGONAL,PS_VERTICAL,PS_CROSS,
           – PS_FDIAGONAL,PS_DIAGCROSS


       값                                     설명
HS_BDIAGONAL         좌 하향 줄 무늬
HS_CROSS             바둑판 모양
HS_DIAGCROSS         좌 하향 및 우 하향 줄무늬
HS_FDIAGONAL         우 하향 줄무늬
HS_HORIZONTAL        수평선
HS_VERTICAL          수직선
11. 내부를 브러쉬로 채우기
 – Bitmap으로 도형을 채우려면
    • hBitMap =
      LoadBitmap(hinst,MAKEINTRESOURCE(IDB_BITMAP1));
    • hNewBrush = CreatePatternBrush(hBitMap);
 – FillRect(hdc, &rect, hBrush)
    • 지정된 브러쉬로 사각형을 채운다.
 – FrameRect(hdc, &rect, hBrush);
    • 사각형의 Frame을 그리지만 채우지는 않는다.
 – InvertRect(hdc,&rect);
    • 사각형 내의 모든 픽셀들을 반전하여 1은 0으로 0은 1로 만든다.
 – Rect구조체를 채운다.
    • SetRect (&rect,xLeft,yTop,xRight,yBottom);
 – 사각형 내에 포인트가 있는지를 결정한다.
    • bInRect = PtInRect(&rect,point);
12. 랜덤 사각형
    – PeekMessage(&msg,NULL,0,0,PM_REMOVE);
           • 처음 4개의 매개변수는 GetMessage와 동일하다.
           • PM_REMOVE는 메시지를 읽은 후 제거한다.
           • PM_NOREMOVE는 메시지를 읽은 후 제거하지 않는다.
    – PeekMessage()의 Return 값
           • 메시지를 읽었는지 읽지 못 했는지를 Return한다.
/*------------------------------------------
  RANDRECT.C -- Displays Random Rectangles
           (c) Charles Petzold, 1998
 ------------------------------------------*/
#include <windows.h>
#include <stdlib.h>       // for the rand function
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
void DrawRectangle (HWND) ;

int cxClient, cyClient ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT ("RandRect") ;
    HWND          hwnd ;
    MSG         msg ;
    WNDCLASS        wndclass ;

   wndclass.style     = CS_HREDRAW | CS_VREDRAW ;
   wndclass.lpfnWndProc = WndProc ;
   wndclass.cbClsExtra = 0 ;
   wndclass.cbWndExtra = 0 ;
   wndclass.hInstance   = hInstance ;
   wndclass.hIcon      = LoadIcon (NULL, IDI_APPLICATION) ;
   wndclass.hCursor     = LoadCursor (NULL, IDC_ARROW) ;
   wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
   wndclass.lpszMenuName = NULL ;
   wndclass.lpszClassName = szAppName ;
              if (!RegisterClass (&wndclass))
              {
                               MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;
                               return 0 ;
              }

              hwnd = CreateWindow (szAppName, TEXT ("Random Rectangles"),WS_OVERLAPPEDWINDOW,
                           CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;

              ShowWindow (hwnd, iCmdShow) ;
              UpdateWindow (hwnd) ;

              while (TRUE)
              {
                             if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
                             {
                                           if (msg.message == WM_QUIT)
                                                         break ;
                                           TranslateMessage (&msg) ;
                                           DispatchMessage (&msg) ;
                             }
                             else
                                           DrawRectangle (hwnd) ;
              }
              return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
   switch (iMsg)
   {
   case WM_SIZE:
       cxClient = LOWORD (lParam) ;
       cyClient = HIWORD (lParam) ;
       return 0 ;

    case WM_DESTROY:
        PostQuitMessage (0) ;
        return 0 ;
    }
    return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}
12. 랜덤 사각형
 – WM_SIZE 메시지
    • 윈도우의 크기가 변경될 때 보내진다.
    • lParam에는 하위 워드에 윈도우의 폭, 상위 워드에 윈도우의 높이
    • wParam에는 메시지가 발생한 이유를 나타내는 플래그 전달

플래그              설명
SIZE_MAXHIDE     다른 윈도우가 최대화되어 이 윈도우가 가려졌다.
SIZE_MAXIMIZED   최대화되었다.
SIZE_MAXSHOW     다른 윈도우가 원래 크기로 복구되어 이 윈도우가 나
                 타났다.
SIZE_MINIMIZED   최소화되었다.
SIZE_RESTORED    크기가 변경되었다.
13. 영역을 생성하고 색칠하기
 –   영역은    사각형,다각형,타원의 조합으로 이루어진다.
 –   영역을    사용하여 그리거나 잘라내기를 할 수 있다.
 –   영역도    GDI객체이다.
 –   생성된    모든 영역은 DeleteObject을 통하여 삭제한다.
 –   영역에    대한 Handle은 HRGN을 반환한다.

 – 영역 생성
     •   hRgn   =   CreateRectRgn(xLeft,yTop,xRight,yBottom);
     •   hRgn   =   CreateRectRgnIndirect(&rect);
     •   hRgn   =   CreateEllipticRgn(xLeft, yTop,xRight,yBottom);
     •   hRgn   =   CreateEllipticRgnIndirect(&rect);
          – CreateRoundRectRgn은 둥근 모양의 사각형 영역을 생성

 – 다각형영역을 생성
     • hRgn=CreatePolygonRgn(&point,iCount,iPolyFillMode);
          – point : POINT형 구조체
          – iCount : 포인트의 개수
          – iPolyFillMode : ALTERNATE또는 WINDING
13. 영역을 생성하고 색칠하기
 – iRgnType =
   CombineRgn(hDestRgn,hSrcRgn1,hSrcRgn2,iCombine);
    • 2개의 원본 영역을 조합하여 목표 핸들이 조합된 영역을 나타내도
      록 한다.
    • iCombine 매개변수는 hSrcRgn1과 hSrcRgn2의 영역이 조합되는
      방식이다.
       –   RGN_AND 두개의 소스 영역의 교차영역
       –   RGN_OR           두개의 소스 영역의 전영역
       –   RGN_XOR 두개의 소스 영역에서 교차 부분을 뺀 영역
       –   RGN_DIFF hSrcRgn2에 속하지 않는 hSrcRgn1영역
       –   RGN_COPY hSrcRgn1전부 (hSrcRgn2무시)
    • Return값
       –   빈 영역이면 NULLREGION;
       –   간단한 사각형,원,다각형이면 SIMPLEREGION
       –   사각형,타원,혹은 다각형들의 조합이면COMPLEXREGION
       –   ERROR면 ERROR
14. 맵핑모드
 – 맵핑모드란 주어진 좌표가 화면상의 실제 어디에 해당하는지를
   결정하는 방법을 말한다.
 – 윈도우에서 사용하는 좌표는 논리 좌표와 물리 좌표 두 가지가
   있다.
    • 논리 좌표 : 윈도우 내부에서 사용되는 좌표를 말한다.
       – TextOut,DrawText등에서 사용하는 좌표를 말한다.
       – DC핸들을 인수로 받아들이는 모든 함수는 논리 좌표이다.
    • 물리 좌표 : 실제 화면에 출력되는 좌표이며 픽셀 단위를 사용한다.
       – 물리좌표 100,100은 그 위치가 정해져 있다.
    • 논리 좌표의 (20,20)은 물리 좌표의 (20,20)일 수도 있고 다른 곳 일
      수도 있다.
    • 윈도우가 사용하는 디폴트 맵핑모드는 물리좌표와 논리좌표가 일치
      한다.


 – int SetMapMode(HDC hdc, int iMapMode);
 – int GetMapMode(HDC hdc);
14. 맵핑모드
맵핑모드           단위          X축 증가   Y축 증가
MM_TEXT        픽셀          오른쪽     아래쪽
MM_LOMETRIC    0.1 mm      오른쪽     위쪽
MM_HIMETRIC    0.01 mm     오른쪽     위쪽
MM_LOENGLISH   0.01인치      오른쪽     위쪽
MM_HIENGLISH   0.001인치     오른쪽     위쪽
MM_TWIPS       1/1440 인치   오른쪽     위쪽
MM_ISOTROPIC   가변          가변      가변
MM_ANISOTROPIC 가변          가변      가변
15. 윈도우와 뷰 포트
 – 윈도우는 논리 좌표가 사용되는 표면을 말한다.
    • 그래픽 출력 함수는 윈도우에 그래픽을 출력한다.
 – 뷰 포트는 물리 좌표가 사용되는 영역을 말한다.
    • 실제로 사용자의 눈에 보이는 좌표 영역이다.

 – SetViewportOrgEx(HDC hdc, int X, int Y, LPPOINT lpPoint);
    • 윈도우의 원점을 이동시킨다.
 – SetWindowOrgEx(HDC hdc, int X, int Y, LPPOINT lpPoint);
    • 뷰 포트의 원점을 이동시킨다.
16. 가변 비율
 – 윈도우 확장을 조정할 수 있는 맵핑모드에는 MM_ISOTROPIC과
   MM_ANISOTROPIC 두 가지가 있다.

 – BOOL SetWindowExtEx(HDC hdc, int nXExtent, int nYExtent,
   LPSIZE lpSize);
    • 논리적인 좌표 범위를 지정한다.
 – BOOL SetViewportExtEx(HDC hdc, int nXExtent, int nYExtent,
   LPSIZE lpSize);
    • 물리적인 좌표 범위를 지정한다.
  5.
키보드
1. 키보드 무시하기
 – Windows는 많은 키보드 함수들을 자체적으로 처리한다.
   • 시스템 함수에 관계되는 키스트로크는 보통 무시할 수 있다. (보통
     Alt키를 포함한다.)
   • 프로그램의 메뉴를 불러내는 키스트로크는 윈도우프로시저에 오지
     만 보통 DefWindowProc에 전달되어 디폴트 처리된다.
 – 특정한 키보드 이벤트를 수신하게 되는 윈도우는 입력 포커스를
   가지고 있는 윈도우이다.
 – 윈도우 프로시저는 WM_SETFOCUS와 WM_KILLFOCUS메시지
   를 가로채어 자신의 윈도우가 언제 입력 포커스를 가지게 되는
   지를 알 수 있다.
 – 키보드 메시지는 메시지 큐에 저장된다.
   • 동기화 때문에.
2. 키스트로크 메시지
 – 키를 누르면 Windows는 WM_KEYDOWN이나
   WM_SYSKEYDOWN을 윈도우의 메시지 큐에 전달한다.
 – 키에서 손을 때면 Windows는 WM_KEYUP이나 WM_SYSKEYUP
   메시지를 메시지 큐에 전달한다.
 – WM_SYSKEYUP과 WM_SYSKEYDOWN은 보통 ALT키와 같이
   눌려진 키에 의해 생성된다.
 – 보통 WM_SYSKEYUP과 WM_SYSKEYDOWN메시지를 무시하고
   DefWindowProc에 전달해 준다.
   •   case WM_SYSKEYDOWN:
   •   case WM_SYSKEYUP:
   •   case WM_SYSCHAR:
   •   return 0; -> 모든 Alt키 동작을 무시하게 한다.
 – 가상 키 코드는 WM_KEYDOWN,WM_KEYUP,
   WM_SYSKEYDOWN ,그리고 WM_SYSKEYUP메시지의 wParam
   매개 변수에 저장된다.
 – 가상 키 코드는 VK_로 시작하는 이름을 가진다.
   • WINUSER.H에 선언 되어있다.
   • VK_LBUTTON,VK_RBUTTON,VK_CANCEL,VK_MBUTTON,
   • VK_BACK,VK_TAB…
3. Shift상태
   – Shift,Ctrl그리고 Alt키나 Caps Lock,Num Lock, Scroll Lock이
     눌렸는지를 알고 싶을 때는 GetKeyStat함수를 이용하여 알 수
     있다.
       • iState = GetKeyState(VK_SHIFT);
           – 만약에 Shift키가 눌려졌을 때는 상위비트가 Setting된다.
       • iState = GetKeyState(VK_CAPITAL);
           – 만약에 Caps Lock키가 켜져 있을 때는 하위 bit가 Setting 된다.
       • 만약 사용자가 Shift-Tab을 입력했는지 알고 싶다면
         WM_KEYDOWN메시지를 처리할 때
           – GetKeyState(VK_SHIFT)
   – SendMessage(hwnd,message,wParam,lParam);
       • 윈도우 프로시저에 메시지를 강제로 전달한다.

 case WM_KEYDOWN:
        switch(wParam)
        {
          case VK_HOME:
                 SendMessage(hwnd,WM_VSCROLL,SB_TOP,0); break;
          case VK_END:
                 SendMessage(hwnd,WM_VSCROLL,SB_BOTTOM,0); break;
          case VK_PRIOR:
                 SendMessage(hwnd,WM_VSCROLL,SB_PAGEUP,0);break;
        }
4. 문자 메시지
 – TranslateMessage()
    • 키스트로크 메시지를 문자 메시지로 변환한다.
 – wParam에 들어 있는 값은 ANSI코드이다.
 – 메시지 순서
    • WM_KEYDOWN        ‘A’에 대한 가상 키 코드
    • WM_CHAR            ‘a’에 대한 문자 코드
    • WM_KEYUP            ‘A’에 대한 가상 키 코드

    •   WM_KEYDOWN      VK_SHIFT 가상 키 코드
    •   WM_KEYDOWN      ‘A’에 대한 가상 키 코드
    •   WM_CHAR          ‘A’에 대한 문자 코드
    •   WM_KEYUP          ‘A’에 대한 가상 키 코드
    •   WM_KEYUP          VK_SHIFT 가상 키 코드
5. 제어문자 처리
 – 윈도우에서 키보드 문자 입력을 읽고 싶은 경우는 WM_CHAR메
   시지를 처리한다.
 – WM_CHAR 메시지는 문자만을 입력하는 메시지이므로 문자 이
   외의 키는 입력 받을 수 없다.
 – 커서 키,함수 키,Delete, Insert, Ctrl그리고 Alt는 WM_CHAR메시
   지가 전달되지 않는다. 위의 키는 KEYDOWN에서 다룬다.
 – Tab, Enter, Backspace, Escape는 WM_CHAR에서 다루는 것이
   더 좋다.
 case WM_CHAR :
   switch(wParam)
   {
      case ‘\b’;    //backspace
      break;
      case ‘\t’:    //tab
      break;
      case ‘\n’:    //줄 바꿈
      break;
      case ‘\r’:    //캐리지 리턴
      break;
      defaule :     //문자 코드
   }
6. 캐럿(커서가 아님)
 – Caret함수
   •   CreateCaret    윈도우와 연결된 캐럿을 생성.
   •   SetCaretPos    윈도우 내에서 캐럿의 위치를 설정.
   •   ShowCaret      캐럿을 보여줌.
   •   HideCaret      캐럿을 숨김.
   •   DestoryCaret   캐럿을 소멸시킴.

 – WM_SETFOCUS메시지에서
   • CreateCaret

 – WM_KILLFOCUS메시지에서
   • DestroyCaret
 7. 폰트
HFONT CreateFont(int nHeight, int nWidth, int nEscapement,
        int nOrientation, int iWeight, DWORD fdwItalic,
        DWORD fdwUnderline, DWORD fdwStrikeOut, DWORD fdwCharSet,
        DWORD fdwOutputPrecision, DWORD fdwClipPrecision,DWORD fdwQuality,
        DWORD fdwPitchAndFamily, LPCTSTR lpszFace);

 인수             설명
 nHeight        폰트의 높이, 이 값이 0일 경우에는 디폴트 크기가 사용된다.
 nWidth         폰트의 폭, 이 값이 0이면 nHeight에서 지정한 높이에 따라 폭을 자동
                으로 결정한다.
 nEscapement    폰트의 각도를 0.1도 단위로 설정한다. 이 각도는 문자가 출력될 X축
                과 문자열과의 각도이다.
 nOrientation   글자 한 자와 X축과의 각도를 지정한다. nEscapement는 전체 문자열
                의 기울기를 지정하는데 비해 이 인수는 개별 문자의 기울기를 설정한
                다.
 fnWeight       폰트의 무게를 0 – 1000까지의 값으로 지정한다. 폰트의 두께를 설정
                한다. 보통 굵기는 FW_NORMAL이 400이다.
 fdwItalic      기울임체, 밑줄, 관통선 속성을 설정한다. DWORD이지만 불린형처럼
 fdwUnderline   사용한다.
 fdwStrkieOut
7. 폰트
인수            설명
fdwCharSet    문자 셋을 설정한다. 실제 사용할 수 있는 옵션은
              ANSI_CHARSET(윈도우에서 사용),OEM_CHARSET(도스에서
              사용)과 HANGEUL_CHARSET이 있다.
fdwOutputPre 출력 정확도를 설정한다.
cision
fdwClipPrecis 클리핑 정확도를 설정한다.
ion
fdwQuality    논리적 폰트를 물리적 폰트에 얼마나 근접시킬 것인가를 지정
fdwPitchAnd   폰트의 피치와 그룹을 설정한다.
Family
lpszFace      글꼴의 이름을 나타내는 문자열을 설정한다.
7. 폰트
 – COLORREF SetTextColor( HDC hdc, COLORREF crColor);
    • Text의 컬러를 Setting하는 함수
 – COLORREF SetBkColor( HDC hdc, COLORREF crColor);
    • 글자의 배경색을 설정하는 함수
 – int SetBkMode( HDC hdc, int iBkMode );
    • 배경색상을 사용할 방법을 설정
    • OPAQUE : 불투명한 배경을 사용한다. (디폴트)
    • TRANSPARENT : 문자 사이의 여백에 있는 원래 배경이 지워지지
      않는다.
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
              switch (message)
              {
              case WM_PAINT:
                             {
                                           HDC                                         hdc;
                                           PAINTSTRUCT ps;
                                           hdc = BeginPaint(hwnd,&ps);
                                           for(int i = 0;i < 900;i+=100)
                                           {
                                                          HFONT hMyFont = CreateFont(50,0,i,0,FW_NORMAL,FALSE,FALSE,FALSE,
                                                                        HANGEUL_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,
                                                                        DEFAULT_QUALITY,VARIABLE_PITCH|FF_SWISS,"굴림");
                                                          HFONT hOldFont = (HFONT)SelectObject(hdc,hMyFont);
                                                          TextOut(hdc,0,300,"강원대학교",10);
                                                          SelectObject(hdc,hOldFont);
                                                          DeleteObject(hMyFont);
                                           }
                                           EndPaint(hwnd,&ps);
                             }
                             return 0;
              case WM_DESTROY:
                             PostQuitMessage (0) ;
                             return 0 ;
              }
              return DefWindowProc (hwnd, message, wParam, lParam) ;
}
/*--------------------------------------
   TYPER.C -- Typing Program
           (c) Charles Petzold, 1998
  --------------------------------------*/
#include <windows.h>
#define BUFFER(x,y) *(pBuffer + y * cxBuffer + x)
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT ("Typer") ;
    HWND         hwnd ;
    MSG         msg ;
    WNDCLASS        wndclass ;

    wndclass.style       = CS_HREDRAW | CS_VREDRAW ;
    wndclass.lpfnWndProc = WndProc ;
    wndclass.cbClsExtra = 0 ;
    wndclass.cbWndExtra = 0 ;
    wndclass.hInstance      = hInstance ;
    wndclass.hIcon        = LoadIcon (NULL, IDI_APPLICATION) ;
    wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
    wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
    wndclass.lpszMenuName = NULL ;
    wndclass.lpszClassName = szAppName ;
               if (!RegisterClass (&wndclass))
               {
                                MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;
                                return 0 ;
               }

              hwnd = CreateWindow (szAppName, TEXT ("Typing Program"),WS_OVERLAPPEDWINDOW,
                           CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL,
                           hInstance, NULL) ;

              ShowWindow (hwnd, iCmdShow) ;
              UpdateWindow (hwnd) ;
              while (GetMessage (&msg, NULL, 0, 0))
              {
                            TranslateMessage (&msg) ;
                            DispatchMessage (&msg) ;
              }
              return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
           static DWORD dwCharSet = DEFAULT_CHARSET ;
           static int  cxChar, cyChar, cxClient, cyClient, cxBuffer, cyBuffer,xCaret, yCaret ;
           static TCHAR * pBuffer = NULL ;
           HDC          hdc ;
           int        x, y, i ;
           PAINTSTRUCT ps ;
           TEXTMETRIC          tm ;
           switch (message)
           {
           case WM_INPUTLANGCHANGE:
                          dwCharSet = wParam ;
           case WM_CREATE:
                          hdc = GetDC (hwnd) ;
                          SelectObject (hdc, CreateFont (0, 0, 0, 0, 0, 0, 0, 0,dwCharSet, 0, 0, 0, FIXED_PITCH, NULL)) ;
                          GetTextMetrics (hdc, &tm) ;
                          cxChar = tm.tmAveCharWidth ;
                          cyChar = tm.tmHeight ;
                          DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ;
                          ReleaseDC (hwnd, hdc) ;
           case WM_SIZE:
                          if (message == WM_SIZE)
                          {
                                          cxClient = LOWORD (lParam) ;
                                          cyClient = HIWORD (lParam) ;
                          }
                          cxBuffer = max (1, cxClient / cxChar) ;
                          cyBuffer = max (1, cyClient / cyChar) ;
                          if (pBuffer != NULL)
                                          free (pBuffer) ;
                          pBuffer = (TCHAR *) malloc (cxBuffer * cyBuffer * sizeof (TCHAR)) ;
                          for (y = 0 ; y < cyBuffer ; y++)
                                          for (x = 0 ; x < cxBuffer ; x++)
                                                          BUFFER(x,y) = ' ' ;
                          xCaret = 0;yCaret = 0 ;
                          if (hwnd == GetFocus ())
                                          SetCaretPos (xCaret * cxChar, yCaret * cyChar) ;
                          InvalidateRect (hwnd, NULL, TRUE) ;
                          return 0 ;
case WM_SETFOCUS:
             CreateCaret (hwnd, NULL, cxChar, cyChar) ;
             SetCaretPos (xCaret * cxChar, yCaret * cyChar) ;
             ShowCaret (hwnd) ;
             return 0 ;
case WM_KILLFOCUS:
             HideCaret (hwnd) ;
             DestroyCaret () ;
             return 0 ;
case WM_KEYDOWN:
             switch (wParam)
             {
                            case VK_HOME:
                                           xCaret = 0 ;                                    break ;
                            case VK_END:
                                           xCaret = cxBuffer - 1 ;                         break ;
                            case VK_PRIOR:
                                           yCaret = 0 ;                                    break ;
                            case VK_NEXT:
                                           yCaret = cyBuffer - 1 ;                         break ;
                            case VK_LEFT:
                                           xCaret = max (xCaret - 1, 0) ;                  break ;
                            case VK_RIGHT:
                                           xCaret = min (xCaret + 1, cxBuffer - 1) ;       break ;
                            case VK_UP:
                                           yCaret = max (yCaret - 1, 0) ;                  break ;
                            case VK_DOWN:
                                           yCaret = min (yCaret + 1, cyBuffer - 1) ;       break ;
                            case VK_DELETE:
                                           for (x = xCaret ; x < cxBuffer - 1 ; x++)
                                                            BUFFER (x, yCaret) = BUFFER (x + 1, yCaret) ;
                                           BUFFER (cxBuffer - 1, yCaret) = ' ' ;
                                           HideCaret (hwnd) ;
                                           hdc = GetDC (hwnd) ;
             SelectObject (hdc, CreateFont (0, 0, 0, 0, 0, 0, 0, 0,dwCharSet, 0, 0, 0, FIXED_PITCH, NULL)) ;
             TextOut (hdc, xCaret * cxChar, yCaret * cyChar,&BUFFER (xCaret, yCaret),cxBuffer - xCaret) ;
                                           DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ;
                                           ReleaseDC (hwnd, hdc) ;
                                           ShowCaret (hwnd) ;
                                           break ;
             }
             SetCaretPos (xCaret * cxChar, yCaret * cyChar) ;
             return 0 ;
case WM_CHAR:
for (i = 0 ; i < (int) LOWORD (lParam) ; i++)
{
                  switch (wParam)
                  {
                  case '\b':               // backspace
                                if (xCaret > 0)
                                {                 xCaret-- ;
                                                  SendMessage (hwnd, WM_KEYDOWN, VK_DELETE, 1) ;
                                }
                                break ;
                  case '\t':              // tab
                                do
                                {                 SendMessage (hwnd, WM_CHAR, ' ', 1) ;
                                }while (xCaret % 8 != 0) ;
                                break ;
                  case '\n':               // line feed
                                if (++yCaret == cyBuffer)
                                                  yCaret = 0 ;
                                break ;
                  case '\r':              // carriage return
                                xCaret = 0 ;
                                if (++yCaret == cyBuffer)
                                                  yCaret = 0 ;
                                break ;
                  case '\x1B':               // escape
                                for (y = 0 ; y < cyBuffer ; y++)
                                                  for (x = 0 ; x < cxBuffer ; x++)
                                                                  BUFFER (x, y) = ' ' ;
                                xCaret = 0 ; yCaret = 0 ;
                                InvalidateRect (hwnd, NULL, FALSE) ;
                                break ;
                  default:               // character codes
                                BUFFER (xCaret, yCaret) = (TCHAR) wParam ;
                                HideCaret (hwnd) ;
                                hdc = GetDC (hwnd) ;
                                SelectObject (hdc, CreateFont (0, 0, 0, 0, 0, 0, 0, 0,
                                dwCharSet, 0, 0, 0, FIXED_PITCH, NULL)) ;
                                TextOut (hdc, xCaret * cxChar, yCaret * cyChar,&BUFFER (xCaret, yCaret), 1) ;
                                DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT)));
                                ReleaseDC (hwnd, hdc) ;
                                ShowCaret (hwnd) ;
                                if (++xCaret == cxBuffer)
                                {                 xCaret = 0
                                                               if (++yCaret == cyBuffer)
                                                                             yCaret = 0 ;
                                                }
                                                break ;
                                 }
                  }

                  SetCaretPos (xCaret * cxChar, yCaret * cyChar) ;
                  return 0 ;

    case WM_PAINT:
                  hdc = BeginPaint (hwnd, &ps) ;
                  SelectObject (hdc, CreateFont (0, 0, 0, 0, 0, 0, 0, 0, dwCharSet, 0, 0, 0, FIXED_PITCH, NULL)) ;
                  for (y = 0 ; y < cyBuffer ; y++)
                                  TextOut (hdc, 0, y * cyChar, & BUFFER(0,y), cxBuffer) ;
                  DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ;
                  EndPaint (hwnd, &ps) ;
                  return 0 ;
    case WM_DESTROY:
                  PostQuitMessage (0) ;
                  return 0 ;
    }
    return DefWindowProc (hwnd, message, wParam, lParam) ;
}
  6.
마우스
1. 마우스의 기본
 – 마우스의 존재여부 확인
   • fMouse = GetSystemMetrics(SM_MOUSEPRESENT);
      – 마우스가 설치되어 있다면 TRUE,
      – 설치되어 있지 않다면 FALSE

 – 마우스 단추의 개수 알아내기
   • cButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
   • 마우스가 설치되어 있지 않다면 0을 Return한다.

 – Windows는 몇 가지의 미리 정의된 마우스 커서를 가지고 있다.
   • IDC_ARROW,IDC_CROSS,IDC_WAIT
   • wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);
2. 클라이언트 영역 마우스 메시지
 – 윈도우 프로시저는 윈도우 클래스에 더블 클릭 메시지를 받도록
   미리 정의된 경우만 DBCLICK메시지를 받는다.
 – 마우스의 모든 메시지에는 lParam값에 마우스의 위치를 담고 있다.
   • x = LOWORD(lParam);
   • y = HIWORD(lParam);
 – wParam에는 마우스 단추와 Shift키 및 Ctrl키의 상태를 나타낸다.
   •   MK_LBUTTON          왼쪽 단추가 눌린 상태
   •   MK_MBUTTON          중간 단추가 눌린 상태
   •   MK_RBUTTON          오른쪽 단추가 눌린 상태
   •   MK_SHIFT            Shift가 눌린 상태
   •   MK_CONTROL          Control키가 눌린 상태
 – wParam & MK_SHIFT값이 TRUE이면 SHIFT키가 눌린 상태를 나
   타냄
   •   MK_LBUTTON          왼쪽 단추를 누른 상태
   •   MK_MBUTTON          중간 단추를 누른 상태
   •   MK_RBUTTON          오른쪽 단추를 누른 상태
   •   MK_SHIFT            SHIFT키를 누른 상태
   •   MK_CONTROL          Control키를 누른 상태
 – 윈도우의 클라이언트 영역 밖에 있을 때에도 계속 마우스의 메시
   지를 받을 수 있다.
 – 만약 시스템 메시지 상자나 대화상자가 나타나 있으면, 다른 어떤
   프로그램도 마우스 메시지를 받을 수 없다.
/*--------------------------------------------------
CONNECT.C -- Connect-the-Dots Mouse Demo Program
(c) Charles Petzold, 1998
--------------------------------------------------*/
#include <windows.h>
#define MAXPOINTS 1000
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
               static TCHAR szAppName[] = TEXT ("Connect") ;
               HWND          hwnd ;
               MSG          msg ;
               WNDCLASS        wndclass ;
               wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
               wndclass.lpfnWndProc = WndProc ;
               wndclass.cbClsExtra = 0 ;
               wndclass.cbWndExtra = 0 ;
               wndclass.hInstance       = hInstance ;
               wndclass.hIcon          = LoadIcon (NULL, IDI_APPLICATION) ;
               wndclass.hCursor         = LoadCursor (NULL, IDC_ARROW) ;
               wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
               wndclass.lpszMenuName = NULL ;
               wndclass.lpszClassName = szAppName ;
               if (!RegisterClass (&wndclass))
               {
                                MessageBox (NULL, TEXT ("Program requires Windows NT!"), szAppName, MB_ICONERROR) ;
                                return 0 ;
               }

              hwnd = CreateWindow (szAppName, TEXT ("Connect-the-Points Mouse Demo"),
                            WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,
                            NULL, NULL, hInstance, NULL) ;
              ShowWindow (hwnd, iCmdShow) ;
              UpdateWindow (hwnd) ;
              while (GetMessage (&msg, NULL, 0, 0))
              {
                            TranslateMessage (&msg) ;
                            DispatchMessage (&msg) ;
              }
              return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            static POINT pt[MAXPOINTS] ;
            static int iCount ;              HDC           hdc ;                int     i, j ;
            PAINTSTRUCT ps ;
            switch (message)
            {
            case WM_LBUTTONDOWN:
                           iCount = 0 ;
                           InvalidateRect (hwnd, NULL, TRUE) ;
                           return 0 ;
            case WM_MOUSEMOVE:
                           if (wParam & MK_LBUTTON && iCount < 1000)
                           {
                                             pt[iCount ].x = LOWORD (lParam) ;
                                             pt[iCount++].y = HIWORD (lParam) ;
                                             hdc = GetDC (hwnd) ;
                                             SetPixel (hdc, LOWORD (lParam), HIWORD (lParam), 0) ;
                                             ReleaseDC (hwnd, hdc) ;
                           }
                           return 0 ;
            case WM_LBUTTONUP:
                           InvalidateRect (hwnd, NULL, FALSE) ;
                           return 0 ;
            case WM_PAINT:
                           hdc = BeginPaint (hwnd, &ps) ;
                           SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
                           ShowCursor (TRUE) ;
                           for (i = 0 ; i < iCount - 1 ; i++)
                                             for (j = i + 1 ; j < iCount ; j++)
                                             {
                                                                MoveToEx (hdc, pt[i].x, pt[i].y, NULL) ;
                                                                LineTo (hdc, pt[j].x, pt[j].y) ;
                                             }
                           ShowCursor (FALSE) ;
                           SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
                           EndPaint (hwnd, &ps) ;
                           return 0 ;
            case WM_DESTROY:
                           PostQuitMessage (0) ;
                           return 0 ;
            }
            return DefWindowProc (hwnd, message, wParam, lParam) ;
}
2. Shift Key처리
    if (wParam & MK_SHIFT)
    {
             if (wParam & MK_CONTROL)
             {
                      [Shift와 Ctrl 키 눌린 상태]
             }
             else
             {
                      [Shift 키 눌린 상태]
             }
    }
    else
    {
             if (wParam & MK_CONTROL)
             {
                      [Ctrl key 눌린 상태]
             }
             else
             {
                      [ Shift key와 Ctrl키가 눌리지 않음]
             }
    }
3. 마우스 더블 클릭
 – 윈도우가 더블 클릭을 받기를 원하면
   • wndclass.style=CS_HREDRAW|CS_VREDRAW|CS_DBCLKS;

 – CS_DBCLKS를 지정하지 않으면
   •   WM_LBUTTONDOWN
   •   WM_LBUTTONUP
   •   WM_LBUTTONDOWN
   •   WM_LBUTTONUP

 – CS_DBCLKS를 지정하면
   •   WM_LBUTTONDOWN
   •   WM_LBUTTONUP
   •   WM_LBUTTONDBCLK
   •   WM_LBUTTONUP
4. 비 클라이언트 영역 마우스 메시지
 – 윈도우에서 비클라이언트 영역은 제목표시줄, 메뉴, 윈도우 스크
   롤바를 포함한다.
 – 비클라이언트 영역을 표시하기 위하여 ‘NC’를 포함하고 있다.
    • WM_NCLBUTTONDOWN,
    • WM_NCRBUTTONDOWN
 – wParam
    • 마우스가 움직이거나 클릭된 비클라이언트 영역을 나타낸다.
 – lParam
    • 화면상의 좌표
 – 화면상의 좌표를 클라이언트 좌표로
    • ScreenToClient(hwnd,&pt);
 – 클라이언트 좌표를 화면상의 좌표로
    • ClientToScreen(hwnd,&pt);
5. Hit-Test메시지
 – 비클라이언트 ‘hit-test’를 나타내는 WM_NCHITTEST 메시지
 – 이 메시지는 다른 모든 클라이언트 영역과 비 클라이언트 영역
   마우스 메시지에 우선한다.
 – lParam
   • 마우스의 화면좌표
 – wParam
   • 사용하지 않는다.
 – 일반적으로 Windows응용 프로그램은 이 메시지를
   DefWindowProc에 전달한다.
 – 윈도우는 이것을 바탕으로 다른 모든 마우스 메시지를 생성한다.
 – WM_NCHITTEST가 처리 될 때 DefWindowProc에서 반환된 값
   은 마우스 메시지의 wParam매개 변수가 된다.
 – DefWindowProc이 WM_NCHITTEST를 처리한 후 HTCLIENT를
   반환하면 Windows는 화면 좌표를 클라이언트 영역 좌표로 변환
   하고 클라이언트 영역 마우스 메시지를 생성한다.
   •   HTCLIENT             클라이언트 영역
   •   HTNOWHERE            창에서 눌리지 않음
   •   HTTRANSPARENT        다른 윈도우에 의해 덮여있는 윈도우
   •   HTERROR              DefWindowProc에 의해 소리 발생
   •   case WM_NCHITTEST:
   •         return (LRESULT)HTCAPTION;
6. 메시지를 생성하는 메시지
 – 시스템 메뉴 아이콘 더블 클릭
   • WM_HITTEST
   • DefWindowProc은 HTSYSMENU의 값을 반환
 – WM_NCLBUTTONDBLCLK
   • wParam => HTSYSMENU
   • DefWindowProc에 전달
   • DefWindowProc이 받으면
 – WM_SYSCOMMAND
   • wParam =>SC_CLOSE
   • DefWindowProc에 전달
   • DefWindowProc이 받으면
 – WM_CLOSE메시지를 발생
   • DefWindowProc이 받으면
   • DestroyWindow()함수 호출
 – WM_DESTROY메시지 발생
   • PostQuitMessage(0)
 – WM_QUIT메시지 발생
#include <windows.h>
#define DIVISIONS 5
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
              PSTR szCmdLine, int iCmdShow)
{
               static TCHAR szAppName[] = TEXT ("Checker2") ;
               HWND        hwnd ;
               MSG        msg ;
               WNDCLASS      wndclass ;

             wndclass.style     = CS_HREDRAW | CS_VREDRAW ;
             wndclass.lpfnWndProc = WndProc ;
             wndclass.cbClsExtra = 0 ;
             wndclass.cbWndExtra = 0 ;
             wndclass.hInstance   = hInstance ;
             wndclass.hIcon      = LoadIcon (NULL, IDI_APPLICATION) ;
             wndclass.hCursor     = LoadCursor (NULL, IDC_ARROW) ;
             wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
             wndclass.lpszMenuName = NULL ;
             wndclass.lpszClassName = szAppName ;

             if (!RegisterClass (&wndclass))
             {
                              MessageBox (NULL, TEXT ("Program requires Windows NT!"),szAppName, MB_ICONERROR) ;
                              return 0 ;
             }
             hwnd = CreateWindow (szAppName, TEXT ("Checker2 Mouse Hit-Test Demo"),WS_OVERLAPPEDWINDOW,
                              CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
                              NULL, NULL, hInstance, NULL) ;
             ShowWindow (hwnd, iCmdShow) ;
             UpdateWindow (hwnd) ;

             while (GetMessage (&msg, NULL, 0, 0))
             {
                           TranslateMessage (&msg) ;
                           DispatchMessage (&msg) ;
             }
             return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
           static BOOL fState[DIVISIONS][DIVISIONS] ;
           static int cxBlock, cyBlock ;
           HDC         hdc ;
           int       x, y ;
           PAINTSTRUCT ps ;
           POINT        point ;
           RECT         rect ;
           switch (message)
           {
           case WM_SIZE :
                            cxBlock = LOWORD (lParam) / DIVISIONS ;
                            cyBlock = HIWORD (lParam) / DIVISIONS ;                   return 0 ;
           case WM_SETFOCUS :
                            ShowCursor (TRUE) ;                         return 0 ;
           case WM_KILLFOCUS :
                            ShowCursor (FALSE)                          return 0 ;
           case WM_KEYDOWN :
                            GetCursorPos (&point) ;
                            ScreenToClient (hwnd, &point) ;
                            x = max (0, min (DIVISIONS - 1, point.x / cxBlock)) ;
                            y = max (0, min (DIVISIONS - 1, point.y / cyBlock)) ;
                            switch (wParam)
                            {
                            case VK_UP :                  y-- ;                       break ;
                            case VK_DOWN :                y++ ;                       break ;
                            case VK_LEFT :                              x-- ;                    break ;
                            case VK_RIGHT :               x++ ;                       break ;
                            case VK_HOME :                x=y=0;                      break ;
                            case VK_END :                               x = y = DIVISIONS - 1 ;  break ;
                            case VK_RETURN :
                            case VK_SPACE :
                                            SendMessage (hwnd, WM_LBUTTONDOWN, MK_LBUTTON,MAKELONG (x * cxBlock, y * cyBloc
                                            break ;
                            }
                            x = (x + DIVISIONS) % DIVISIONS ;
                            y = (y + DIVISIONS) % DIVISIONS ;
                            point.x = x * cxBlock + cxBlock / 2 ;
                            point.y = y * cyBlock + cyBlock / 2 ;
                            ClientToScreen (hwnd, &point) ;
                            SetCursorPos (point.x, point.y) ;
                            return 0 ;
    case WM_LBUTTONDOWN :
                x = LOWORD (lParam) / cxBlock ;
                y = HIWORD (lParam) / cyBlock ;
                if (x < DIVISIONS && y < DIVISIONS)
                {
                                fState[x][y] ^= 1 ;

                                   rect.left = x * cxBlock ;
                                   rect.top = y * cyBlock ;
                                   rect.right = (x + 1) * cxBlock ;
                                   rect.bottom = (y + 1) * cyBlock ;

                                   InvalidateRect (hwnd, &rect, FALSE) ;
                   }
                   else
                              MessageBeep (0) ;
                return 0 ;
    case WM_PAINT :
                hdc = BeginPaint (hwnd, &ps) ;

                  for (x = 0 ; x < DIVISIONS ; x++)
                                   for (y = 0 ; y < DIVISIONS ; y++)
                                   {
                                                    Rectangle (hdc, x * cxBlock, y * cyBlock,(x + 1) * cxBlock, (y + 1) * cyBlock) ;
                                                    if (fState [x][y])
                                                    {
                                                                     MoveToEx (hdc, x *cxBlock, y *cyBlock, NULL) ;
                                                                     LineTo (hdc, (x+1)*cxBlock, (y+1)*cyBlock) ;
                                                                     MoveToEx (hdc, x *cxBlock, (y+1)*cyBlock, NULL) ;
                                                                     LineTo (hdc, (x+1)*cxBlock, y *cyBlock) ;
                                                    }
                                   }
                  EndPaint (hwnd, &ps) ;
                  return 0 ;
    case WM_DESTROY :
                  PostQuitMessage (0) ;
                  return 0 ;
    }
    return DefWindowProc (hwnd, message, wParam, lParam) ;
}
/*-------------------------------------------------
   CHECKER3.C -- Mouse Hit-Test Demo Program No. 3
            (c) Charles Petzold, 1998
  -------------------------------------------------*/
#include <windows.h>
#define DIVISIONS 5
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
LRESULT CALLBACK ChildWndProc (HWND, UINT, WPARAM, LPARAM) ;
TCHAR szChildClass[] = TEXT ("Checker3_Child") ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
               static TCHAR szAppName[] = TEXT ("Checker3") ;
               HWND          hwnd ;
               MSG          msg ;
               WNDCLASS        wndclass ;
               wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
               wndclass.lpfnWndProc = WndProc ;
               wndclass.cbClsExtra = 0 ;
               wndclass.cbWndExtra = 0 ;
               wndclass.hInstance       = hInstance ;
               wndclass.hIcon          = LoadIcon (NULL, IDI_APPLICATION) ;
               wndclass.hCursor         = LoadCursor (NULL, IDC_ARROW) ;
               wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
               wndclass.lpszMenuName = NULL ;
               wndclass.lpszClassName = szAppName ;
               if (!RegisterClass (&wndclass))
               {
                                MessageBox (NULL, TEXT ("Program requires Windows NT!"),szAppName, MB_ICONERROR) ;
                                return 0 ;
               }
               wndclass.lpfnWndProc = ChildWndProc ;
               wndclass.cbWndExtra = sizeof (long) ;
               wndclass.hIcon          = NULL ;
               wndclass.lpszClassName = szChildClass ;
               RegisterClass (&wndclass) ;
               hwnd = CreateWindow (szAppName, TEXT ("Checker3 Mouse Hit-Test Demo"),WS_OVERLAPPEDWINDOW,
                                CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL);
               ShowWindow (hwnd, iCmdShow) ;
               UpdateWindow (hwnd) ;
               while (GetMessage (&msg, NULL, 0, 0))
               {
                                TranslateMessage (&msg) ;
                                DispatchMessage (&msg) ;
               }
               return msg.wParam ;
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
           static HWND hwndChild[DIVISIONS][DIVISIONS] ;
           int      cxBlock, cyBlock, x, y ;
           switch (message)
           {
           case WM_CREATE :
                         for (x = 0 ; x < DIVISIONS ; x++)
                                         for (y = 0 ; y < DIVISIONS ; y++)
                                         hwndChild[x][y] = CreateWindow (szChildClass, NULL,WS_CHILDWINDOW | WS_VISIBLE,
                                                                       0, 0, 0, 0, hwnd, (HMENU) (y << 8 | x),
                                                                       (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE), NULL) ;
                         return 0 ;
           case WM_SIZE :
                         cxBlock = LOWORD (lParam) / DIVISIONS ;
                         cyBlock = HIWORD (lParam) / DIVISIONS ;
                         for (x = 0 ; x < DIVISIONS ; x++)
                                         for (y = 0 ; y < DIVISIONS ; y++)
                                         MoveWindow (hwndChild[x][y],x * cxBlock, y * cyBlock,cxBlock, cyBlock, TRUE) ;
                         return 0 ;
           case WM_LBUTTONDOWN :
                         MessageBeep (0) ;
                         return 0 ;
           case WM_DESTROY :
                         PostQuitMessage (0) ;
                         return 0 ;
           }
           return DefWindowProc (hwnd, message, wParam, lParam) ;
}
LRESULT CALLBACK ChildWndProc (HWND hwnd, UINT message,WPARAM wParam, LPARAM lParam)
{
   HDC         hdc ;
   PAINTSTRUCT ps ;
   RECT        rect ;
   switch (message)
   {
   case WM_CREATE :
       SetWindowLong (hwnd, 0, 0) ;         // on/off flag
       return 0 ;
   case WM_LBUTTONDOWN :
       SetWindowLong (hwnd, 0, 1 ^ GetWindowLong (hwnd, 0)) ;
       InvalidateRect (hwnd, NULL, FALSE) ;
       return 0 ;
   case WM_PAINT :
       hdc = BeginPaint (hwnd, &ps) ;
       GetClientRect (hwnd, &rect) ;
       Rectangle (hdc, 0, 0, rect.right, rect.bottom) ;
       if (GetWindowLong (hwnd, 0))
       {
           MoveToEx (hdc, 0,          0, NULL) ;
           LineTo (hdc, rect.right, rect.bottom) ;
           MoveToEx (hdc, 0,          rect.bottom, NULL) ;
           LineTo (hdc, rect.right, 0) ;
       }
       EndPaint (hwnd, &ps) ;
       return 0 ;
   }
   return DefWindowProc (hwnd, message, wParam, lParam) ;
}
6. 윈도우의 종류
• WS_CHILD, WS_POPUP
  – 오버랜드가 아닌 윈도우는 차일드나 팝업 윈도우 둘 중 하나가
    된다.
  – 이 두 스타일은 상호 배치되는 성질이 있기 때문에 동시에 같이
    사용할 수는 없다.

  – WS_CHILD
    • 다른 윈도우의 차일드 윈도우가 된다.
    • CreateWindow의 hParent인수에 부모 윈도우의 핸들을 대입해 주
      어 어떤 윈도우의 차일드가 될 것인가를 지정해 주어야 한다.
    • 차일드 윈도우는 부모 윈도우의 작업영역 밖을 벗어날 수 없다.
    • 메인 윈도우의 작업영역 내부에서 사용되는 컨트롤이 이 스타일을
      사용한다.
  – WS_POPUP
    • 만든 윈도우는 대화상자나 메시지 박스처럼 부모 윈도우의 작업 영
      역밖으로 이동할 수 있다.
    • 항상 부모 윈도우보다 수직적으로 위에 위치(Z 순서)하므로 부모에
      의해 가려지지 않는다.
#include <windows.h>
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
              static TCHAR szAppName[] = TEXT ("HelloWin") ;
              HWND                      hwnd ;
              MSG       msg ;
              WNDCLASS wndclass ;

             wndclass.style                                = CS_HREDRAW | CS_VREDRAW ;
             wndclass.lpfnWndProc            = WndProc ;
             wndclass.cbClsExtra       =0;
             wndclass.cbWndExtra             =0;
             wndclass.hInstance       = hInstance ;
             wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
             wndclass.hCursor                = LoadCursor (NULL, IDC_ARROW) ;
             wndclass.hbrBackground          = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
             wndclass.lpszMenuName           = NULL ;
             wndclass.lpszClassName          = szAppName ;
             if (!RegisterClass (&wndclass))
             {
                             MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName,MB_ICONERROR) ;
                             return 0 ;
             }
             hwnd = CreateWindow (szAppName,                            // window class name
                                             "The Hello Program",                         // window caption
                                             WS_POPUPWINDOW,         // window style
                                             10,                        // initial x position
                                             10,                        // initial y position
                                             200,                       // initial x size
                                             100,                       // initial y size
                                             NULL,                                        // parent window handle
                                             NULL,                                        // window menu handle
                                             hInstance,                                   // program instance handle
                                             NULL) ;                                      // creation parameters
             ShowWindow (hwnd, iCmdShow) ;
             UpdateWindow (hwnd) ;
             while (GetMessage (&msg, NULL, 0, 0))
             {
                             TranslateMessage (&msg) ;
                             DispatchMessage (&msg) ;
             }
             return msg.wParam ;
}
6. 확장 윈도우 스타일
  – HWND CreateWindowEx( DWORD dwExStyle, LPCTSTR
    lpClassName)
     • CreateWindow에 dwExStyle이 추가되었다.

WS_EX_TOPMOST 모든 윈도우보다 수직적으로 위에 있는 윈도우를 만든다.
              활성화된 상태에서도 다른 윈도우에 가려지지 않는다.
WS_EX_TRANSPA   형제 윈도우가 다 그려지기 전에 그려지지 않아 투명하게
RENT            보이는 윈도우를 만든다.
WS_EX_CLIENTED 작업영역이 쑥 들어간 음각 모양으로 만든다.
GE
WS_EX_LAYERD    2000에서 추가된 속성이며 레이어드 윈도우를 생성한다.
   6. 확장 윈도우 스타일
 • 작업영역 크기 설정
      – BOOL AdjustWindowRect( LPRECT lpRect, DWORD dwStyle,
        BOOL bMenu);
           • 이 함수는 원하는 작업영역의 크기를 주면 작업영역 크기에 맞는 윈
             도우 크기를 계산해 준다.
           • lpRect : 작업영역의 크기
           • dwStyle : 윈도우의 스타일
           • bMenu : 메뉴의 존재 여부를 전달

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
         switch (message)
         {
         case WM_CREATE:
         {
                   RECT rect = {0,0,300,300};
                   AdjustWindowRect(&rect,WS_OVERLAPPEDWINDOW,FALSE);
                   MoveWindow(hwnd,100,100,rect.right-rect.left,rect.bottom-rect.top,FALSE);
         }
         return 0;
7. 마우스 캡쳐
 – 마우스 캡처하기
    • 윈도우의 클라이언트나 비클라이언트 영역 위에 있을 때 마우스 메
      시지를 받을 수 있다.
    • 마우스가 윈도우 밖에 있을 때 마우스 메시지를 받고 싶으면 마우스
      를 캡처해야 한다.
 – SetCapture(hwnd);
 – ReleaseCapture();
  7.
타이머
1. 타이머의 기초
 – SetTimer를 호출하면 Windows는 프로그램에 타이머를 할당한
   다.
    • WM_TIMER메시지가 발생한다.
 – KillTimer함수를 발생시켜 타이머 메시지가 전송되는 것을 중지
   시킬 수 있다.
 – Windows는 WM_TIMER메시지를 WM_PAINT메시지와 동등하게
   취급한다.
    • 두 메시지 모두 우선순위가 낮으며 메시지 큐에 다른 메시지가 없을
      때만 이 메시지가 프로그램에 전달된다.
    • 메시지 큐에 WM_TIMER메시지가 있는 상태에서 다른 WM_TIMER
      메시지가 들어오면 두 메시지는 하나로 결합한다.
 – SetTimer(hwnd,1,interval,NULL);
    • WM_TIMER를 받게 될 윈도우의 핸들
    • 타이머의 ID
    • 1/1000초
 – KillTimer(hwnd,1);
    • 윈도우 핸들, 삭제할 타이머의 ID
 – SetTimer(hwnd,TimerID,interval,TimerProc);
    • WM_TIMER를 받게 될 윈도우의 핸들,
    • 타이머의 ID, 1/1000초
    • Timer CallBack함수
/*-----------------------------------------
   BEEPER1.C -- Timer Demo Program No. 1
            (c) Charles Petzold, 1998
  -----------------------------------------*/
#include <windows.h>
#define ID_TIMER 1
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
               static TCHAR szAppName[] = TEXT ("Beeper1") ;
               HWND         hwnd ;
               MSG         msg ;
               WNDCLASS       wndclass ;

             wndclass.style     = CS_HREDRAW | CS_VREDRAW ;
             wndclass.lpfnWndProc = WndProc ;
             wndclass.cbClsExtra = 0 ;
             wndclass.cbWndExtra = 0 ;
             wndclass.hInstance   = hInstance ;
             wndclass.hIcon      = LoadIcon (NULL, IDI_APPLICATION) ;
             wndclass.hCursor     = LoadCursor (NULL, IDC_ARROW) ;
             wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
             wndclass.lpszMenuName = NULL ;
             wndclass.lpszClassName = szAppName ;

             if (!RegisterClass (&wndclass))
             {
                              MessageBox (NULL, TEXT ("Program requires Windows NT!"),szAppName, MB_ICONERROR);
                              return 0 ;
             }

             hwnd = CreateWindow (szAppName, TEXT ("Beeper1 Timer Demo"),WS_OVERLAPPEDWINDOW,
             CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL);

             ShowWindow (hwnd, iCmdShow) ;
             UpdateWindow (hwnd) ;

             while (GetMessage (&msg, NULL, 0, 0))
             {
                           TranslateMessage (&msg) ;
                           DispatchMessage (&msg) ;
             }
             return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            static BOOL fFlipFlop = FALSE ;
            HBRUSH       hBrush ;
            HDC        hdc ;
            PAINTSTRUCT ps ;
            RECT       rc ;
            switch (message)
            {
            case WM_CREATE:
                            SetTimer (hwnd, ID_TIMER, 1000, NULL) ;
                            return 0 ;
            case WM_TIMER :
                            MessageBeep (-1) ;
                            fFlipFlop = !fFlipFlop ;
                            InvalidateRect (hwnd, NULL, FALSE) ;
                            return 0 ;
            case WM_PAINT :
                            hdc = BeginPaint (hwnd, &ps) ;
                            GetClientRect (hwnd, &rc) ;
                            hBrush = CreateSolidBrush (fFlipFlop ? RGB(255,0,0) : RGB(0,0,255)) ;
                            FillRect (hdc, &rc, hBrush) ;
                            EndPaint (hwnd, &ps) ;
                            DeleteObject (hBrush) ;
                            return 0 ;
            case WM_DESTROY :
                            KillTimer (hwnd, ID_TIMER) ;
                            PostQuitMessage (0) ;
                            return 0 ;
            }
            return DefWindowProc (hwnd, message, wParam, lParam) ;
}
/*----------------------------------------
BEEPER2.C -- Timer Demo Program No. 2
(c) Charles Petzold, 1998
----------------------------------------*/
#include <windows.h>
#define ID_TIMER 1
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
VOID CALLBACK TimerProc (HWND, UINT, UINT, DWORD ) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
               static TCHAR szAppName[] = TEXT ("Beeper2") ;
               HWND        hwnd ;
               MSG        msg ;
               WNDCLASS      wndclass ;

             wndclass.style     = CS_HREDRAW | CS_VREDRAW ;
             wndclass.lpfnWndProc = WndProc ;
             wndclass.cbClsExtra = 0 ;
             wndclass.cbWndExtra = 0 ;
             wndclass.hInstance   = hInstance ;
             wndclass.hIcon      = LoadIcon (NULL, IDI_APPLICATION) ;
             wndclass.hCursor     = LoadCursor (NULL, IDC_ARROW) ;
             wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
             wndclass.lpszMenuName = NULL ;
             wndclass.lpszClassName = szAppName ;

             if (!RegisterClass (&wndclass))
             {
                              MessageBox (NULL, TEXT ("Program requires Windows NT!"), szAppName, MB_ICONERROR) ;
                              return 0 ;
             }
             hwnd = CreateWindow (szAppName, TEXT ("Beeper2 Timer Demo"), WS_OVERLAPPEDWINDOW,
             CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;

             ShowWindow (hwnd, iCmdShow) ;
             UpdateWindow (hwnd) ;

             while (GetMessage (&msg, NULL, 0, 0))
             {
                           TranslateMessage (&msg) ;
                           DispatchMessage (&msg) ;
             }
             return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            switch (message)
            {
            case WM_CREATE:
                            SetTimer (hwnd, ID_TIMER, 1000, TimerProc) ;
                            return 0 ;
            case WM_DESTROY:
                            KillTimer (hwnd, ID_TIMER) ;
                            PostQuitMessage (0) ;
                            return 0 ;
            }
            return DefWindowProc (hwnd, message, wParam, lParam) ;
}
VOID CALLBACK TimerProc (HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
            static BOOL fFlipFlop = FALSE ;
            HBRUSH       hBrush ;
            HDC        hdc ;
            RECT       rc ;

            MessageBeep (-1) ;
            fFlipFlop = !fFlipFlop ;

            GetClientRect (hwnd, &rc) ;

            hdc = GetDC (hwnd) ;
            hBrush = CreateSolidBrush (fFlipFlop ? RGB(255,0,0) : RGB(0,0,255)) ;

            FillRect (hdc, &rc, hBrush) ;
            ReleaseDC (hwnd, hdc) ;
            DeleteObject (hBrush) ;
}
2. FindWindow
  – HWND FindWindow( LPCTSTR lpClassName, LPCTSTR
    lpWindowName);
     • lpClassName, lpWindowName에 캡션을 전달해주되 둘 중 하나만
       지정해 줄 수도 있다.
     • 대소문자는 구분하지 않는다.
     • 조건에 맞는 윈도우를 찾으면 그 핸들을 리턴해 준다. 찾지 못할 경
       우에는 NULL을 리턴한다.
     • 자신이 만든 윈도우에만 제한적으로 사용한다. ( 캡션을 변경하지
       말아야 한다.)


  – HWND WindowFromPoint(POINT Point);
     • 이 함수는 Point화면 좌표 아래에 있는 윈도우를 조사해서 그 핸들
       을 리턴해 준다.
VOID CALLBACK TimerProc (HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
         POINT pt;
         GetCursorPos(&pt);
         HWND hFindhwnd = WindowFromPoint(pt);

         InvalidateRect(hwnd,NULL,TRUE);
         UpdateWindow(hwnd);
         if (hFindhwnd == NULL)
         {
                    HDC hdc = GetDC(hwnd);
                    TextOut(hdc,0,0,"윈도우 없음",11);
                    ReleaseDC(hwnd,hdc);
         }
         else
         {
                    HDC hdc = GetDC(hwnd);
                    char Temp[256];
                    GetWindowText(hFindhwnd,Temp,256);
                    TextOut(hdc,0,0,Temp,strlen(Temp));

                   GetClassName(hFindhwnd,Temp,256);
                   TextOut(hdc,0,20,Temp,strlen(Temp));
                   ReleaseDC(hwnd,hdc);
         }
}
3. 윈도우 열거
 – BOOL EnumWindows(WNDENUMPROC lpEnumFunc,
   LPARAM lParam);
    • 현재 실행중인 모든 최상위 윈도우들을 열거하여 첫 번째 인수로 지
      정된 콜백함수를 호출해 준다.
    • BOOL CALLBACK EnumWindowProc(HWND hwnd, LPARAM
      lParam);
    • EnumWindows함수는 모든 최상위 윈도우를 검색하여 그 핸들을 콜
      백함수로 전달해 주되 모든 윈도우를 다 찾거나 콜백함수가 FALSE
      를 리턴할 때까지 검색을 계속한다.


 – BOOL EnumChildWindows( HWND hWndParent,
   WNDENUMPROC lpEnumFunc, LPARAM lParam);
    • 특정 부모 윈도우의 차일드만 검색
 – BOOL EnumThreadWindows(DWORD dwThreadId,
   WNDENUMPROC lpfn, LPARAM lParam);
    • 스레드에 속한 윈도우의 목록을 조사
static int yPos;
BOOL CALLBACK MyEnumProc (HWND hwnd, LPARAM lParam);
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
              switch (message)
              {
              case WM_LBUTTONDOWN:
                           yPos = 0;
                           EnumWindows(MyEnumProc,(LPARAM)hwnd);
                           return 0;
              case WM_DESTROY:
                           PostQuitMessage (0) ;
                           return 0 ;
              }
              return DefWindowProc (hwnd, message, wParam, lParam) ;
}
BOOL CALLBACK MyEnumProc (HWND hwnd, LPARAM lParam)
{
              HWND myhwnd = (HWND)lParam;
              HDC hdc = GetDC(myhwnd);
              char Temp[256];
              GetWindowText(hwnd,Temp,256);
              TextOut(hdc,0,yPos,Temp,strlen(Temp));
              ReleaseDC(myhwnd,hdc);
              yPos += 14;
              return TRUE;
}
3. 윈도우 크기 변경
      – WM_SIZING
           • 사용자가 윈도우 크기를 변경하고 있을 때 보내진다.
           • wParam : 사용자가 드래그하고 있는 윈도우의 경계선이 어느쪽인
             가를 지정하는 WMSZ_BOTTOM, WMSZ_LEFT, WMSZ_TOP등의
             값이 전달된다.
           • lParam : 현재 윈도우의 영역을 화면 좌표로 가지는 RECT구조체의
             포인터가 전달된다.
           • 이메시지에서 좌표를 변경했으면 반드시 TRUE를 리턴 해야 한다.
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
          switch (message)
          {
          case WM_SIZING:
                      ((RECT *)lParam)->left = ((RECT *)lParam)->left / 50 * 50;
                      ((RECT *)lParam)->top = ((RECT *)lParam)->top / 50 * 50;
                      ((RECT *)lParam)->right = ((RECT *)lParam)->right / 50 * 50;
                      ((RECT *)lParam)->bottom = ((RECT *)lParam)->bottom / 50 * 50;
                      return TRUE;
          case WM_DESTROY:
                      PostQuitMessage (0) ;
                      return 0 ;
          }
          return DefWindowProc (hwnd, message, wParam, lParam) ;
}
    3. 윈도우 크기 변경
      – WM_GETMINMAXINFO
           • 운영체제는 윈도우의 크기나 위치를 바꾸기 전에 이 메시지를 응용
             프로그램으로 보내어 위치와 크기에 대해 제한을 할 수 있도록 기회
             를 준다.
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
          switch (message)
          {
          case WM_GETMINMAXINFO:
                      {
                              MINMAXINFO * lpMinMaxInfo = (LPMINMAXINFO)lParam;
                              lpMinMaxInfo->ptMaxTrackSize.x = 300;
                              lpMinMaxInfo->ptMaxTrackSize.y = 300;
                              lpMinMaxInfo->ptMinTrackSize.x = 100;
                              lpMinMaxInfo->ptMinTrackSize.x = 100;

                                 lpMinMaxInfo->ptMaxSize.x = 200;
                                 lpMinMaxInfo->ptMaxSize.y = 200;
                                 lpMinMaxInfo->ptMaxPosition.x = 100;
                                 lpMinMaxInfo->ptMaxPosition.y = 100;
                      }
                      return 0;
          case WM_DESTROY:
                      PostQuitMessage (0) ;
                      return 0 ;
          }
          return DefWindowProc (hwnd, message, wParam, lParam) ;
}
 4. 윈도우 이동
       – WM_MOVING
             • 윈도우가 이동 중일 때 발생한다.
             • 윈도우 이동이 완료되었을 때는 WM_MOVE메시지가 전달된다.
       – WM_WINDOWPOSCHNAGING
             •   윈도우의 위치 뿐만 아니라 Z순서가 변해도 전달된다.
             •   이동 중일 때 발생한다.
             •   이동이 완료되면 WM_WINDOWPOSCHANGED 메시지가 전달된다.
             •   lParam에 WINDOWPOS구조체의 주소가 전달된다.
             •   이 구조체의 값들을 참고하여 현재 위치와 크기를 알 수 있으며, 강
                 제로 값을 변경하여 위치를 조정할 수도 있다.
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
           switch (message)
           {
           case WM_WINDOWPOSCHANGING:
                         {
                                      int t = ((LPWINDOWPOS)lParam)->x;
                                      if (t < 30) ((LPWINDOWPOS)lParam)->x = 0;
                         }
                         return 0;
           case WM_DESTROY:
                         PostQuitMessage (0) ;
                         return 0 ;
           }
           return DefWindowProc (hwnd, message, wParam, lParam) ;
}
5. 반투명한 윈도우
 – 레이어드 윈도우를 만들려면 WS_EX_LAYERD 확장 스타일을 준
   다.
 – 레이어드 윈도우가 되면 이 윈도우는 다음 함수를 호출하기 전에
   는 화면에 보이지 않는다.
   • BOOL SetLayeredWindowAttributes( HWND hwnd, COLORREF
     crKey, BYTE bAlpha, DWORD dwFlags);
   • 레이어드 윈도우의 투명 및 반투명 속성을 설정한다.
   • crKey는 투명으로 처리할 색상을 지정한다.
   • 투명으로 지정된 색상 부분은 윈도우에서 보이지 않는 투명 영역으
     로 처리된다.
   • bAlpha는 윈도우 전체의 반투명 정도를 지정하는데 0이면 완전 투
     명이며 255는 불투명이고, 128이면 반쯤 투명하다.
   • dwFlags는 두 효과중 어떤 효과를 줄 것인가를 지정하는데
     LWA_COLORKEY 플래그는 투명 색상 지정을, LWA_ALPHA는 반투
     명정도를지정하며 두 플래그를 동시에 줄 수도 있다.
6. 훅
  – 훅이란 메시지가 목표 윈도우에 전달되기 전에 메시지를 가로채
    는 프로시저이다.
  – 응용 프로그램이 훅 프로시저를 설치하면 메시지가 윈도우로 보
    내지기 전에 훅 프로시저에 먼저 보내진다.
  – 훅 프로시저가 어떤 메시지를 받을 것인가는 훅 타입과 훅의 범
    위에 따라 달라진다.
  – 시스템 전역 훅 프로시저
       • 모든 스레드에서 발생하는 메시지를 가로챈다.
  – 스레드 한정적 훅 프로시저
       • 특정 스레드에서 발생하는 메시지만 가로챈다.

  – 운영체제는 설치된 훅 프로시저들을 훅 체인으로 관리한다.
       • 훅 체인이란 훅 프로시저 함수들의 번지를 담고 있는 일종의 함수
         포인터 배열이라고 할 수 있다.
  – 응용 프로그램이 훅 프로시저를 설치하면 운영체제는 훅 체인의
    선두에 이 프로시저를 등록한다.
  – 훅 프로시저가 감시하는 메시지가 발생하면 운영체제는 훅 체인
    의 선두에 등록된 훅 프로시저에게 이 메시지를 전달하고 훅 프
    로시저는 체인을 따라 다음 훅 프로시저에게 메시지를 반복적으
    로 전달하며 끝으로 그 메시지를 받을 윈도우에게 전달된다.
6. 훅
  – LRESULT CALLBACK KeyboardProcc(int code, WPARAM
    wParam, LPARAM lParam);
       • code : 훅 프로시저에서 이 메시지를 어떻게 처리할 것인가를 알려
         준다.

  – HHOOK SetWindowHookEx(int idHook, HOOKPROC lpfn,
    HINSTANCE hMod, DWORD dwThreadId);
       • idHook : 설치하고자 하는 훅의 타입
       • lpfn : 훅 프로시저의 번지
       • hMod : 훅 프로시저를 가진 인스턴스 핸들
       • dwThradId : 훅 프로시저가 감시할 스레드의 ID이되 이 값이 0이면
         시스템의 모든 스레드에서 발생하는 메시지가 훅 프로시저로 전달
         된다.
       • 시스템의 모든 메시지를 감시하고자 한다거나 다른 프로그램의 메
         시지를 감시하고자 할 경우 lpfn은 반드시 분리된 DLL에 있어야 하
         며 hMod는 이 DLL의 핸들이어야 한다.
  – BOOL UnhookWindowsHookEx( HHOOK hhk );
  – LRESULT CallNextHookEx(HHOOK hhk, int nCode, WPARAM
    wParam, LPARAM lParam);
#define WINVER 0x500
#define _WIN32_WINNT 0x500

#include <windows.h>
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
HWND g_hMainWnd;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
              static TCHAR szAppName[] = TEXT ("Beeper2") ;
              MSG        msg ;
              WNDCLASS      wndclass ;

             wndclass.style     = CS_HREDRAW | CS_VREDRAW ;
             wndclass.lpfnWndProc = WndProc ;
             wndclass.cbClsExtra = 0 ;
             wndclass.cbWndExtra = 0 ;
             wndclass.hInstance   = hInstance ;
             wndclass.hIcon      = LoadIcon (NULL, IDI_APPLICATION) ;
             wndclass.hCursor     = LoadCursor (NULL, IDC_ARROW) ;
             wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
             wndclass.lpszMenuName = NULL ;
             wndclass.lpszClassName = szAppName ;

             if (!RegisterClass (&wndclass))
             {
                              MessageBox (NULL, TEXT ("Program requires Windows NT!"), szAppName, MB_ICONERROR) ;
                              return 0 ;
             }
             g_hMainWnd = CreateWindow (szAppName, TEXT ("Beeper2 Timer Demo"), WS_OVERLAPPEDWINDOW,
             CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;

             ShowWindow (g_hMainWnd, iCmdShow) ;
             UpdateWindow (g_hMainWnd) ;

             while (GetMessage (&msg, NULL, 0, 0))
             {
                           TranslateMessage (&msg) ;
                           DispatchMessage (&msg) ;
             }
             return msg.wParam ;
}
HHOOK hKeyHook;
LRESULT CALLBACK KeyHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
            HDC hdc;
            char str[256];
            RECT rect = {100,120,500,150};
            static int iCount = 0;

             if (nCode < 0)
             {
                             return CallNextHookEx(hKeyHook,nCode,wParam,lParam);
             }
             wsprintf(str,"nCode=%d, wParam=%d, lParam=%x, iCount=%d", nCode, wParam, lParam,iCount++);
             hdc = GetDC(g_hMainWnd);
             InvalidateRect(g_hMainWnd,&rect,TRUE);
             UpdateWindow(g_hMainWnd);
             TextOut(hdc,100,120,str,strlen(str));
             ReleaseDC(g_hMainWnd,hdc);
             if (wParam == 'A')
                             return 1;
             return CallNextHookEx(hKeyHook,nCode,wParam,lParam);
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            switch (message)
            {
            case WM_CREATE:
                          hKeyHook = SetWindowsHookEx(WH_KEYBOARD,KeyHookProc,NULL,GetCurrentThreadId());
                          return 0;
            case WM_CHAR:
                          {
                                        static int xPos = 0;
                                        char c = (char)wParam;
                                        HDC hdc = GetDC(hwnd);
                                        TextOut(hdc,xPos,0,&c,1);
                                        ReleaseDC(hwnd,hdc);
                                        xPos+= 10;
                          }
                          return 0;
            case WM_DESTROY:
                          PostQuitMessage (0) ;
                          return 0 ;
            }
            return DefWindowProc (hwnd, message, wParam, lParam) ;
}
       8.
자식 윈도우 제어
1. 자식 윈도우 제어
 – 부모 윈도우의 handle을 알아내기
   • hwndParent = GetParent(hwnd);
 – 부모 윈도우에 메시지 보내기
   • SendMessage(hwndParent,message,wParam,lParam);
 – 자식 컨트롤 만들기
   • CreateWindow()로 자식 윈도우를 만든다.
   • MoveWindow()호출로 자식 윈도우의 위치와 크기를 조절한다.
   • 미리 정의된 컨트롤(단추,체크 상자,편집 상자,목록 상자…) 중 하나
     를 만들 때는 자식 윈도우의 클래스가 이미 윈도우에 등록되어 있기
     때문에 윈도우 클래스를 등록할 필요가 없다.
 – 각각의 단추를 클릭하면자식 윈도우 컨트롤은 자신의 부모 윈도
   우에게 WM_COMMAND메시지를 보낸다.
   • LOWORD(wParam) : 자식윈도우 ID
   • HIWORD(wParam) : 알림코드
   • lParam : 자식윈도우의 핸들
 – BS_OWNERDRAW
   • 사용자가 책임지고 버튼을 그려야 한다.
   • 버튼이 그려져야 할 경우에 부모에게 WM_DRAWITEM메시지를 보
     낸다.
   • lParam : DRAWITEMSTRUCT구조체의 포인터가 들어 있다.
/*----------------------------------------
BTNLOOK.C -- Button Look Program
(c) Charles Petzold, 1998
----------------------------------------*/
#include <windows.h>
struct
{
               int    iStyle ;
               TCHAR * szText ;
} button[] = {
               BS_PUSHBUTTON,             TEXT ("PUSHBUTTON"),
               BS_DEFPUSHBUTTON, TEXT ("DEFPUSHBUTTON"),
               BS_CHECKBOX,             TEXT ("CHECKBOX"),
               BS_AUTOCHECKBOX, TEXT ("AUTOCHECKBOX"),
               BS_RADIOBUTTON,            TEXT ("RADIOBUTTON"),
               BS_3STATE,             TEXT ("3STATE"),
               BS_AUTO3STATE,            TEXT ("AUTO3STATE"),
               BS_GROUPBOX,             TEXT ("GROUPBOX"),
               BS_AUTORADIOBUTTON, TEXT ("AUTORADIO"),
               BS_OWNERDRAW,              TEXT ("OWNERDRAW")
};
#define NUM (sizeof button / sizeof button[0])
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
               static TCHAR szAppName[] = TEXT ("BtnLook") ;
               HWND            hwnd ;
               MSG           msg ;
               WNDCLASS          wndclass ;
               wndclass.style          = CS_HREDRAW | CS_VREDRAW ;
               wndclass.lpfnWndProc = WndProc ;
               wndclass.cbClsExtra = 0 ;
               wndclass.cbWndExtra = 0 ;
               wndclass.hInstance         = hInstance ;
               wndclass.hIcon           = LoadIcon (NULL, IDI_APPLICATION) ;
               wndclass.hCursor          = LoadCursor (NULL, IDC_ARROW) ;
               wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
               wndclass.lpszMenuName = NULL ;
               wndclass.lpszClassName = szAppName ;
               if (!RegisterClass (&wndclass))
               {
                                 MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;
                                 return 0 ;
               }
            hwnd = CreateWindow (szAppName, TEXT ("Button Look"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
                         CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;

            ShowWindow (hwnd, iCmdShow) ;
            UpdateWindow (hwnd) ;

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

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            static HWND hwndButton[NUM] ;
            static RECT rect ;
            static TCHAR szTop[] = TEXT ("message       wParam   lParam"),
            szUnd[] = TEXT ("_______       ______   ______"),
            szFormat[] = TEXT ("%-16s%04X-%04X %04X-%04X"),
            szBuffer[50] ;
            static int cxChar, cyChar ;
            HDC          hdc ;
            PAINTSTRUCT ps ;
            int       i;

            switch (message)
            {
            case WM_CREATE :
                          cxChar = LOWORD (GetDialogBaseUnits ()) ;
                          cyChar = HIWORD (GetDialogBaseUnits ()) ;
                          for (i = 0 ; i < NUM ; i++)
                                            hwndButton[i] = CreateWindow ( TEXT("button"),button[i].szText,
                                            WS_CHILD | WS_VISIBLE | button[i].iStyle,cxChar, cyChar * (1 + 2 * i),
                                            20 * cxChar, 7 * cyChar / 4,hwnd, (HMENU) i,((LPCREATESTRUCT) lParam)->hInstance, NULL) ;
                          return 0 ;

            case WM_SIZE :
                         rect.left = 24 * cxChar ;
                         rect.top = 2 * cyChar ;
                         rect.right = LOWORD (lParam) ;
                         rect.bottom = HIWORD (lParam) ;
                         return 0 ;
    case WM_PAINT :
                InvalidateRect (hwnd, &rect, TRUE) ;

                  hdc = BeginPaint (hwnd, &ps) ;
                  SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
                  SetBkMode (hdc, TRANSPARENT) ;

                  TextOut (hdc, 24 * cxChar, cyChar, szTop, lstrlen (szTop)) ;
                  TextOut (hdc, 24 * cxChar, cyChar, szUnd, lstrlen (szUnd)) ;

               EndPaint (hwnd, &ps) ;
               return 0 ;
    case WM_DRAWITEM :
    case WM_COMMAND :
               ScrollWindow (hwnd, 0, -cyChar, &rect, &rect) ;

                  hdc = GetDC (hwnd) ;
                  SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;

                  TextOut (hdc, 24 * cxChar, cyChar * (rect.bottom / cyChar - 1),
                  szBuffer,
                  wsprintf (szBuffer, szFormat,
                  message == WM_DRAWITEM ? TEXT ("WM_DRAWITEM") :
                  TEXT ("WM_COMMAND"),
                  HIWORD (wParam), LOWORD (wParam),
                  HIWORD (lParam), LOWORD (lParam))) ;

                  ReleaseDC (hwnd, hdc) ;
                  ValidateRect (hwnd, &rect) ;
                  break ;
    case WM_DESTROY :
                  PostQuitMessage (0) ;
                  return 0 ;
    }
    return DefWindowProc (hwnd, message, wParam, lParam) ;
}
1. 자식 윈도우 제어
• 단추 클래스
  – class name      : “button”
  – WindowText      : button[I].szText
  – Window Style    : WS_CHILD|WS_VISIBLE|button[I]
  – x Position      : cxChar
  – y Position      : cyChar * (1 + 2 * I)
  – Width           : 20 * xChar
  – Height          : 7 * yChar / 4
  – Parent Window : hwnd
  – Child window ID : (HMENU) I
  – Instance handle : ((LPCREATSTRUCT) lParam) ->hInstance
  – Extra parameters : NULL
• 인스턴스 핸들을 얻는 방법 3가지
  – WM_CREATE시 :
      • lParam에 CREATESTRUCT형식의 구조체에 대한 포인터가 들어 있
        다.
   – 전역변수를 이용하는 방법
      • hInst = hInstance;
   – GetWindowLong(hwnd,GWL_HINSTANCE)
2. 자식 윈도우가 부모 윈도우에 메시지 보
내기자식 윈도우 컨트롤은 자신의 부모 윈도우에게 WM_COMMAND
   –
   메시지를 보낸다.
    • LOWORD(wParam)      : 자식윈도우 ID
    • HIWORD(wParam)      : 알림코드
    • lParam              : 자식윈도우의 핸들
  – 단추 알림코드의 가능한 값들
    •   BN_CLICKED                      :   0
    •   BN_PAINT                        :   1
    •   BN_HILITE or BN_PUSHED          :   2
    •   BN_UNHILITE or BN_UNPUSHED      :   3
    •   BN_DISABLE                      :   4
    •   BN_DOUBLECLICKED or BN_DBLCLK   :   5
    •   BN_SETFOCUS                     :   6
    •   BN_KILLFOCUS                    :   7
  – 알림코드 6과 7은 단추 스타일이 BS_NOTIFY를 포함하는 경우
    에만 전달된다.
3. 부모 윈도우가 자식윈도우에게 보내는 메
시지 : Button Message
• BM
 – BM_GETCHECK
 – BM_SETCHECK
    • 체크박스와 라디오 단추의 체크표시를 설정하기 위해서 보낸다.
 – BM_GETSTATE
 – BM_SETSTATE
    • 한 윈도우를 마우스로 누르거나 Space Bar를 눌렀을 때의 상태를
      의미한다.
 – BM_SETSTYLE
    • 단추가 만들어진 후 단추의 스타일을 변경할 수 있게 한다.
 – BM_CLICK
 – BM_GETIMAGE
 – BM_SETIMAGE
• 자식 윈도우의 ID를 얻기
 – Id = GetWindowLong(hwndChild,GWL_ID);
 – Id = GetDlgCtlID(hwndChild);

 – ID를 아는 상태에서 자식윈도우의 핸들 얻기
    • hwndChild = GetDlgItem(hwndParent,ID);
4. 누르기 단추
 – 마우스로 단추를 누르고 떼면 원래의 모양으로 되돌아오고 부모
   에게 WM_COMMAND메시지와 함께 알림코드로 BN_CLICKED
   를 보낸다.
 – 단추 윈도우에 BM_SETSTATE메시지를 보냄으로써 단추의 누른
   상태를 지정할 수 있다.
   • SendMessage(hwndButton,BM_SETSTATE,1,0);
 – 다음의 호출로 단추는 정상으로 돌아 온다.
   • SendMessage(hwndButton,BM_SETSTATE,0,0);
 – 누르기 단추의 상태를 알려면
   • SendMessage(hwndButton,BM_GETSTATE,0,0);
   • 단추가 눌려졌으면 TRUE를, 정상 상태이면 FALSE를 반환한다.
5. 체크 상자
 – 체크 상자의 일반적인 두 가지 유형
 – BS_CHECKBOX
   • 컨트롤에 BM_SETCHECK메시지를 전달한 후 체크 표시를 설정해
     야 한다.
   • wParam      : 체크표시 1 , 제거 0
   • BM_GETCHECK메시지를 전달하여 Check상태를 얻을 수 있다.
   • SendMessage((HWND)lParam,BM_SETCHECK,
          (WPARAM)!SendMessage((HWND)lParam,
          BM_GETCHECK,0,0),0);
 – BS_AUTOCHECKBOX
   • 자동으로 자신이 Check표시를 토글 한다.
   • iCheck =(int)SendMessage(hwndButton,BM_GETCHECK,0,0);
      – 체크되어 있으면 : TRUE
      – 체크되어 있지 않으면 : FALSE
6. 라디오 단추
 – 라디오 단추에서 WM_COMMAND를 받을 때는 wParam이 1인
   BM_SETCHECK메시지를 보내 체크를 표시한다.
   • SendMessage(hwndButton,BM_SETCHECK,1,0);
 – 같은 그룹에 있는 모든 라디오 단추에 wParam이 0인
   BM_SETCHECK메시지를 전달하여 체크 설정을 취소한다.
   • SendMessage(hwndButton,BM_SETCHECK,0,0);

• 단추 텍스트 변경하기
 – SetWindowText를 호출하면 텍스트를 변경할 수 있다.
   • SetWindowText(hwnd,pszString);
   • iLength = GetWindowText(hwnd,pszBuffer,iMaxLength)
      – iMaxLength는 복사할 문자의 최대 개수를 지정
   • iLength = GetWindowTextLength(hwnd);
7. 보이는 단추와 사용 가능한 단추
 – 자식윈도우를 만들 때 WS_VISIBLE을 포함하지 않으면
   ShowWindow를 호출할 때까지 자식 윈도우를 표시하지 않는다.
 – 윈도우를 보이게 한다.
   • ShowWindow(hwndChild,SW_SHOWNORMAL);
 – 윈도우를 숨긴다.
   • ShowWindow(hwndChild,SW_HIDE);
 – 자식윈도우가 보이는지 보이지 않는지를 알아낸다.
   • IsWindowVisible(hwndChild)
 – 자식윈도우를 사용 가능하게 하거나 불가능하게 한다.
   • EnableWindow(hwndChild,FALSE);
   • IsWindowEnabled(hwndChild);
8. 단추와 입력 포커스
 – WM_KILLFOCUS시에 wParam 매개 변수는 입력 포커스를 받는
   윈도우의 핸들이다.


 case WM_KILLFOCUS:
        for ( i = 0; i < NUM; i++)
        {
                   if (hwndChild[I] == (HWND)wParam)
                   {
                            SetFocus(hwnd);
                            break;
                   }
        }

 case WM_KILLFOCUS:
        if (hwnd == GetParetn((HWND)wParam)
        {
                  SetFocus(hwnd);
                  break;
        }
8. WM_CTLCOLORBTN메시지
 – 이 메시지는 자식 윈도우가 자신의 클라이언트 영역을 표시할 때
   단추 컨트롤이 부모 윈도우에게 보내는 메시지이다.
   • wParam   : 단추의 장치 컨텍스트에 대한 핸들
   • lParam   : 단추의 윈도우 핸들

   • SetTextColor를 사용하여 텍스트 색상을 설정한다.
   • SetBkColor를 사용하여 텍스트 배경을 설정한다.
   • 자식 윈도우에 브러쉬 핸들을 반환한다.
#include <windows.h>
#define ID_SMALLER          1
#define ID_LARGER           2
#define BTN_WIDTH           (8 * cxChar)
#define BTN_HEIGHT          (4 * cyChar)

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
HINSTANCE hInst ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
              static TCHAR szAppName[] = TEXT ("OwnDraw") ;
              MSG        msg ;
              HWND        hwnd ;
              WNDCLASS      wndclass ;

             hInst = hInstance ;

             wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
             wndclass.lpfnWndProc = WndProc ;
             wndclass.cbClsExtra = 0 ;
             wndclass.cbWndExtra = 0 ;
             wndclass.hInstance       = hInstance ;
             wndclass.hIcon          = LoadIcon (NULL, IDI_APPLICATION) ;
             wndclass.hCursor         = LoadCursor (NULL, IDC_ARROW) ;
             wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
             wndclass.lpszMenuName = szAppName ;
             wndclass.lpszClassName = szAppName ;
             if (!RegisterClass (&wndclass))
             {
                              MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;
                              return 0 ;
             }
             hwnd = CreateWindow (szAppName, TEXT ("Owner-Draw Button Demo"),WS_OVERLAPPEDWINDOW,
                              CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;

             ShowWindow (hwnd, iCmdShow) ;
             UpdateWindow (hwnd) ;

             while (GetMessage (&msg, NULL, 0, 0))
             {
                           TranslateMessage (&msg) ;
                           DispatchMessage (&msg) ;
             }
             return msg.wParam ;
}
void Triangle (HDC hdc, POINT pt[])
{
                SelectObject (hdc, GetStockObject (BLACK_BRUSH)) ;
                Polygon (hdc, pt, 3) ;
                SelectObject (hdc, GetStockObject (WHITE_BRUSH)) ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
                static HWND       hwndSmaller, hwndLarger ;
                static int   cxClient, cyClient, cxChar, cyChar ;
                int         cx, cy ;
                LPDRAWITEMSTRUCT pdis ;
                POINT          pt[3] ;
                RECT           rc ;
                switch (message)
                {
                case WM_CREATE :
                                cxChar = LOWORD (GetDialogBaseUnits ()) ;
                                cyChar = HIWORD (GetDialogBaseUnits ()) ;
                                // Create the owner-draw pushbuttons
                                hwndSmaller = CreateWindow (TEXT ("button"), TEXT (""),  WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
                                               0, 0, BTN_WIDTH, BTN_HEIGHT,hwnd, (HMENU) ID_SMALLER, hInst, NULL) ;
                                hwndLarger = CreateWindow (TEXT ("button"), TEXT (""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
                                               0, 0, BTN_WIDTH, BTN_HEIGHT, hwnd, (HMENU) ID_LARGER, hInst, NULL) ;
                                return 0 ;
                case WM_SIZE :
                                cxClient = LOWORD (lParam) ;
                                cyClient = HIWORD (lParam) ;
                                // Move the buttons to the new center
                                MoveWindow (hwndSmaller,cxClient/2-3*BTN_WIDTH/2, cyClient/2-BTN_HEIGHT/2,BTN_WIDTH,
                                BTN_HEIGHT,TRUE);
                                MoveWindow (hwndLarger,cxClient/2+BTN_WIDTH/2,cyClient/2-BTN_HEIGHT/2,BTN_WIDTH,
                                BTN_HEIGHT,TRUE);
                                return 0 ;
                case WM_COMMAND :
                                GetWindowRect (hwnd, &rc) ;
                                // Make the window 10% smaller or larger
                                switch (wParam)
                                {
                                case ID_SMALLER :
                                               rc.left += cxClient / 20 ;
                                               rc.right -= cxClient / 20 ;
                                               rc.top += cyClient / 20 ;
                                               rc.bottom -= cyClient / 20 ;
                                               break ;
            case ID_LARGER :
                         rc.left -= cxClient / 20 ;
                         rc.right += cxClient / 20 ;
                         rc.top -= cyClient / 20 ;
                         rc.bottom += cyClient / 20 ;
                         break ;
            }
            MoveWindow (hwnd, rc.left, rc.top, rc.right - rc.left,   rc.bottom - rc.top, TRUE) ;
            return 0 ;

case WM_DRAWITEM :
           pdis = (LPDRAWITEMSTRUCT) lParam ;
           // Fill area with white and frame it black
           FillRect (pdis->hDC, &pdis->rcItem,(HBRUSH) GetStockObject (WHITE_BRUSH)) ;
           FrameRect (pdis->hDC, &pdis->rcItem,(HBRUSH) GetStockObject (BLACK_BRUSH)) ;
           // Draw inward and outward black triangles
           cx = pdis->rcItem.right - pdis->rcItem.left ;
           cy = pdis->rcItem.bottom - pdis->rcItem.top ;
           switch (pdis->CtlID)
           {
           case ID_SMALLER :
                            pt[0].x = 3 * cx / 8 ; pt[0].y = 1 * cy / 8 ;
                            pt[1].x = 5 * cx / 8 ; pt[1].y = 1 * cy / 8 ;
                            pt[2].x = 4 * cx / 8 ; pt[2].y = 3 * cy / 8 ;
                            Triangle (pdis->hDC, pt) ;
                            pt[0].x = 7 * cx / 8 ; pt[0].y = 3 * cy / 8 ;
                            pt[1].x = 7 * cx / 8 ; pt[1].y = 5 * cy / 8 ;
                            pt[2].x = 5 * cx / 8 ; pt[2].y = 4 * cy / 8 ;
                            Triangle (pdis->hDC, pt) ;
                            pt[0].x = 5 * cx / 8 ; pt[0].y = 7 * cy / 8 ;
                            pt[1].x = 3 * cx / 8 ; pt[1].y = 7 * cy / 8 ;
                            pt[2].x = 4 * cx / 8 ; pt[2].y = 5 * cy / 8 ;
                            Triangle (pdis->hDC, pt) ;
                            pt[0].x = 1 * cx / 8 ; pt[0].y = 5 * cy / 8 ;
                            pt[1].x = 1 * cx / 8 ; pt[1].y = 3 * cy / 8 ;
                            pt[2].x = 3 * cx / 8 ; pt[2].y = 4 * cy / 8 ;
                            Triangle (pdis->hDC, pt) ;
                            break ;
                  case ID_LARGER :
                                   pt[0].x = 5 * cx / 8 ; pt[0].y = 3 * cy / 8 ;
                                   pt[1].x = 3 * cx / 8 ; pt[1].y = 3 * cy / 8 ;
                                   pt[2].x = 4 * cx / 8 ; pt[2].y = 1 * cy / 8 ;
                                   Triangle (pdis->hDC, pt) ;
                                   pt[0].x = 5 * cx / 8 ; pt[0].y = 5 * cy / 8 ;
                                   pt[1].x = 5 * cx / 8 ; pt[1].y = 3 * cy / 8 ;
                                   pt[2].x = 7 * cx / 8 ; pt[2].y = 4 * cy / 8 ;
                                   Triangle (pdis->hDC, pt) ;
                                   pt[0].x = 3 * cx / 8 ; pt[0].y = 5 * cy / 8 ;
                                   pt[1].x = 5 * cx / 8 ; pt[1].y = 5 * cy / 8 ;
                                   pt[2].x = 4 * cx / 8 ; pt[2].y = 7 * cy / 8 ;
                                   Triangle (pdis->hDC, pt) ;
                                   pt[0].x = 3 * cx / 8 ; pt[0].y = 3 * cy / 8 ;
                                   pt[1].x = 3 * cx / 8 ; pt[1].y = 5 * cy / 8 ;
                                   pt[2].x = 1 * cx / 8 ; pt[2].y = 4 * cy / 8 ;
                                   Triangle (pdis->hDC, pt) ;
                                   break ;
                  }
                  // Invert the rectangle if the button is selected
                  if (pdis->itemState & ODS_SELECTED)
                                   InvertRect (pdis->hDC, &pdis->rcItem) ;
                  // Draw a focus rectangle if the button has the focus
                  if (pdis->itemState & ODS_FOCUS)
                  {
                                   pdis->rcItem.left += cx / 16 ;
                                   pdis->rcItem.top += cy / 16 ;
                                   pdis->rcItem.right -= cx / 16 ;
                                   pdis->rcItem.bottom -= cy / 16 ;
                                   DrawFocusRect (pdis->hDC, &pdis->rcItem) ;
                  }
                  return 0 ;
    case WM_DESTROY :
                  PostQuitMessage (0) ;
                  return 0 ;
    }
    return DefWindowProc (hwnd, message, wParam, lParam) ;
}
9. BS_OWNERDRAW 스타일 단추
 – 단추는 다시 표시되어야 할 때마다 자신의 부모 윈도우에게
   WM_DRAWITEM메시지를 전달한다.
 – WM_DRAWITEM메시지를 처리하는 동안 lParam에는
   DRAWITEMSTRUCT구조체의 포인터를 return한다.
    •   hDC : 단추에 대한 DC
    •   rcItem : 단추 크기를 제공한느 RECT구조체
    •   CtlID : 컨트롤 윈도우ID
    •   itemState : 단추가 눌린 상태인지 또는 입력 포커스를 가지고 있는
        지를 나타낸다.ODS_SELECT,ODS_FOCUS
 – Progrmming Windows Page 451 참조
10. 정적 클래스
 – 마우스나 키보드 입력을 받지 않으며, 부모 윈도우에
   WM_COMMAND메시지를 전송하지 않는다.
 – 정적 자식 윈도우 위로 마우스를 옮기거나 클릭 할 때, 자식 윈도
   우는 WM_NCHITTEST메시지를 가로채고, HTTRANSPARENT를
   윈도우에 반환한다.
 – SS_LEFT,SS_RIGHT,SS_CENTER를 포함하여 문자열을 정렬
11. 스크롤 바 클래스
 – 스크롤 바 컨트롤은 부모 윈도우에WM_COMMAND를 보내지 않
   는 대신, 윈도우 스크롤 바처럼 WM_VSCROLL과WM_HSCROLL
   메시지를 보낸다.

 – 스크롤 바 메시지를 처리할 때,
   • lParam 매개 변수로 윈도우 스크롤 바 컨트롤과 컨트롤 스크롤 바
     사이를 구별할 수 있다.
   • 컨트롤 스크롤 바 :
      – lParam 값 : 윈도우의 핸들
      – hwndScrollBar = (HWND) lParam;
   • 윈도우 스크롤 바 :
      – lParam 값 : 0
   • wParam :
      – nScrollCode = (int)LOWORD(wParam);
      – nPos = (short int)HIWORD(wParam);
11. 스크롤 바 클래스
Value                 Description
SB_BOTTOM             Scrolls to the lower right
SB_ENDSCROLL          Ends scroll
SB_LINEDOWN           Scrolls one line down
SB_LINEUP             Scrolls one line up
SB_PAGEDOWN           Scrolls one page down
SB_PAGEUP             Scrolls one page up
SB_THUMBPOSITION      The user has dragged the scroll box (thumb) and
                      released the mouse button. The nPos parameter
                      indicates the position of the scroll box at the end
                      of the drag operation.
SB_THUMBTRACK         The user is dragging the scroll box. This message
                      is sent repeatedly until the user releases the mouse
                      button. The nPos parameter indicates the position
                      that the scroll box has been dragged to.
SB_TOP                Scrolls to the upper left

  –   SetScrollRange(hwndScroll,SB_CTL,iMin,iMax,bRedraw);
  –   SetScrollPos(hwndScroll,SB_CTL,iPos,bRedraw);
  –   SetScrollInfo(hwndScroll,SB_CTL,&si,bRedraw);
  –   윈도우 스크롤 바는
         • 첫번째 매개변수로 메인 윈도우의 핸들,
         • 두번째 매개변수로 SB_VERT,SB_HORZ를 사용
/*----------------------------------------
   COLORS1.C -- Colors Using Scroll Bars
           (c) Charles Petzold, 1998
  ----------------------------------------*/
#include <windows.h>
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
LRESULT CALLBACK ScrollProc (HWND, UINT, WPARAM, LPARAM) ;
int   idFocus ;
WNDPROC OldScroll[3] ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
                static TCHAR szAppName[] = TEXT ("Colors1") ;
                HWND        hwnd ;
                MSG        msg ;
                WNDCLASS      wndclass ;

             wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
             wndclass.lpfnWndProc = WndProc ;
             wndclass.cbClsExtra = 0 ;
             wndclass.cbWndExtra = 0 ;
             wndclass.hInstance       = hInstance ;
             wndclass.hIcon          = LoadIcon (NULL, IDI_APPLICATION) ;
             wndclass.hCursor         = LoadCursor (NULL, IDC_ARROW) ;
             wndclass.hbrBackground = CreateSolidBrush (0) ;
             wndclass.lpszMenuName = NULL ;
             wndclass.lpszClassName = szAppName ;
             if (!RegisterClass (&wndclass))
             {
                              MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;
                              return 0 ;
             }
             hwnd = CreateWindow (szAppName, TEXT ("Color Scroll"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
                              CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;
             ShowWindow (hwnd, iCmdShow) ;
             UpdateWindow (hwnd) ;
             while (GetMessage (&msg, NULL, 0, 0))
             {
                              TranslateMessage (&msg) ;
                              DispatchMessage (&msg) ;
             }
             return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
           static COLORREF crPrim[3] = { RGB (255, 0, 0), RGB (0, 255, 0),                 RGB (0, 0, 255) } ;
           static HBRUSH hBrush[3], hBrushStatic ;
           static HWND hwndScroll[3], hwndLabel[3], hwndValue[3], hwndRect ;
           static int   color[3], cyChar ;
           static RECT rcColor ;
           static TCHAR * szColorLabel[] = { TEXT ("Red"), TEXT ("Green"), TEXT ("Blue") } ;
           HINSTANCE          hInstance ;
           int        i, cxClient, cyClient ;
           TCHAR          szBuffer[10] ;
           switch (message)
           {
           case WM_CREATE :
                           hInstance = (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE) ;
                           // Create the white-rectangle window against which the
                           // scroll bars will be positioned. The child window ID is 9.
                           hwndRect = CreateWindow (TEXT ("static"), NULL,                 WS_CHILD | WS_VISIBLE | SS_WHITERECT,
                                             0, 0, 0, 0,     hwnd, (HMENU) 9, hInstance, NULL) ;
                           for (i = 0 ; i < 3 ; i++)
                           {
                                             // The three scroll bars have IDs 0, 1, and 2, with
                                             // scroll bar ranges from 0 through 255.
                                             hwndScroll[i] = CreateWindow (TEXT ("scrollbar"), NULL, WS_CHILD | WS_VISIBLE |
                                             WS_TABSTOP | SBS_VERT, 0, 0, 0, 0, hwnd, (HMENU) i, hInstance, NULL) ;
                                             SetScrollRange (hwndScroll[i], SB_CTL, 0, 255, FALSE) ;
                                             SetScrollPos (hwndScroll[i], SB_CTL, 0, FALSE) ;
                                             // The three color-name labels have IDs 3, 4, and 5,
                                             // and text strings "Red", "Green", and "Blue".
                                             hwndLabel [i] = CreateWindow (TEXT ("static"), szColorLabel[i],          WS_CHILD |
                                                             WS_VISIBLE | SS_CENTER, 0, 0, 0, 0, hwnd, (HMENU) (i + 3), hInstance, NULL) ;
                                             // The three color-value text fields have IDs 6, 7,
                                             // and 8, and initial text strings of "0".
                                             hwndValue [i] = CreateWindow (TEXT ("static"), TEXT ("0"),WS_CHILD | WS_VISIBLE |
                                                             SS_CENTER, 0, 0, 0, 0,hwnd, (HMENU) (i + 6), hInstance, NULL) ;
                                             OldScroll[i] = (WNDPROC) SetWindowLong (hwndScroll[i],
                                             GWL_WNDPROC, (LONG) ScrollProc) ;
                                             hBrush[i] = CreateSolidBrush (crPrim[i]) ;
                           }
                           hBrushStatic = CreateSolidBrush (GetSysColor (COLOR_BTNHIGHLIGHT)) ;
                           cyChar = HIWORD (GetDialogBaseUnits ()) ;
                           return 0 ;
case WM_SIZE :
             cxClient = LOWORD (lParam) ;
             cyClient = HIWORD (lParam) ;
             SetRect (&rcColor, cxClient / 2, 0, cxClient, cyClient) ;
             MoveWindow (hwndRect, 0, 0, cxClient / 2, cyClient, TRUE) ;
             for (i = 0 ; i < 3 ; i++)
             {
             MoveWindow (hwndScroll[i],(2 * i + 1) * cxClient / 14, 2 * cyChar,cxClient / 14, cyClient - 4 * cyChar, TRUE) ;
             MoveWindow (hwndLabel[i],(4 * i + 1) * cxClient / 28, cyChar / 2,cxClient / 7, cyChar, TRUE) ;
             MoveWindow (hwndValue[i],(4 * i + 1) * cxClient / 28, cyClient - 3 * cyChar / 2,cxClient / 7, cyChar, TRUE) ;
             }
             SetFocus (hwnd) ;
             return 0 ;
case WM_SETFOCUS :
             SetFocus (hwndScroll[idFocus]) ;
             return 0 ;
case WM_VSCROLL :
             i = GetWindowLong ((HWND) lParam, GWL_ID) ;
             switch (LOWORD (wParam))
             {
             case SB_PAGEDOWN :color[i] += 15 ;break;
             case SB_LINEDOWN :color[i] = min (255, color[i] + 1) ;break ;
             case SB_PAGEUP :color[i] -= 15 ;; break;
             case SB_LINEUP :color[i] = max (0, color[i] - 1) ;break ;
             case SB_TOP : color[i] = 0 ; break ;
             case SB_BOTTOM :color[i] = 255 ;break ;
             case SB_THUMBPOSITION :
             case SB_THUMBTRACK :color[i] = HIWORD (wParam) ;break ;
             default :break ;
             }
             SetScrollPos (hwndScroll[i], SB_CTL, color[i], TRUE) ;
             wsprintf (szBuffer, TEXT ("%i"), color[i]) ;
             SetWindowText (hwndValue[i], szBuffer) ;
             DeleteObject ((HBRUSH)SetClassLong (hwnd, GCL_HBRBACKGROUND, (LONG)
                               CreateSolidBrush (RGB (color[0], color[1], color[2])))) ;
             InvalidateRect (hwnd, &rcColor, TRUE) ;
             return 0 ;
case WM_CTLCOLORSCROLLBAR :
             i = GetWindowLong ((HWND) lParam, GWL_ID) ;
             return (LRESULT) hBrush[i] ;
              case WM_CTLCOLORSTATIC :
                          i = GetWindowLong ((HWND) lParam, GWL_ID) ;
                          if (i >= 3 && i <= 8) // static text controls
                          {
                                           SetTextColor ((HDC) wParam, crPrim[i % 3]) ;
                                           SetBkColor ((HDC) wParam, GetSysColor (COLOR_BTNHIGHLIGHT));
                                           return (LRESULT) hBrushStatic ;
                          }
                          break ;
              case WM_SYSCOLORCHANGE :
                          DeleteObject (hBrushStatic) ;
                          hBrushStatic = CreateSolidBrush (GetSysColor (COLOR_BTNHIGHLIGHT)) ;
                          return 0 ;
              case WM_DESTROY :
                          DeleteObject ((HBRUSH)
                          SetClassLong (hwnd, GCL_HBRBACKGROUND, (LONG)GetStockObject (WHITE_BRUSH))) ;

                             for (i = 0 ; i < 3 ; i++)
                                               DeleteObject (hBrush[i]) ;

                             DeleteObject (hBrushStatic) ;
                             PostQuitMessage (0) ;
                             return 0 ;
              }
              return DefWindowProc (hwnd, message, wParam, lParam) ;
}

LRESULT CALLBACK ScrollProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            int id = GetWindowLong (hwnd, GWL_ID) ;
            switch (message)
            {
            case WM_KEYDOWN :
                           if (wParam == VK_TAB)
                                          SetFocus (GetDlgItem (GetParent (hwnd),(id + (GetKeyState (VK_SHIFT) < 0 ? 2 : 1)) % 3)) ;
                                          break ;
            case WM_SETFOCUS :
                           idFocus = id ;
                           break ;
            }
            return CallWindowProc (OldScroll[id], hwnd, message, wParam, lParam) ;
}
12. 윈도우 서브클래싱(SubClassing)
 – 스크롤 바 컨트롤에 대한 윈도우 프로시저는 Window내부 어느
   곳인가에 있다.
 – GWL_WNDPROC를 사용하여 GetWindowLong을 호출하여 윈
   도우 프로시저에 대한 주소를 얻을 수 있다.
 – SetWindowLong을 이용하여 윈도우 프로시저를 설정할 수도 있
   다.
 – OldScroll[I] = (WNDPROC)SetWindowLong
   (hwndScroll[I],GWL_WNDPROC,(LONG) ScrollProc));
 – WM_CTLCOLORSCROLLBAR
   • 스크롤 바가 자신이 그려져야 할 때 부모 윈도우에게 이 메시지를
     보낸다.
   • hdcSB = (HDC) wParam;
   • hwndSB = (HWND) lParam;
   • 스크롤 바의 배경을 칠할 브러쉬의 핸들을 리턴한다.
/*-------------------------------------------------------
POPPAD1.C -- Popup Editor using child window edit box
(c) Charles Petzold, 1998
-------------------------------------------------------*/
#include <windows.h>
#define ID_EDIT     1
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
TCHAR szAppName[] = TEXT ("PopPad1") ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
               HWND       hwnd ;
               MSG      msg ;
               WNDCLASS wndclass ;

             wndclass.style     = CS_HREDRAW | CS_VREDRAW ;
             wndclass.lpfnWndProc = WndProc ;
             wndclass.cbClsExtra = 0 ;
             wndclass.cbWndExtra = 0 ;
             wndclass.hInstance   = hInstance ;
             wndclass.hIcon      = LoadIcon (NULL, IDI_APPLICATION) ;
             wndclass.hCursor     = LoadCursor (NULL, IDC_ARROW) ;
             wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
             wndclass.lpszMenuName = NULL ;
             wndclass.lpszClassName = szAppName ;

             if (!RegisterClass (&wndclass))
             {
                              MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;
                              return 0 ;
             }
             hwnd = CreateWindow (szAppName, szAppName,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,
                              CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;

             ShowWindow (hwnd, iCmdShow) ;
             UpdateWindow (hwnd) ;

             while (GetMessage (&msg, NULL, 0, 0))
             {
                           TranslateMessage (&msg) ;
                           DispatchMessage (&msg) ;
             }
             return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            static HWND hwndEdit ;
            switch (message)
            {
            case WM_CREATE :
                          hwndEdit = CreateWindow (TEXT ("edit"), NULL,WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
                                        WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL,0, 0, 0, 0,
                                        hwnd, (HMENU) ID_EDIT,((LPCREATESTRUCT) lParam) -> hInstance, NULL) ;
                          return 0 ;
            case WM_SETFOCUS :
                          SetFocus (hwndEdit) ;
                          return 0 ;
            case WM_SIZE :
                          MoveWindow (hwndEdit, 0, 0, LOWORD (lParam), HIWORD (lParam), TRUE) ;
                          return 0 ;
            case WM_COMMAND :
                          if (LOWORD (wParam) == ID_EDIT)
                          if (HIWORD (wParam) == EN_ERRSPACE || HIWORD (wParam) == EN_MAXTEXT)
                                        MessageBox (hwnd, TEXT ("Edit control out of space."),szAppName, MB_OK | MB_ICONSTOP) ;
                          return 0 ;
            case WM_DESTROY :
                          PostQuitMessage (0) ;
                          return 0 ;
            }
            return DefWindowProc (hwnd, message, wParam, lParam) ;
}
13. 에디트 클래스 스타일
 – 에디트 컨트롤은 부모 윈도우에 WM_COMMAND메시지를 보낸
   다.
   • LOWORD(wParam)       자식 윈도우
   • HIWORD(wParam)       알림 코드
   • lParam               자식 윈도우 핸들
 – 기본값으로 에디트 컨트롤은 하나의 줄을 갖는다.
   • 여러줄 : ES_MULTILINE
 – 자동적으로 수평 방향으로 스크롤하려면
   • ES_AUTOHSCROLL
 – 입력 포커스를 잃은 때에도 계속 하이라이트되게 하려면
   • ES_NOHIDESEL
 – 인식 코드
   •   EN_SETFOCUS : 컨트롤이 입력 포커스를 얻었다.
   •   EN_KILLFOCUS : 컨트롤이 입력 포커스를 잃었다.
   •   EN_CHANGE : 컨트롤의 내용이 변경될 것이다.
   •   EN_UPDATE : 컨트롤의 내용이 변경되었다.
   •   EN_ERRSPACE : 컨트롤의 여백이 없다.
   •   EN_MAXTEXT : 컨트롤에 삽입할 공간이 없다.
   •   EN_HSCROLL : 컨트롤의 수평 스크롤 바가 클릭 되었다.
   •   EN_VSCROLL : 컨트롤의 수직 스크롤 바가 클릭 되었다.
14. 에디트 컨트롤에 메시지를 전송하기
 – 현재 선택된 것을 자르고,복사하고,지우기
   •   SendMessage(hwndEdit,WM_CUT,0,0);
   •   SendMessage(hwndEdit,WM_COPY,0,0);
   •   SendMessage(hwndEdit,WM_CLEAR,0,0);
   •   SendMessage(hwndEdit,WM_PASTE,0,0);
 – 현재 선택의 시작과 끝을 얻을 수 있다.
   • SendMessage(hwndEdit,EM_GETSEL,(WPARAM) &iStart,
     (LPARAM) &iEND);
 – 텍스트를 선택
   • SendMEssage(hwndEdit,EM_SETSEL,iStart,iEnd);
 – 다른 텍스트를 교체
   • SendMEssage(hwndEdit,EM_REPLACESEL,0,(LPARAM)szString);
 – 멀티라인 에디트 컨트롤에서 줄의 수
   • iCount = SendMessage(hwndEdit,EM_GETLINECOUNT,0,0);
 – 줄 길이를 얻을 수 있다.
   • iLength = SendMessage(hwndEdit,EM_LINELENGTH,iLine,0);
 – 줄 자체를 버퍼에 복사
   • iLength = SendMessage(hwndEdit,EM_GETLINE,iLine,
   (LPARAM) szBuffer);
15. 목록 상자 스타일
 – 윈도우 클래스로는 listbox를 사용
 – 기본 목록 상자 스타일을 부모에게 WM_COMMAND를 보내지 않
   는다.
 – 목록 상자 스타일 LBS_NOTIFY를 사용하여, WM_COMMAND메시
   지를 부모 윈도우가 받도록 해준다.
 – 다중 목록 상자를 만들려면 LBS_MULTIPLESEL스타일을 사용한
   다.
 – 새로운 항목이 스크롤 바 목록에 추가 될 때마다 자신을 갱신한다.
   LBS_NOREDRAW스타일을 포함하여 이것을 방지
 – SendMessage를 사용하여 텍스트를 삽입
   • SendMessage(hwndlist,LB_ADDSTRING,0,(LPARAM)szString);
   • SendMessage(hwndlist,LB_INSERTSTRING,iIndex,(LPARAM)szStri
     ng);
   • SendMessage(hwndlist,LB_DELETESTRING,iIndex, 0);
 – 목록을 모두 삭제
   • SendMessage(hwndlist,LB_RESETCONTENT,0, 0);
 – 컨트롤의 다시 그리기 플래그를 끄기
   • SendMessage(hwndlist,WM_SETREDRAW,FALSE,0);
   • SendMessage(hwndlist,WM_SETREDRAW,TRUE,0);
15. 목록 상자 스타일
스타일                설명
LBS_DISABLENOSCR   리스트박스는 항목이 많으면 스크롤 바를 보여주고 스
OLL                크롤 할 항목이 없으면 스크롤 바를 숨긴다.
                   스크롤할 항목이 없어도 스크롤 바를 숨기지 않고 흐린
                   모양의 스크롤 바를 보여준다.
LBS_EXENDEDSEL     Shift키와 마우스 또는 특수한 키 조합을 사용하여 복수
                   개의 항목을 선택할 수 있도록 한다.
LBS_HASSTRING      오너 드로우 스타일이 지정된 경우 문자열도 함께 가질
                   것인가를 지정한다.
LBS_MULTIPLESEL    여러 개의 항목을 선택할 수 있도록 한다.
LBS_NOTIFY         사용자가 목록 중 하나를 선택했을 때 부모 윈도우로
                   통지 메시지를 보내도록 한다.
LBS_SORT           추가된 항목들을 자동 정렬하도록 한다.
LBS_OWNERDRAW      문자열이 아닌 비트맵이나 그림을 넣을 수 있도록 한다.

LBS_MULTICOLUMN    수평으로 여러 단을 이루어 항목을 보여주도록 한다.
                   LB_SETCOLUMNWIDTH 메시지로 각 단의 폭을 지정
LBS_STANDARD       LBS_NOTIFY|LBS_SORT|LBS_BORDER
15. 목록 상자 스타일
스타일               설명
LB_ADDSTRING      리스트 박스에 항목을 추가한다. lParam으로 추가하고
                  자 하는 문자열의 번지를 넘겨준다.
LB_DELETESTRING   항목을 삭제한다. wParam으로 항목의 번호를 넘겨주
                  며 남은 항목 수를 리턴한다.
LB_GETCURSEL      현재 선택된 항목의 번호를 리턴한다.
LB_GETTEXT        지정한 항목의 문자열을 읽는다. wParam에 항목 번
                  호,lParam에 문자열 버퍼의 번지
LB_DIR            파일 목록을 리스트 박스에 추가한다.

LB_FINDSTRING     주어진 검색식에 맞는 첫 번째 항목의 인덱스를 조사한
                  다.
LB_GETCOUNT       총 항목 개수를 조사한다.

LB_GETITEMDATA    주어진 항목의 항목 데이터를 조사한다.

LB_GETITEMRECT    주어진 항목의 작업영역 좌표를 조사한다.
15. 목록 상자 스타일
스타일               설명
LB_GETSEL         항목의 선택 상태를 조사한다.

LB_GETSELCOUNT    다중 선택 리스트 박스에서 선택된 항목의 개수를 조사

LB_GETSELITEMS    다중 선택 리스트 박스에서 선택된 항목의 인덱스를 배
                  열에 채워준다.
LB_GETTEXT        주어진 항목의 문자열을 조사한다.

LB_GETTEXTLEN     주어진 항목의 문자열의 길이를 조사한다.

LB_RESETCONTENT   모든 항목을 삭제한다.

LB_SETITEMDATA    항목 데이터를 대입한다.
15. 목록 상자 스타일
• 항목 선택 및 추출하기
 – 항목의 개수를 알아낸다.
   • iCount = SendMessage(hwndList,LB_GETCOUNT,0,0);
 – 해당 항목을 선택
   • SendMessage(hwndList,LB_SETCURSEL,iIndex,0);
 – 항목의 첫 문자에 근거하여 항목을 선택
   • iIndex = SendMessage(hwndList, LB_SELECTSTRING,iIndex,
     (LPARAM)szSearchString);
   • WPARAM으로 제공되는 iIndex는szSearchString과 부합되는 첫 문자
     를 가진 항목에 대한 검색이 시작될 때 인덱스값이다.
   • 부합되는 첫문자가 없으면 LB_ERR를 반환
 – 현재 선택의 인덱스를 알아 낸다.
   • iIndex = SendMessage(hwndList,LB_GETCURSEL,0,0);
 – 어떤 문자열의 길이 리턴
   • iLength = SendMessage(hwndList,LB_GETTEXTLEN, iIndex,0);
 – 항목을 텍스트 버퍼로 복사할 수 있다.
   • iLength = SendMessage(hwndList,LB_GETTEXT,iIndex,
     (LPARAM)szBuffer);
 – 다중 선택 목록에서는 LB_SETCURSEL, LB_GETCURSEL,
   LB_SELECTSTRING을 사용할 수 없다.
15. 목록 상자 스타일
 – 다중 선택 목록에서 특정 항목의 선택 상태를 설정하기 위해
   LB_SETSEL을 사용
   • SendMessage(hwndList,LB_SETSEL,wParam,iIndex);
   • wParam => 취소 : 0, 선택은 0이 아닌 값
 – 특정 항목의 선택 상태를 알려면 LB_GETSEL이용
   • iSelect=SendMessage(hwndList,LB_GETSEL,iIndex,0);
   • iSelect : iIndex가 항목이 선택되어 있으면 0이 아닌 값
   • 선택되어 있지 않으면 0으로 설정
 – 목록 상자는 자신의 부모에게 WM_COMMAND를 보낸다.
   • LOWORD(wParam)       : 자식 윈도우 ID
   • HIWORD(wPram)        : 알림 코드
   • LParam               : 자식 윈도우 핸들
 – 알림 코드
   • LBN_ERRSPACE :
      – 목록 상자 컨트롤이 공간이 부족하다는 것을 가리킨다.
   • LBN_SELCHANGE
      – 현재 선택이 변경되었다는 것을 가리킨다.
      – 사용자가 하이라이트 목록 상자를 통해 이동시키고 Space bar키로 선택
        상태를 토글하거나 마우스로 항목을 선택할 때 발생한다.
   • LBN_DBLCLK
      – 목록 상자 항목이 마우스로 더블 클릭 되었음을 가리킨다.
 – 윈도우 스타일이 LBS_NOTIFY일 경우에만
   BN_SELCHANGE,LBN_DBLCLK를 받을 수 있다.
/*----------------------------------------
   ENVIRON.C -- Environment List Box
           (c) Charles Petzold, 1998
  ----------------------------------------*/
#include <windows.h>
#define ID_LIST     1
#define ID_TEXT      2
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
               static TCHAR szAppName[] = TEXT ("Environ") ;
               HWND         hwnd ;
               MSG         msg ;
               WNDCLASS       wndclass ;

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

             if (!RegisterClass (&wndclass))
             {
                              MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;
                              return 0 ;
             }
             hwnd = CreateWindow (szAppName, TEXT ("Environment List Box"),WS_OVERLAPPEDWINDOW,
                              CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;
             ShowWindow (hwnd, iCmdShow) ;
             UpdateWindow (hwnd) ;
             while (GetMessage (&msg, NULL, 0, 0))
             {
                              TranslateMessage (&msg) ;
                              DispatchMessage (&msg) ;
             }
             return msg.wParam ;
}
void FillListBox (HWND hwndList)
{
                 int   iLength ;
                 TCHAR * pVarBlock, * pVarBeg, * pVarEnd, * pVarName ;
                 pVarBlock = GetEnvironmentStrings () ; // Get pointer to environment block
                 while (*pVarBlock)
                 {
                                 if (*pVarBlock != '=') // Skip variable names beginning with '='
                                 {
                                                 pVarBeg = pVarBlock ;              // Beginning of variable name
                                                 while (*pVarBlock++ != '=') ;      // Scan until '='
                                                 pVarEnd = pVarBlock - 1 ;           // Points to '=' sign
                                                 iLength = pVarEnd - pVarBeg ;         // Length of variable name
                                                 // Allocate memory for the variable name and terminating
                                                 // zero. Copy the variable name and append a zero.
                                                 pVarName = calloc (iLength + 1, sizeof (TCHAR)) ;
                                                 CopyMemory (pVarName, pVarBeg, iLength * sizeof (TCHAR)) ;
                                                 pVarName[iLength] = '\0' ;
                                                 // Put the variable name in the list box and free memory.
                                                 SendMessage (hwndList, LB_ADDSTRING, 0, (LPARAM) pVarName) ;
                                                 free (pVarName) ;
                                 }
                                 while (*pVarBlock++ != '\0') ;      // Scan until terminating zero
                 }
                 FreeEnvironmentStrings (pVarBlock) ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)
{
            static HWND hwndList, hwndText ;
            int       iIndex, iLength, cxChar, cyChar ;
            TCHAR        * pVarName, * pVarValue ;
            switch (message)
            {
            case WM_CREATE :
                             cxChar = LOWORD (GetDialogBaseUnits ()) ;
                             cyChar = HIWORD (GetDialogBaseUnits ()) ;
                             // Create listbox and static text windows.
                             hwndList = CreateWindow (TEXT ("listbox"), NULL,WS_CHILD | WS_VISIBLE | LBS_STANDARD,
                                             cxChar, cyChar * 3,cxChar * 16 + GetSystemMetrics (SM_CXVSCROLL),cyChar * 5,
                                             hwnd, (HMENU) ID_LIST,(HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE),NULL) ;
                             hwndText = CreateWindow (TEXT ("static"), NULL,WS_CHILD | WS_VISIBLE | SS_LEFT,
                                             cxChar, cyChar, GetSystemMetrics (SM_CXSCREEN), cyChar,hwnd, (HMENU) ID_TEXT,
                                             (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE),NULL) ;
                             FillListBox (hwndList) ;
                             return 0 ;
            case WM_SETFOCUS :
                             SetFocus (hwndList) ;
                             return 0 ;
            case WM_COMMAND :
                             if (LOWORD (wParam) == ID_LIST && HIWORD (wParam) == LBN_SELCHANGE)
                             {
                                             iIndex = SendMessage (hwndList, LB_GETCURSEL, 0, 0) ;
                                             iLength = SendMessage (hwndList, LB_GETTEXTLEN, iIndex, 0) + 1 ;
                                             pVarName = calloc (iLength, sizeof (TCHAR)) ;
                                             SendMessage (hwndList, LB_GETTEXT, iIndex, (LPARAM) pVarName) ;
                                             iLength = GetEnvironmentVariable (pVarName, NULL, 0) ;
                                             pVarValue = calloc (iLength, sizeof (TCHAR)) ;
                                             GetEnvironmentVariable (pVarName, pVarValue, iLength) ;
                                             SetWindowText (hwndText, pVarValue) ;
                                             free (pVarName) ;
                                             free (pVarValue) ;
                             }
                             return 0 ;
            case WM_DESTROY :
                             PostQuitMessage (0) ;
                             return 0 ;
            }
            return DefWindowProc (hwnd, message, wParam, lParam) ;
}
16. 파일 나열하기
 – LB_DIR
    • 목록 상자를 파일 디렉토리 리스트와 옵션으로 서브 디렉토리와 유효
      한 디스크 드라이브로 채운다.
    • SendMessage(hwndList,LB_DIR,iAttr,(LPARAM)szFileSpec);
    • iAttr
       –   파일 속성 코드이다.
       –   DDL_READWRITE : 일반 파일
       –   DDL_READONLY : 읽기 전용
       –   DDL_HIDDEN : 숨김
       –   DDL_SYSTEM : 시스템
       –   DDL_DIRECTORY : 하위 디렉토리
       –   DDL_ARCHIVE : 보관 bit가 설정된 파일
       –   DDL_DRIVES : 드라이브 문자 포함
       –   DDL_EXECLUSIVE : 독점 검색 전용
    • lParam은 “*.*”같은 파일 지정 문자열에 대한 포인터
/*---------------------------------------------
   HEAD.C -- Displays beginning (head) of file
         (c) Charles Petzold, 1998
  ---------------------------------------------*/
#include <windows.h>
#define ID_LIST      1
#define ID_TEXT       2
#define MAXREAD          8192
#define DIRATTR        (DDL_READWRITE | DDL_READONLY | DDL_HIDDEN | DDL_SYSTEM | DDL_DIRECTORY | DDL_ARCHIVE | DDL_DRIVES)
#define DTFLAGS         (DT_WORDBREAK | DT_EXPANDTABS | DT_NOCLIP | DT_NOPREFIX)
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
LRESULT CALLBACK ListProc (HWND, UINT, WPARAM, LPARAM) ;
WNDPROC OldList ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
               static TCHAR szAppName[] = TEXT ("head") ;
               HWND            hwnd ;
               MSG            msg ;
               WNDCLASS          wndclass ;
               wndclass.style          = CS_HREDRAW | CS_VREDRAW ;
               wndclass.lpfnWndProc = WndProc ;
               wndclass.cbClsExtra = 0 ;
               wndclass.cbWndExtra = 0 ;
               wndclass.hInstance        = hInstance ;
               wndclass.hIcon           = LoadIcon (NULL, IDI_APPLICATION) ;
               wndclass.hCursor          = LoadCursor (NULL, IDC_ARROW) ;
               wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1) ;
               wndclass.lpszMenuName = NULL ;
               wndclass.lpszClassName = szAppName ;
               if (!RegisterClass (&wndclass))
               {
                                 MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;
                                 return 0 ;
               }
               hwnd = CreateWindow (szAppName, TEXT ("head"), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                                 CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;
               ShowWindow (hwnd, iCmdShow) ;
               UpdateWindow (hwnd) ;
               while (GetMessage (&msg, NULL, 0, 0))
               {
                                 TranslateMessage (&msg) ;
                                 DispatchMessage (&msg) ;
               }
               return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            static BOOL      bValidFile ;
            static BYTE     buffer[MAXREAD] ;
            static HWND      hwndList, hwndText ;
            static RECT     rect ;
            static TCHAR szFile[MAX_PATH + 1] ;
            HANDLE           hFile ;
            HDC            hdc ;
            int         i, cxChar, cyChar ;
            PAINTSTRUCT        ps ;
            TCHAR           szBuffer[MAX_PATH + 1] ;
            switch (message)
            {
            case WM_CREATE :
                            cxChar = LOWORD (GetDialogBaseUnits ()) ;
                            cyChar = HIWORD (GetDialogBaseUnits ()) ;
                            rect.left = 20 * cxChar ;
                            rect.top = 3 * cyChar ;
                            hwndList = CreateWindow (TEXT ("listbox"), NULL,WS_CHILDWINDOW | WS_VISIBLE | LBS_STANDARD,
                                             cxChar, cyChar * 3,cxChar * 13 + GetSystemMetrics (SM_CXVSCROLL),cyChar * 10,hwnd,
                                             (HMENU) ID_LIST,(HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE),NULL) ;
                            GetCurrentDirectory (MAX_PATH + 1, szBuffer) ;
                            hwndText = CreateWindow (TEXT ("static"), szBuffer,WS_CHILDWINDOW | WS_VISIBLE | SS_LEFT,
                                             cxChar, cyChar, cxChar * MAX_PATH, cyChar,hwnd, (HMENU) ID_TEXT,
                                             (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE),NULL) ;
                            OldList = (WNDPROC) SetWindowLong (hwndList, GWL_WNDPROC,(LPARAM) ListProc) ;
                            SendMessage (hwndList, LB_DIR, DIRATTR, (LPARAM) TEXT ("*.*")) ;
                            return 0 ;
            case WM_SIZE :
                            rect.right = LOWORD (lParam) ;
                            rect.bottom = HIWORD (lParam) ;
                            return 0 ;
            case WM_SETFOCUS :
                            SetFocus (hwndList) ;
                            return 0 ;
case WM_COMMAND :
           if (LOWORD (wParam) == ID_LIST && HIWORD (wParam) == LBN_DBLCLK)
           {
                        if (LB_ERR == (i = SendMessage (hwndList, LB_GETCURSEL, 0, 0)))
                                        break ;
                        SendMessage (hwndList, LB_GETTEXT, i, (LPARAM) szBuffer) ;
                        if (INVALID_HANDLE_VALUE != (hFile = CreateFile (szBuffer, GENERIC_READ,
                                        FILE_SHARE_READ, NULL,OPEN_EXISTING, 0, NULL)))
                        {
                                        CloseHandle (hFile) ;
                                        bValidFile = TRUE ;
                                        lstrcpy (szFile, szBuffer) ;
                                        GetCurrentDirectory (MAX_PATH + 1, szBuffer) ;
                                        if (szBuffer [lstrlen (szBuffer) - 1] != '\\')
                                                          lstrcat (szBuffer, TEXT ("\\")) ;
                                                          SetWindowText (hwndText, lstrcat (szBuffer, szFile)) ;
                        }
                        else
                        {
                                        bValidFile = FALSE ;
                                        szBuffer [lstrlen (szBuffer) - 1] = '\0' ;
                                        // If setting the directory doesn't work, maybe it's
                                        // a drive change, so try that.
                                        if (!SetCurrentDirectory (szBuffer + 1))
                                        {
                                                          szBuffer [3] = ':' ;
                                                          szBuffer [4] = '\0' ;
                                                          SetCurrentDirectory (szBuffer + 2) ;
                                        }
                                        // Get the new directory name and fill the list box.
                                        GetCurrentDirectory (MAX_PATH + 1, szBuffer) ;
                                        SetWindowText (hwndText, szBuffer) ;
                                        SendMessage (hwndList, LB_RESETCONTENT, 0, 0) ;
                                        SendMessage (hwndList, LB_DIR, DIRATTR,(LPARAM) TEXT ("*.*")) ;
                        }
                        InvalidateRect (hwnd, NULL, TRUE) ;
           }
           return 0 ;
             case WM_PAINT :
                         if (!bValidFile)
                                           break ;
                           if (INVALID_HANDLE_VALUE == (hFile = CreateFile (szFile, GENERIC_READ,
                                           FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)))
                           {
                                           bValidFile = FALSE ;
                                           break ;
                           }
                           ReadFile (hFile, buffer, MAXREAD, &i, NULL) ;
                           CloseHandle (hFile) ;
                           hdc = BeginPaint (hwnd, &ps) ;
                           SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
                           SetTextColor (hdc, GetSysColor (COLOR_BTNTEXT)) ;
                           SetBkColor (hdc, GetSysColor (COLOR_BTNFACE)) ;
                           // Assume the file is ASCII
                           DrawTextA (hdc, buffer, i, &rect, DTFLAGS) ;
                           EndPaint (hwnd, &ps) ;
                           return 0 ;
             case WM_DESTROY :
                           PostQuitMessage (0) ;
                           return 0 ;
             }
             return DefWindowProc (hwnd, message, wParam, lParam) ;
}

LRESULT CALLBACK ListProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            if (message == WM_KEYDOWN && wParam == VK_RETURN)
                           SendMessage (GetParent (hwnd), WM_COMMAND, MAKELONG (1, LBN_DBLCLK), (LPARAM) hwnd) ;
            return CallWindowProc (OldList, hwnd, message, wParam, lParam) ;
}
 17. OWNERDRAW
     – 오너 드로우에 사용되는 메시지에는 두 가지가 있다.
     – WM_MEASUREITEM
        • 이 메시지는 리스트 박스의 항목이 그려지기 전에 항목의 크기를 부모
          윈도우에게 물어보기 위해 사용한다.


typedef struct MEASUREITEMSTRUCT
{
        UINT           CtlType;
        UINT           CtlID;
        UINT           itemID;
        UINT           itemWidth;
        UINT           itemHeight;
        ULONG_PTR      itemData;
} MEASUREITEMSTRUCT
17. OWNERDRAW
CtlType      컨트롤의 타입, ODT_BUTTON, ODT_COMBOBOX,
             ODT_LISTBOX,ODT_LISTVIEW, ODT_MENU, ODT_STATIC
CtlID        컨트롤의 ID
itemID       메뉴 항목의 ID, 또는 리스트 박스나 콤보 박스의 항목 인덱스
itemWidth    항목의 폭을 지정하는 멤버. 부모 윈도우는 이 멤버에 항목의 폭
             을 대입해 주어야 한다.
itemHeight   항목의 높이를 지정하는 멤버. 부모 윈도우는 이 멤버에 항목의
             높이를 대입해 주어야 한다.
itemData     메뉴나 리스트 박스의 각 항목에 저장된 항목 데이터이다.
             LB_HASSTRINGS스타일을 가진 경우 이 값은
             LB_SETITEMDATA에 의해 기억된 값이지만 그렇지 않은 경우에
             는 LB_ADDSTRING메시지의 lParam을 통해 전달된 값이다.

   – 이 메시지를 처리했으면 반드시 TRUE를 리턴해 주어야 한다.
 17. OWNERDRAW
     – WM_DRAWITEM
         • 이 메시지의 wParam으로는 컨트롤의 ID가 전달되며, lParam에는
           DRAWITEMSTRUCT의 포인터가 전달된다.


typedef struct tagDRAWITEMSTRUCT
{
        UINT            CtlType;
        UINT            CtlID;
        UINT            itemID;
        UINT            itemAction;
        UINT            itemState;
        HWND            hwndItem;
        HDC             hDC;
        RECT            rcItem;
        ULONG_PTR       itemData;
} DRAWITEMSTRUCT;
17. OWNERDRAW
CtlType      컨트롤의 타입, ODT_BUTTON, ODT_COMBOBOX,
             ODT_LISTBOX,ODT_LISTVIEW, ODT_MENU, ODT_STATIC
CtlID        컨트롤의 ID
itemID       메뉴 항목의 ID, 또는 리스트 박스나 콤보 박스의 항목 인덱스
             리스트 박스나 콤보 박스가 비어 있을 때는 -1이 전달된다.
itemAction   어떤 처리가 필요한지를 지정한다. ODA_DRAWENTIRE(전체 항
             목을 다 그려야 한다.), ODA_FOCUS(포커스를 그려야 한다.),
             ODA_SELECT(선택이 변경되었다.)의 값을 가지게 된다.
itemState    항목의 현재 상태를 나타낸다.
hwndItem     컨트롤의 윈도우 핸들이다.
hDC          그리기에 사용할 DC이다.
rcItem       항목이 그려져야 할 사각형이다.
itemData     메뉴나 리스트 박스의 각 항목에 기억된 항목 데이터이다.
17. OWNERDRAW
ODS_CHECKED    항목이 체크되었다. ( 메뉴에서만 사용 가능 )
ODS_COMBOBOX   콤보 박스의 에디트에서 그리기가 발생했다.
EDIT
ODS_DEFAULT    디폴트 항목이다.
ODS_DISABLE    항목이 디스에이블되었다.
ODS_FOCUS      항목이 포커스를 가졌다.
ODS_GRAYED     항목이 사용금지되었다. (메뉴에서만 사용 가능)
ODS_SELECTED   항목이 선택되었다.
       9.
메뉴와 기타 자원
1. 자원
 – 아이콘,커서,메뉴,대화상자는 모두 Windows의 자원이다.
 – 자원은 데이터로 생각할 수 있으며, 프로그램의 EXE파일에 저장
   된다.
 – 실행 가능한 프로그램의 데이터 영역에는 존재하지 않는다.
     • 자원은 프로그램 소스코드에 정의된 변수를 이용하여 즉시 액세스될
       수 없다.
     • Windows가 자원을 메모리에 올려 사용할 수 있게 하는 함수를 제공
       한다. ( LoadIcon, LoadCursor)
 – 아이콘을 동적으로 변경
     • SetClassLong(hwnd,GCL_HICON,LoadIcon(hInstance,
       MAKEINTRESOURCE(IDI_LTICON)));
 – 아이콘 표시
     • DrawIcon(hdc,x,y,GetClassLong(hwnd,GCL_HICON));


 case WM_LBUTTONDOWN :
      SetClassLong(hwnd,GCL_HICON,(long)LoadIcon(hInstance,
      MAKEINTRESOURCE(IDI_NEWICON)));
      return 0;
#include <windows.h>
#include "resource.h"
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
               TCHAR szAppName[] = TEXT ("IconDemo") ;
               HWND       hwnd ;
               MSG       msg ;
               WNDCLASS wndclass ;
               wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
               wndclass.lpfnWndProc = WndProc ;
               wndclass.cbClsExtra = 0 ;
               wndclass.cbWndExtra = 0 ;
               wndclass.hInstance       = hInstance ;
               wndclass.hIcon          = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON)) ;
               wndclass.hCursor         = LoadCursor (NULL, IDC_ARROW) ;
               wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
               wndclass.lpszMenuName = NULL ;
               wndclass.lpszClassName = szAppName ;
               if (!RegisterClass (&wndclass))
               {
                                MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;
                                return 0 ;
               }
               hwnd = CreateWindow (szAppName, TEXT ("Icon Demo"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
                                CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;
               ShowWindow (hwnd, iCmdShow) ;
               UpdateWindow (hwnd) ;
               while (GetMessage (&msg, NULL, 0, 0))
               {
                                TranslateMessage (&msg) ;
                                DispatchMessage (&msg) ;
               }
               return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            static HICON hIcon ;
            static int cxIcon, cyIcon, cxClient, cyClient ;
            HDC          hdc ;
            HINSTANCE hInstance ;
            PAINTSTRUCT ps ;
            int       x, y ;
            switch (message)
            {
            case WM_CREATE :
                             hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;
                             hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON)) ;
                             cxIcon = GetSystemMetrics (SM_CXICON) ;
                             cyIcon = GetSystemMetrics (SM_CYICON) ;
                             return 0 ;
            case WM_SIZE :
                             cxClient = LOWORD (lParam) ;
                             cyClient = HIWORD (lParam) ;
                             return 0 ;
            case WM_PAINT :
                             hdc = BeginPaint (hwnd, &ps) ;
                             for (y = 0 ; y < cyClient ; y += cyIcon)
                                              for (x = 0 ; x < cxClient ; x += cxIcon)
                                                               DrawIcon (hdc, x, y, hIcon) ;
                             EndPaint (hwnd, &ps) ;
                             return 0 ;
            case WM_DESTROY :
                             PostQuitMessage (0) ;
                             return 0 ;
            }
            return DefWindowProc (hwnd, message, wParam, lParam) ;
}
2. 사용자 정의 커서 사용하기
 – Wndclass.hCursor = LoadCursor(hInstance,
   MAKEINTRESOURCE(IDC_CURSOR));
 – SetClassLong(hwndChild,GCL_HCURSOR,LoadCursor(hInstanc
   e, MAKEINTRESOURCE(IDC_CURSOR));
 – SetCursor(hCursor);

• 문자열 사용
 – LoadString(hInstance,id,szBuffer,iMaxLength);

• 프로그램에 메뉴 참조하기
 – 사용자가 메뉴 항목을 선택할 때 WM_COMMAND메시지를 보낸다.
 – Wndclass.lpszMenuName =szAppName;
 – CreateWindow의 9번째 인자가 NULL인 경우는 윈도우 클래스에
   기반을 둔 메뉴를 사용한다.
 – hMenu = LoadMenu(hInstance,
   MAKEINTRESOURCE(ID_MENU));
 – SetMenu(hwnd,hMenu)
    • 동적으로 메뉴를 바꿀 수 있다
3. 메뉴와 메시지
 – WM_INITMENU
   •   wParam : 메인 메뉴의 핸들
   •   lParam : 0
   •   항목이 선택되기 전에 발생한다.
   •   메뉴를 변경하기 위해서 사용
 – WM_MENUSELECT
   • LOWORD(wParam) : 선택된 항목
        – 메뉴 ID나 팝업 메뉴 인덱스
   • HIWORD(wPram) : 선택 플래그
        – MF_GRAYED,MF_DISABLED,MF_CHECKED,MF_BITMAP,MF_POPUP,
          MF_HELP,MF_SYSMENU,MF_MOUSESELECT의 조합
   • lParam : 선택된 항목을 포함하는 메뉴에 대한 핸들
   • 대부분 DefWindowProc에 전달
 – Windows는 팝업 메뉴를 표시할 준비가 되었을 때,
   WM_INITMENUPOPUP메시지를 윈도우 프로시저에 보낸다.
   •   wParam : 팝업 메뉴 핸들
   •   LOWORD(lParam) : 팝업 인덱스
   •   HIWORD(lParam) : 시스템 메뉴 : 1, 그 이외 0
   •   항목을 표시하기 전에 활성, 비활성 상태로 표시하기 위해서 중요하다.
 – WM_COMMAND
   • LOWORD(wParam) : 메뉴 ID
   • HIWORD(wParam) : 0
   • lParam         : 0
/*-----------------------------------------
   MENUDEMO.C -- Menu Demonstration
            (c) Charles Petzold, 1998
  -----------------------------------------*/
#include <windows.h>
#include "resource.h"
#define ID_TIMER 1
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
TCHAR szAppName[] = TEXT ("MenuDemo") ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
               HWND       hwnd ;
               MSG       msg ;
               WNDCLASS wndclass ;
               wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
               wndclass.lpfnWndProc = WndProc ;
               wndclass.cbClsExtra = 0 ;
               wndclass.cbWndExtra = 0 ;
               wndclass.hInstance       = hInstance ;
               wndclass.hIcon          = LoadIcon (NULL, IDI_APPLICATION) ;
               wndclass.hCursor         = LoadCursor (NULL, IDC_ARROW) ;
               wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
               wndclass.lpszMenuName = szAppName ;
               wndclass.lpszClassName = szAppName ;
               if (!RegisterClass (&wndclass))
               {
                                MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;
                                return 0 ;
               }
               hwnd = CreateWindow (szAppName, TEXT ("Menu Demonstration"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
                                CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;
               ShowWindow (hwnd, iCmdShow) ;
               UpdateWindow (hwnd) ;
               while (GetMessage (&msg, NULL, 0, 0))
               {
                                TranslateMessage (&msg) ;
                                DispatchMessage (&msg) ;
               }
               return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            static int idColor [5] = { WHITE_BRUSH, LTGRAY_BRUSH, GRAY_BRUSH,DKGRAY_BRUSH, BLACK_BRUSH } ;
            static int iSelection = IDM_BKGND_WHITE ;
            HMENU         hMenu ;
            switch (message)
            {
            case WM_COMMAND:
                             hMenu = GetMenu (hwnd) ;
                             switch (LOWORD (wParam))
                             {
                             case IDM_FILE_NEW:
                             case IDM_FILE_OPEN:
                             case IDM_FILE_SAVE:
                             case IDM_FILE_SAVE_AS:
                                           MessageBeep (0) ;
                                           return 0 ;
                             case IDM_APP_EXIT:
                                           SendMessage (hwnd, WM_CLOSE, 0, 0) ;
                                           return 0 ;
                             case IDM_EDIT_UNDO:
                             case IDM_EDIT_CUT:
                             case IDM_EDIT_COPY:
                             case IDM_EDIT_PASTE:
                             case IDM_EDIT_CLEAR:
                                           MessageBeep (0) ;
                                           return 0 ;
                             case IDM_BKGND_WHITE:           // Note: Logic below
                             case IDM_BKGND_LTGRAY:           // assumes that IDM_WHITE
                             case IDM_BKGND_GRAY:            // through IDM_BLACK are
                             case IDM_BKGND_DKGRAY:            // consecutive numbers in
                             case IDM_BKGND_BLACK:           // the order shown here.
                                           CheckMenuItem (hMenu, iSelection, MF_UNCHECKED) ;
                                           iSelection = LOWORD (wParam) ;
                                           CheckMenuItem (hMenu, iSelection, MF_CHECKED) ;
                                           SetClassLong (hwnd, GCL_HBRBACKGROUND,
                                                           (LONG)GetStockObject(idColor [LOWORD (wParam) - IDM_BKGND_WHITE])) ;
                                           InvalidateRect (hwnd, NULL, TRUE) ;
                                           return 0 ;
                  case IDM_TIMER_START:
                                if (SetTimer (hwnd, ID_TIMER, 1000, NULL))
                                {
                                                EnableMenuItem (hMenu, IDM_TIMER_START, MF_GRAYED) ;
                                                EnableMenuItem (hMenu, IDM_TIMER_STOP, MF_ENABLED) ;
                                }
                                return 0 ;
                  case IDM_TIMER_STOP:
                                KillTimer (hwnd, ID_TIMER) ;
                                EnableMenuItem (hMenu, IDM_TIMER_START, MF_ENABLED) ;
                                EnableMenuItem (hMenu, IDM_TIMER_STOP, MF_GRAYED) ;
                                return 0 ;
                  case IDM_APP_HELP:
                                MessageBox (hwnd, TEXT ("Help not yet implemented!"),
                                szAppName, MB_ICONEXCLAMATION | MB_OK) ;
                                return 0 ;
                  case IDM_APP_ABOUT:
                                MessageBox (hwnd, TEXT ("Menu Demonstration Program\n"),
                                TEXT ("(c) Charles Petzold, 1998"),szAppName, MB_ICONINFORMATION | MB_OK) ;
                                return 0 ;
                  }
                  break ;
    case WM_TIMER:
                  MessageBeep (0) ;
                  return 0 ;
    case WM_DESTROY:
                  PostQuitMessage (0) ;
                  return 0 ;
    }
    return DefWindowProc (hwnd, message, wParam, lParam) ;
}
MENUDEMO MENU DISCARDABLE
BEGIN
  POPUP "&File"
  BEGIN
     MENUITEM "&New",                 ID_MENUITEM40020
     MENUITEM "&Open",                IDM_FILE_OPEN
     MENUITEM "&Save",                IDM_FILE_SAVE
     MENUITEM "Save &As...",          IDM_FILE_SAVE_AS
     MENUITEM SEPARATOR
     MENUITEM "E&xit",                IDM_APP_EXIT
  END
  POPUP "&Edit"
  BEGIN
     MENUITEM "&Undo",                IDM_EDIT_UNDO
     MENUITEM SEPARATOR
     MENUITEM "C&ut",                 IDM_EDIT_CUT
     MENUITEM "&Copy",                IDM_EDIT_COPY
     MENUITEM "&Paste",               IDM_EDIT_PASTE
     MENUITEM "De&lete",              IDM_EDIT_CLEAR
  END
  POPUP "&Background"
  BEGIN
     MENUITEM "&White",               IDM_BKGND_WHITE, CHECKED
     MENUITEM "&Light Gray",          IDM_BKGND_LTGRAY
     MENUITEM "&Gray",                IDM_BKGND_GRAY
     MENUITEM "&Dark Gray",           IDM_BKGND_DKGRAY
     MENUITEM "&Black",               IDM_BKGND_BLACK
  END
  POPUP "&Timer"
  BEGIN
     MENUITEM "&Start",               IDM_TIMER_START
     MENUITEM "S&top",                IDM_TIMER_STOP, GRAYED
  END
  POPUP "&Help"
  BEGIN
     MENUITEM "&Help...",              IDM_APP_HELP
     MENUITEM "&About MenuDemo...",   IDM_APP_ABOUT
  END
END
3. 메뉴와 메시지
  – 메뉴의 핸들 얻기 : hMenu = GetMenu(hwnd);
  – 체크하지 않기 :
     • CheckMenuItem(hMenu,iSelection,MF_UNCHECKED);
  – 체크하기
     • iSelection = wParam;
     • CheckMenuItem(hMenu,iSelection,MF_CHECKED);
  – Disable & Grayed Menu만들기
     • EnableMenuItem(hMenu,IDM_TIMER_START,MF_GRAYED);
  – Disable Menu만들기
     • EnableMenuItem(hMenu,IDM_TIMER_START, MF_DISABLED);
  – Enable Menu만들기
     • EnableMenuItem(hMenu,IDM_TIMER_START, MF_ENABLED);
• TrackPopupMenu
  – WM_CREATE:
     • hMenu = LoadMenu(hInst,szAppName);
     • hMenu = GetSubMenu(hMenu,0);
  – WM_RBUTTON:
     •   Point.x = LOWORD(lParam);
     •   Point.y = HIWORD(lParam);
     •   ClientToScreen(hwnd,&Point);
     •   TrackPopupMenu(hMenu,TPM_RIGHTBUTTON,Point.x,Point.y,0,h
         wnd,NULL);
4. 시스템 메뉴 사용하기
 – 시스템 메뉴의 핸들을 얻는다.
    • hMenu = GetSystemMenu(hwnd,FALSE);
    • FALSE : 시스템 메뉴를 수정
    • TRUE : 추가된 항목을 제거
 – AppendMenu
    • 메뉴의 끝에 새 항목을 추가한다.
 – DeleteMenu
    • 메뉴에서 기존의 항목을 떼어 내고 완전히 없앤다.
 – InsertMenu
    • 메뉴에 새 항목을 집어 넣는다.
 – ModifyMenu
    • 기존의 메뉴항목을 수정한다.
 – RemoveMenu
    • 메뉴에서 기존의 항목을 떼어낸다.
 – DrawMenuBar(hwnd)
    • 상위 메뉴 항목을 변경했을 때, 그 변화는 Windows가 메뉴를 그릴 때
      까지 나타나지 않는다. 다시 메뉴를 그리는 명령
 – hMenuPopup = GetSubMenu(hMenu,iPosition);
    • 팝업 메뉴에 대한 핸들을 리턴
5. 기타 메뉴 명령
 – 상위나 팝업 메뉴에 있는 항목의 수를 리턴
    • iCount = GetMenuItem(hMenu);
 – 팝업 메뉴에 있는 항목의 메뉴ID를 리턴
    • Id = GetMenuID(hMenuPopup,iPosition);
 – 체크 설정 및 취소
    • CheckMenuItem(hMenu,id,iCheck);
    • Id를 Position으로 사용하려면
        – CheckMenuItem(hMenu,iPosition,MF_CHECKED|MF_BYPOSITION);
 – iCharCount = GetMenuString(hMneu,id,pString, iMaxCount,
   iFlag);
    • iFlag :
        – MF_BYCOMMAND : id가 메뉴 ID
        – MF_BYPOSITION : id가 인덱스 위치
 – iFlags = GetMenuState(hMenu,id,iFlag)
    • iFlag :
        – MF_BYCOMMAND : id가 메뉴 ID
        – MF_BYPOSITION : id가 인덱스 위치
    • 리턴 값 :
        – MF_DISABLED, MF_GRAYED, MF_CHECKED, MF_MENUBREAK,
          MF_MENUBARBREAK, MF_SEPARATOR
 – SetMenu(hwnd,hMenuMain);
    • 메뉴를 변경한다.
6. 키보드 액셀러레이터
 – WM_COMMAND메시지를 발생시키는 키의 조합이다.
 – 액셀러레이터 테이블 로드
HANDLE hAccel;
hAccel = LoadAccelerators(hInstance,TEXT("MyAccelerators"));

while(GetMessage(&msg,NULL,0,0))
{
        if (!TranslateAccelerator(hwnd,hAccel,&msg))
        {
                  TranslateMessage(&msg);
                  DispatchMessage(&msg);
        }
}
/*-----------------------------------------------------
POPPAD2.C -- Popup Editor Version 2 (includes menu)
(c) Charles Petzold, 1998
-----------------------------------------------------*/
#include <windows.h>
#include "resource.h"
#define ID_EDIT      1
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
TCHAR szAppName[] = TEXT ("PopPad2") ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
               HACCEL hAccel ;
               HWND       hwnd ;
               MSG       msg ;
               WNDCLASS wndclass ;
               wndclass.style          = CS_HREDRAW | CS_VREDRAW ;
               wndclass.lpfnWndProc = WndProc ;
               wndclass.cbClsExtra = 0 ;
               wndclass.cbWndExtra = 0 ;
               wndclass.hInstance        = hInstance ;
               wndclass.hIcon           = LoadIcon (hInstance, szAppName) ;
               wndclass.hCursor          = LoadCursor (NULL, IDC_ARROW) ;
               wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
               wndclass.lpszMenuName = szAppName ;
               wndclass.lpszClassName = szAppName ;
               if (!RegisterClass (&wndclass))
               {
                                MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;
                                return 0 ;
               }
               hwnd = CreateWindow (szAppName, szAppName,WS_OVERLAPPEDWINDOW,GetSystemMetrics (SM_CXSCREEN) / 4,
GetSystemMetrics (SM_CYSCREEN) / 4,              GetSystemMetrics (SM_CXSCREEN) / 2,    GetSystemMetrics (SM_CYSCREEN) / 2,
                                NULL, NULL, hInstance, NULL) ;
               ShowWindow (hwnd, iCmdShow) ;
               UpdateWindow (hwnd) ;
               hAccel = LoadAccelerators (hInstance, szAppName) ;
               while (GetMessage (&msg, NULL, 0, 0))
               {
                                if (!TranslateAccelerator (hwnd, hAccel, &msg))
                                {
                                                 TranslateMessage (&msg) ;
                                                 DispatchMessage (&msg) ;
                                }
               }
               return msg.wParam ;
AskConfirmation (HWND hwnd)
{
              return MessageBox (hwnd, TEXT ("Really want to close PopPad2?"),szAppName, MB_YESNO | MB_ICONQUESTION) ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            static HWND hwndEdit ;
            int      iSelect, iEnable ;
            switch (message)
            {
            case WM_CREATE:
                            hwndEdit = CreateWindow (TEXT ("edit"), NULL,WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
                                            WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0, 0, 0, 0,
                                            hwnd, (HMENU) ID_EDIT,((LPCREATESTRUCT) lParam)->hInstance, NULL) ;
                            return 0 ;
            case WM_SETFOCUS:
                            SetFocus (hwndEdit) ;
                            return 0 ;
            case WM_SIZE:
                            MoveWindow (hwndEdit, 0, 0, LOWORD (lParam), HIWORD (lParam), TRUE) ;
                            return 0 ;
            case WM_INITMENUPOPUP:
                            if (lParam == 1)
                            {
                                            EnableMenuItem ((HMENU) wParam, IDM_EDIT_UNDO,SendMessage (hwndEdit, EM_CANUNDO, 0, 0)
                                                           MF_ENABLED : MF_GRAYED) ;
                                            EnableMenuItem ((HMENU) wParam, IDM_EDIT_PASTE,IsClipboardFormatAvailable (CF_TEXT) ?
                                                           MF_ENABLED : MF_GRAYED) ;
                                            iSelect = SendMessage (hwndEdit, EM_GETSEL, 0, 0) ;
                                            if (HIWORD (iSelect) == LOWORD (iSelect))
                                                           iEnable = MF_GRAYED ;
                                            else
                                                           iEnable = MF_ENABLED ;
                                                           EnableMenuItem ((HMENU) wParam, IDM_EDIT_CUT, iEnable) ;
                                                           EnableMenuItem ((HMENU) wParam, IDM_EDIT_COPY, iEnable) ;
                                                           EnableMenuItem ((HMENU) wParam, IDM_EDIT_CLEAR, iEnable) ;
                                                           return 0 ;
                            }
                            break ;
case WM_COMMAND:
              if (lParam)
              {
if (LOWORD (lParam) == ID_EDIT && (HIWORD (wParam) == EN_ERRSPACE || HIWORD (wParam) == EN_MAXTEXT))
                           MessageBox (hwnd, TEXT ("Edit control out of space."),szAppName, MB_OK | MB_ICONSTOP) ;
                                        return 0 ;
              }
              else
                           switch (LOWORD (wParam))
                           {
                           case IDM_FILE_NEW:
                           case IDM_FILE_OPEN:
                           case IDM_FILE_SAVE:
                           case IDM_FILE_SAVE_AS:
                           case IDM_FILE_PRINT:
                                        MessageBeep (0) ;
                                        return 0 ;
                           case IDM_APP_EXIT:
                                        SendMessage (hwnd, WM_CLOSE, 0, 0) ;
                                        return 0 ;
                           case IDM_EDIT_UNDO:
                                        SendMessage (hwndEdit, WM_UNDO, 0, 0) ;
                                        return 0 ;
                           case IDM_EDIT_CUT:
                                        SendMessage (hwndEdit, WM_CUT, 0, 0) ;
                                        return 0 ;
                           case IDM_EDIT_COPY:
                                        SendMessage (hwndEdit, WM_COPY, 0, 0) ;
                                        return 0 ;
                           case IDM_EDIT_PASTE:
                                        SendMessage (hwndEdit, WM_PASTE, 0, 0) ;
                                        return 0 ;
                           case IDM_EDIT_CLEAR:
                                        SendMessage (hwndEdit, WM_CLEAR, 0, 0) ;
                                        return 0 ;
                           case IDM_EDIT_SELECT_ALL:
                                        SendMessage (hwndEdit, EM_SETSEL, 0, -1) ;
                                        return 0 ;
                           case IDM_HELP_HELP:
              MessageBox (hwnd, TEXT ("Help not yet implemented!"),szAppName, MB_OK | MB_ICONEXCLAMATION) ;
                                        return 0 ;
                           case IDM_APP_ABOUT:
              MessageBox (hwnd, TEXT ("POPPAD2 (c) Charles Petzold, 1998"),szAppName, MB_OK | MB_ICONINFORMATION
                                        return 0 ;
                           }
    case WM_CLOSE:
                  if (IDYES == AskConfirmation (hwnd))
                  DestroyWindow (hwnd) ;
                  return 0 ;
    case WM_QUERYENDSESSION:
                  if (IDYES == AskConfirmation (hwnd))
                                 return 1 ;
                  else
                                 return 0 ;
    case WM_DESTROY:
                  PostQuitMessage (0) ;
                  return 0 ;
    }
    return DefWindowProc (hwnd, message, wParam, lParam) ;
}
  10.
대화상자
1. 다이얼로그 박스
• 대화상자
 – 다양한 자식 윈도우 컨트롤을 가진 팝업 윈도우 형식을 갖는다.
 – 메시지는 대화상자 프로시저에서 처리된다.
 – 대화상자 프로시저
      •   대화상자가 만들어질 때 자식 윈도우 컨트롤을 초기화
      •   자식 윈도우 컨트롤로부터의 메시지를 처리
      •   대화상자를 종료.
      •   키보드와 마우스 입력도 처리하지 않는다.
      •   WM_PAINT도 처리하지 않는다.
• 모달
 –    사용자가 대화상자와 또 다른 윈도우 사이를 전환할 수 없다.
 –   가장 일반적
 –   확인이나 취소 버튼을 이용하여 대화상자를 종료
 –   DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1),hwn
     d,DlgProc);
      •   hInstance : Instance의 Handle
      •   MAKEINTRESOURCE(IDD_DIALOG1) : 대화상자의 이름
      •    hwnd : 대화상자의 부모
      •   DlgProc : 대화상자 프로시저 함수
1. 다이얼로그 박스
• 파라미터 전달
 – INT_PTR DialogBoxParam( HINSTANCE hInstance, LPCTSTR
   lPTemplateName, HWND hWndParent, DLGPROC lpDialogFunc,
   LPARAM dwInitParam);
    • 함수의 원형은 DialogBox와 거의 유사하되 제일 뒤에 dwInitParam이
      라는 인수가 하나 더 추가되었다.
    • 이 값은 대화상자를 생성할 때 WM_INITDIALOG의 lParam으로 전달
      된다.
• 아이들 메시지
 – 모달 대화상자가 떠 있는 동안 부모 윈도우는 사용 금지되어 아무
   런 입력도 받지 못한다.
 – 사용 금지된 윈도우도 메시지를 처리할 수 있는데 백그라운드 작
   업이 필요하다면 WM_ENTERIDLE메시지를 사용할 수 있다.
 – 이 메시지는 모달 대화상자나 메뉴 윈도우가 떠 잇는 동안 부모
   윈도우에게 주기적으로 보내진다.
 – 모달 대화상자는 자신에게 전달된 메시지를 처리하다가 더 이상
   처리할 메시지가 없을 때, 부모 윈도우에게 이 메시지를 전달한다.
/*------------------------------------------
   ABOUT1.C -- About Box Demo Program No. 1
           (c) Charles Petzold, 1998
   ------------------------------------------*/
#include <windows.h>
#include "resource.h"
LRESULT CALLBACK WndProc           (HWND, UINT, WPARAM, LPARAM) ;
BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
                static TCHAR szAppName[] = TEXT ("About1") ;
                MSG        msg ;
                HWND        hwnd ;
                WNDCLASS       wndclass ;

             wndclass.style     = CS_HREDRAW | CS_VREDRAW ;
             wndclass.lpfnWndProc = WndProc ;
             wndclass.cbClsExtra = 0 ;
             wndclass.cbWndExtra = 0 ;
             wndclass.hInstance   = hInstance ;
             wndclass.hIcon      = LoadIcon (hInstance, szAppName) ;
             wndclass.hCursor     = LoadCursor (NULL, IDC_ARROW) ;
             wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
             wndclass.lpszMenuName = szAppName ;
             wndclass.lpszClassName = szAppName ;

             if (!RegisterClass (&wndclass))
             {
                              MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;
                              return 0 ;
             }
             hwnd = CreateWindow (szAppName, TEXT ("About Box Demo Program"),WS_OVERLAPPEDWINDOW,
                              CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;

             ShowWindow (hwnd, iCmdShow) ;
             UpdateWindow (hwnd) ;

             while (GetMessage (&msg, NULL, 0, 0))
             {
                           TranslateMessage (&msg) ;
                           DispatchMessage (&msg) ;
             }
             return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
           static HINSTANCE hInstance ;

            switch (message)
            {
            case WM_CREATE :
                          hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;
                          return 0 ;
            case WM_COMMAND :
                          switch (LOWORD (wParam))
                          {
                          case IDM_APP_ABOUT :
                                         DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ;
                                         break ;
                          }
                          return 0 ;
            case WM_DESTROY :
                          PostQuitMessage (0) ;
                          return 0 ;
            }
            return DefWindowProc (hwnd, message, wParam, lParam) ;
}

BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
           switch (message)
           {
           case WM_INITDIALOG :
                         return TRUE ;
           case WM_COMMAND :
                         switch (LOWORD (wParam))
                         {
                         case IDOK :
                         case IDCANCEL :
                                       EndDialog (hDlg, 0) ;
                                       return TRUE ;
                         }
           break ;
           }
           return FALSE ;
}
1. 다이얼로그 박스
윈도우 프로시저와 대화상자 프로시저
     LRESULT를 반환          BOOL을 반환
   메시지 처리하지 않으면        메시지 처리 O : TRUE
    DefWindowProc호출    메시지 처리 X : FALSE
   초기화 : WM_CREATE    초기화 : WM_INITDIALOG
/*------------------------------------------
   ABOUT3.C -- About Box Demo Program No. 3
           (c) Charles Petzold, 1998
  ------------------------------------------*/
#include <windows.h>
#include "resource.h"
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ;
LRESULT CALLBACK EllipPushWndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
                static TCHAR szAppName[] = TEXT ("About3") ;
                MSG          msg ;
                HWND          hwnd ;
                WNDCLASS        wndclass ;
                wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
                wndclass.lpfnWndProc = WndProc ;
                wndclass.cbClsExtra = 0 ;
                wndclass.cbWndExtra = 0 ;
                wndclass.hInstance       = hInstance ;
                wndclass.hIcon          = LoadIcon (hInstance, szAppName) ;
                wndclass.hCursor         = LoadCursor (NULL, IDC_ARROW) ;
                wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
                wndclass.lpszMenuName = szAppName ;
                wndclass.lpszClassName = szAppName ;
                if (!RegisterClass (&wndclass))
                {
                                 MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;
                                 return 0 ;
                }
                wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
                wndclass.lpfnWndProc = EllipPushWndProc ;
                wndclass.cbClsExtra = 0 ;
                wndclass.cbWndExtra = 0 ;
                wndclass.hInstance       = hInstance ;
                wndclass.hIcon          = NULL ;
                wndclass.hCursor         = LoadCursor (NULL, IDC_ARROW) ;
                wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1) ;
                wndclass.lpszMenuName = NULL ;
                wndclass.lpszClassName = TEXT ("EllipPush") ;
                RegisterClass (&wndclass) ;
                hwnd = CreateWindow (szAppName, TEXT ("About Box Demo Program"),WS_OVERLAPPEDWINDOW,
                                 CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;
                ShowWindow (hwnd, iCmdShow) ;
                UpdateWindow (hwnd) ;
              {
                            TranslateMessage (&msg) ;
                            DispatchMessage (&msg) ;
              }
              return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            static HINSTANCE hInstance ;
            switch (message)
            {
            case WM_CREATE :
                          hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;
                          return 0 ;
            case WM_COMMAND :
                          switch (LOWORD (wParam))
                          {
                          case IDM_APP_ABOUT :
                                         DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ;
                                         return 0 ;
                          }
                          break ;
            case WM_DESTROY :
                          PostQuitMessage (0) ;
                          return 0 ;
            }
            return DefWindowProc (hwnd, message, wParam, lParam) ;
}

BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
           switch (message)
           {
           case WM_INITDIALOG :
                          return TRUE ;
           case WM_COMMAND :
                          switch (LOWORD (wParam))
                          {
                          case IDOK :
                                        EndDialog (hDlg, 0) ;
                                        return TRUE ;
                          }
                          break ;
           }
           return FALSE ;
LRESULT CALLBACK EllipPushWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
TCHAR      szText[40] ;
HBRUSH      hBrush ;
HDC       hdc ;
PAINTSTRUCT ps ;
RECT      rect ;
switch (message)
{
              case WM_PAINT :
                          GetClientRect (hwnd, &rect) ;
                          GetWindowText (hwnd, szText, sizeof (szText)) ;
                          hdc = BeginPaint (hwnd, &ps) ;
                          hBrush = CreateSolidBrush (GetSysColor (COLOR_WINDOW)) ;
                          hBrush = (HBRUSH) SelectObject (hdc, hBrush) ;
                          SetBkColor (hdc, GetSysColor (COLOR_WINDOW)) ;
                          SetTextColor (hdc, GetSysColor (COLOR_WINDOWTEXT)) ;

                          Ellipse (hdc, rect.left, rect.top, rect.right, rect.bottom) ;
                          DrawText (hdc, szText, -1, &rect,
                          DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
                          DeleteObject (SelectObject (hdc, hBrush)) ;
                          EndPaint (hwnd, &ps) ;
                          return 0 ;
            case WM_KEYUP :
                          if (wParam != VK_SPACE)
                                          break ;
                          // fall through
            case WM_LBUTTONUP :
                          SendMessage (GetParent (hwnd), WM_COMMAND,GetWindowLong (hwnd, GWL_ID), (LPARAM) hwnd) ;
                          return 0 ;
            }
            return DefWindowProc (hwnd, message, wParam, lParam) ;
}
2. 모달리스 대화상자
     DialogBox()   CreateDialog()
     종료후 리턴          바로 리턴

 – Visible 속성을 Check하여야 한다.
 – 메시지는 프로그램의 메시지 큐를 통해서 들어온다.
 – 메시지 루프 루틴을 변경해야 한다.
 – IsDialogMessage(hDlgModeless, & msg)
     • 모달리스 메시지이면 대화상자 윈도우 프로시저에 메시지를
       전달, Return TRUE
 – EndDialog대신 DestroyWindow를 사용.
/*------------------------------------------------
COLORS2.C -- Version using Modeless Dialog Box
(c) Charles Petzold, 1998
------------------------------------------------*/
#include <windows.h>
LRESULT CALLBACK WndProc             (HWND, UINT, WPARAM, LPARAM) ;
BOOL CALLBACK ColorScrDlg (HWND, UINT, WPARAM, LPARAM) ;
HWND hDlgModeless ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
               static TCHAR szAppName[] = TEXT ("Colors2") ;
               HWND          hwnd ;
               MSG          msg ;
               WNDCLASS        wndclass ;
               wndclass.style          = CS_HREDRAW | CS_VREDRAW ;
               wndclass.lpfnWndProc = WndProc ;
               wndclass.cbClsExtra = 0 ;
               wndclass.cbWndExtra = 0 ;
               wndclass.hInstance        = hInstance ;
               wndclass.hIcon           = LoadIcon (NULL, IDI_APPLICATION) ;
               wndclass.hCursor          = LoadCursor (NULL, IDC_ARROW) ;
               wndclass.hbrBackground = CreateSolidBrush (0L) ;
               wndclass.lpszMenuName = NULL ;
               wndclass.lpszClassName = szAppName ;
               if (!RegisterClass (&wndclass))
               {
                                MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;
                                return 0 ;
               }
               hwnd = CreateWindow (szAppName, TEXT ("Color Scroll"),WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                                CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;
               ShowWindow (hwnd, iCmdShow) ;
               UpdateWindow (hwnd) ;
               hDlgModeless = CreateDialog (hInstance, TEXT ("ColorScrDlg"), hwnd, ColorScrDlg) ;
               while (GetMessage (&msg, NULL, 0, 0))
               {
                                if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg))
                                {
                                                 TranslateMessage (&msg) ;
                                                 DispatchMessage (&msg) ;
                                }
               }
               return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            switch (message)
            {
            case WM_DESTROY :
            DeleteObject ((HGDIOBJ) SetClassLong (hwnd, GCL_HBRBACKGROUND,(LONG) GetStockObject (WHITE_BRUSH)));
            PostQuitMessage (0) ;
                              return 0 ;
            }
            return DefWindowProc (hwnd, message, wParam, lParam) ;
}
BOOL CALLBACK ColorScrDlg (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
            static int iColor[3] ;
            HWND         hwndParent, hCtrl ;
            int      iCtrlID, iIndex ;

            switch (message)
            {
            case WM_INITDIALOG :
                          for (iCtrlID = 10 ; iCtrlID < 13 ; iCtrlID++)
                          {
                                           hCtrl = GetDlgItem (hDlg, iCtrlID) ;
                                           SetScrollRange (hCtrl, SB_CTL, 0, 255, FALSE) ;
                                           SetScrollPos (hCtrl, SB_CTL, 0, FALSE) ;
                          }
                          return TRUE ;
    case WM_VSCROLL :
                   hCtrl = (HWND) lParam ;
                   iCtrlID = GetWindowLong (hCtrl, GWL_ID) ;
                   iIndex = iCtrlID - 10 ;
                   hwndParent = GetParent (hDlg) ;
                   switch (LOWORD (wParam))
                   {
                   case SB_PAGEDOWN :
                                   iColor[iIndex] += 15 ;       // fall through
                   case SB_LINEDOWN :
                                   iColor[iIndex] = min (255, iColor[iIndex] + 1) ;
                                   break ;
                   case SB_PAGEUP :
                                   iColor[iIndex] -= 15 ;       // fall through
                   case SB_LINEUP :
                                   iColor[iIndex] = max (0, iColor[iIndex] - 1) ;
                                   break ;
                   case SB_TOP :
                                   iColor[iIndex] = 0 ;
                                   break ;
                   case SB_BOTTOM :
                                   iColor[iIndex] = 255 ;
                                   break ;
                   case SB_THUMBPOSITION :
                   case SB_THUMBTRACK :
                                   iColor[iIndex] = HIWORD (wParam) ;
                                   break ;
                   default :
                                   return FALSE ;
                   }
                   SetScrollPos (hCtrl, SB_CTL,       iColor[iIndex], TRUE) ;
                   SetDlgItemInt (hDlg, iCtrlID + 3, iColor[iIndex], FALSE) ;
                   DeleteObject ((HGDIOBJ) SetClassLong (hwndParent, GCL_HBRBACKGROUND,
                                   (LONG) CreateSolidBrush (RGB (iColor[0], iColor[1], iColor[2])))) ;
                   InvalidateRect (hwndParent, NULL, TRUE) ;
                   return TRUE ;
    }
    return FALSE ;
}
BOOL GetOpenFileName( LPOPENFILENAME );
typedef struct tagOFN
{
        DWORD               lStructSize;
        HWND                hwndOwner;
        HINSTANCE           hInstance;
        LPCTSTR             lpstrFilter;
        LPTSTR              lpstrCustomFilter;
        DWORD               nMaxCustFilter;
        DWORD               nFilterIndex;
        LPTSTR              lpstrFile;
        DWORD               nMaxFile;
        LPTSTR              lpstrFileTitle;
        DWORD               nMaxFileTitle;
        LPCTSTR             lpstrInitialDir;
        LPCTSTR             lpstrTitle;
        DWORD               Flags;
        WORD                nFileOffset;
        WORD                nFileExtension;
        LPCTSTR             lpstrDefExt;
        DWORD               lCustData;
        LPOFNHOOKPROC       lpfnHook;
        LPCTSTR             lpTemplateName;
} OPENFILENAME;
3. 공용대화상자
• FileOpen, FileSave Dialog
   – BOOL GetOpenFileName( LPOPENFILENAME lpofn );
   – BOOL GetSaveFileName( LPOPENFILENAME lpofn );
   – OPENFILENAME
      • lStructSize : OPENFILENAME 구조체의 크기
      • hwndOwner : 대화상자의 소유자를 지정
      • hInstance : 별도의 대화상자 템플릿을 사용할 경우 리소스를 가진
        인스턴스 핸들을 지정
      • lpstrFilter : 파일 형식 콤보 박스에 나타낼 필터들이며 널 문자를 기
        준으로 “파일형식\0필터”와 같이 기술한다.
      • nFilterIndex : 파일 형식 콤보 박스에서 사용할 필터의 인덱스를 지
        정한다. 1이면 첫 번째 Filter, 2면 두 번째 필터
      • lpstrFile : 파일 이름 에디트에 처음 나타낼 파일명을 지정. 사용자
        가 최종적으로 선택한 파일의 이름(완전경로)을 리턴하는 용도로도
        사용된다.
      • nMaxFile : lpstrFile멤버의 길이. 최소 256
      • lpstrFileTitle : 파일의 이름을 돌려받기 위한 버퍼를 제공 (이름만
        리턴)
3. 공용대화상자
    •   lpstrInitialDir : 파일 찾기를 시작할 디렉토리를 지정한다.
    •   lpstrTitle : 대화상자의 캡션을 지정한다.
    •   Flags : 대화상자의 모양과 동작을 지정하는 옵션.
    •   nFileOffset : lpstrFile버퍼 내의 파일명 오프셋을 리턴한다.
        lpstrFile버퍼에서 이 오프셋만큼 더하면 경로명을 제외한 파일명만
        얻을 수 있다.
 – Flags
    • OFN_ALLOWMULTISELECT : 복수 개의 파일을 선택할 수 있도록
      한다. 이 플래그를 OFN_EXPLORER플래그 없이 사용하면 구형 대
      화상자가 열린다.
    • OFN_ENABLESIZING : 크기 조정이 가능하도록 한다.
    • OFN_FILEMUSTEXIST : 사용자는 존재하는 파일만 입력해 넣을 수
      있으며 없는 파일을 입력한 경우 경고 메시지를 보여준다.
    • OFN_FORCESHOWHIDDEN : 시스템 파일과 숨겨진 파일을 보여준
      다.
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
          switch (message)
          {
          case WM_CREATE:
                      return 0;
          case WM_LBUTTONDOWN:
                      {
                                char temp[256];
                                strcpy(temp,"123.txt");

                                 OPENFILENAME ofn;
                                 ZeroMemory(&ofn,sizeof(ofn));
                                 ofn.lStructSize = sizeof(OPENFILENAME);
                                 ofn.hwndOwner = hwnd;
                                 ofn.lpstrFilter = "모든 파일(*.*)\0*.*\0텍스트 파일(*.txt)\0*.txt\0\0\0";
                                 ofn.lpstrFile = temp;
                                 ofn.nFilterIndex = 2;
                                 ofn.nMaxFile = 256;
                                 ofn.Flags = OFN_EXPLORER | OFN_ALLOWMULTISELECT | OFN_ENABLESIZING ;
                                 if (GetOpenFileName(&ofn) != NULL)
                                 {
                                                MessageBox(hwnd,temp,"파일선택",MB_OK);
                                 }
                      }
                      return 0;
          case WM_DESTROY:
                      PostQuitMessage (0) ;
                      return 0 ;
          }
          return DefWindowProc (hwnd, message, wParam, lParam) ;
}
3. 공용대화상자
 – 복수 개의 파일 선택
   • 폴더 경로 \0 첫 번째 파일명 \0 두 번째 파일명 \0\0
3. 공용대화상자
BOOL ChooseColor( LPCHOOSECOLOR );
typedef struct
{
        DWORD              lStructSize;
        HWND               hwndOwner;
        HWND               hInstance;
        COLORREF           rgbResult;
        COLORREF *         lpCustColors;
        DWORD              Flags;
        LPARAM             lCustData;
        LPCCHOOKPROC       lpfnHook;
        LPCTSTR                     lpTemplateName;
} CHOOSECOLOR;

rgbResult specifies the user's color selection
lpCustColors
Pointer to an array of 16 COLORREF values that contain red, green,
blue (RGB) values for the custom color boxes in the dialog box
3. 공용대화상자
 – Flags
    •   CC_ANYCOLOR : 기본 색상중 모든 색상을 보여주도록 한다.
    •   CC_FULLOPEN : 대화상자를 확장형으로 보여준다.
    •   CC_PREVENTFULLOPEN : 확장형으로 열리지 못하도록 한다.
    •   CC_SOLDCOLOR : 디더링되지 않은 색상만 보여준다.
 3. 공용대화상자
static COLORREF s_CustColors[16];
static COLORREF s_color = RGB(255, 255, 255);
for (i = 0; i < 16; i++)
{
          s_CustColors[i] = RGB(255, 255, 255);
}

cc.lStructSize = sizeof(CHOOSECOLOR);
cc.hwndOwner = hwnd;
cc.hInstance = NULL;
cc.rgbResult = s_color;
cc.lpCustColors = s_CustColors;
cc.Flags = CC_RGBINIT;
cc.lCustData = 0;
cc.lpfnHook = NULL;
cc.lpTemplateName = NULL;
if (ChooseColor(&cc))
{
         s_color = cc.rgbResult;
         InvalidateRect(hwnd, NULL, TRUE);
}
 3. 공용대화상자
    – BOOL ChooseFont( LPCHOOSEFONT lpcf );

typedef struct
{
         DWORD        lStructSize;
         HWND         hwndOwner;
         HDC          hDC;
         LPLOGFONT    lpLogFont;
         INT          iPointSize;
         DWORD        Flags;
         COLORREF     rgbColors;
         LPARAM       lCustData;
         LPCFHOOKPROC lpfnHook;
         LPCTSTR      lpTemplateName;
         HINSTANCE    hInstance;
         LPTSTR       lpszStyle;
         WORD         nFontType;
         INT          nSizeMin;
         INT          nSizeMax;
} CHOOSEFONT, *LPCHOOSEFONT;
3. 공용대화상자
lpStructSize   구조체의 크기
hwndOwner      대화상자의 소유자를 지정한다.
hDC            Flags에 CF_PRINTERFONTS가 설정되어 있는 경우에 프린터
               DC를 지정한다.
lpLogFont      사용자가 선택한 글꼴 정보를 리턴 할 LOGFONT 구조체
               CF_INITTOLOGFONTSTRUCT 플래그가 설정되어 있으면 이
               구조체의 정보대로 폰트 선택 대화상자가 초기화 된다.
iPointSize     선택한 폰트의 포인트 크기를 1/10포인트 단위로 리턴
Flags          대화상자 초기화와 사용자의 선택값을 리턴하는 플래그들이다.
rgbColors      CF_EFFECTS플래그가 설정된 경우 사용자가 선택한 글꼴 색
               상을 리턴한다.
lpszStyle      글꼴 스타일 콤보 박스 초기화에 사용될 스타일 값이며 사용자
               가 선택한 스타일을 리턴한다.
3. 공용대화상자
CF_APPLY     적용 버튼을 표시하도록 한다. 이 플래그를 사용할 경우
             APPLY버튼을 누를 때 보내지는 WM_COMMAND메시지를 처
             리할 훅 프로시저를 제공해 주어야 한다.
CF_SCREENF 시스템에 설치된 화면 폰트만 보여준다.
ONTS
CF_PRINTER   hDC 멤버가 지정한 프린터의 폰트만 보여준다.
FONTS
CF_EFFECTS   관통선, 밑줄 등의 글꼴 스타일과 색상을 선택할 수 있도록 한
             다.
    11.
공통 컨트롤
1. 공통 컨트롤의 초기화
   – 공통 컨트롤은 DLL에 의해 제공되는 것이기 때문에 이 DLL이 로
     드되어 있지 않으면 윈도우 클래스가 정의되지 않으며 컨트롤도
     생성할 수 없다.
      • 1. CommCtrl.h를 반드시 포함시킨다.
         – 공통 컨트롤에 대한 윈도우 클래스명, 스타일, 메시지 등에 대한 매크로
           상수가 정의되어 있다.
      • 2. 프로그램 선두에 InitCommonControls()함수를 호출해 주어야
        한다.
         – 이 함수는 공통 컨트롤을 제공하는 DLL인 COMCTL32.DLL이 제대로
           로드되었는지를 체크해 주고 만약 로드 되어 있지 않으면 로드한다.
      • 3. ComCtl32.lib를 링크 시킨다.
      • BOOL InitCommonControlsEx( LPINITCOMMONCONTROLSEX
        lpInitCtrls);


typedef struct tagINITCOMMONCONTROLSEX {
        DWORD            dwSize;
        DWORD            dwICC;
} INITCOMMONCONTROLSEX, *LPINITCOMMONCONTROLSEX;
1. 공통 컨트롤의 초기화
플래그                      컨트롤
ICC_ANIMATE_CLASS        애니메이트 컨트롤
ICC_BAR_CLASSES          툴바, 상태바, 트랙바, 툴팁
ICC_COLL_CLASSES         리바
ICC_DATE_CLASSES         날짜 선택 컨트롤
ICC_INTERNET_CLASSES     IP 컨트롤
ICC_LISTVIEW_CLASSES     리스트 뷰, 헤더 컨트롤
ICC_PAGESCROLLER_CLASS   페이지컨트롤
ICC_PROGRESS_CLASS       프로그래스 바
ICC_TAB_CLASSES          탭, 툴팁 컨트롤
ICC_TREEVIEW_CLASSES     트리 뷰, 툴팁 컨트롤
ICC_UPDOWN_CLASS         업 다운 컨트롤
ICC_USEREX_CLASSES       확장 콤보 박스
ICC_WIN95_CLASSES        애니메이트, 헤더, 핫키, 리스트 뷰, 프로그
                         래스, 상태란, 탭, 툴 팁, 툴 바, 트랙 바, 업
                         다운 컨트롤
2. Progress 컨트롤
  – 윈도우 클래스 : PROGRESS_CLASS
  – 1. PBM_SETRANGE메시지로 범위를 설정한다.
  – 2. PBM_SETPOS메시지로 위치 값을 설정한다.
메시지            설명
PBM_SETRANGE   범위를 설정한다. wParam은 사용하지 않는다.
               LOWORD(lParam)으로 최소값, HIWORD(lParam)으로 최
               대값을 주면 된다. 범위는 0 – 100
PBM_SETPOS     wParam으로 위치를 설정한다.
PBM_DELTAPOS   wParam이 지정하는 만큼 위치를 증감시킨다. 새 위치는
               현재 위치 + wParam이 된다.
PBM_SETSTEP    단계 증가값을 wParam으로 지정한다. 이 값은
               PBM_STEPIP메시지에서 사용한다.
PBM_STEPIT     단계 증가 값만큼 위치를 이동시킨다.
PBM_SETRANGE   wParam으로 최소값, lParam으로 최대값을 지정한다.
32             65535이상의 범위를 가지는 프로그래스를 만들 때 사용
               한다.
2. Progress 컨트롤
메시지            설명
PBM_GETRANGE   현재 설정된 범위를 얻는다.
PBM_GETPOS     현재 설정된 위치를 얻는다.
BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
           static HWND hProgress;
           switch (message)
           {
           case WM_INITDIALOG :
                          hProgress = GetDlgItem(hDlg,IDC_PROGRESS1);
                          SendMessage(hProgress,PBM_SETRANGE,0,MAKELPARAM(0,100));
                          SendMessage(hProgress,PBM_SETPOS,0,0);
                          return TRUE ;
           case WM_COMMAND :
                          switch (LOWORD (wParam))
                          {
                          case IDC_BUTTON1:
                                        {
                                                       for(int i = 0;i <= 100;i++)
                                                       {
                                                                         SendMessage(hProgress,PBM_SETPOS,i,0);
                                                                         Sleep(50);
                                                       }
                                        }
                                        return TRUE;
                          case IDOK :
                          case IDCANCEL :
                                        EndDialog (hDlg, 0) ;
                                        return TRUE ;
                          }
           break ;
           }
           return FALSE ;
}
3. 트랙 바
TBS_HORZ        수평 트랙 바를 만든다
TBS_VERT        수직 트랙 바를 만든다.
TBS_AUTOTICKS   범위내의 모든 값에 틱 마크를 배치한다.
TBS_NOTICKS     틱 마크를 출력하지 않는다.
TBS_BOTTOM      수평일 경우 바닥에 틱 마크를 출력한다.
TBS_TOP         수평일 경우 위쪽에 틱 마크를 출력한다.
TBS_RIGHT       수직일 경우 오른쪽에 틱 마크를 출력한다.
TBS_LEFT        수직일 경우 왼쪽에 틱 마크를 출력한다.
TBS_BOTH        틱 마크를 양쪽에 출력한다.
TBS_ENABLESEL   선택 영역을 표시한다. 이 스타일이 지정되었을 경우 선택
RANGE           영역의 시작점과 끝점의 틱은 삼각형 모양이 되며 선택된
                영역이 밝게 강조된다.
TBS_FIXEDLENG   썸의 길이를 변경할 수 있도록 한다.
TH
TBS_NOTHUMB     썸을 가지지 않는다.
TBS_TOOLTIPS    툴 팁을 사용한다.
3. 트랙 바
TBM_GETPOS      트랙바의 현재 위치를 얻는다.
TBM_GETPOSMIN   범위의 최소값을 얻는다.
TBM_GETPOSMAX   범위의 최대값을 얻는다.
TBM_GETTIC      wParam으로 지정한 인덱스의 틱 위치를 얻는다.
TBM_SETTIC      lParam으로 지정한 인덱스에 틱 마크를 배치한다.
TBM_SETPOS      lParam으로 트랙바의 위치를 변경한다.wParam이
                TRUE이면 위치 변경 후 트랙바를 다시 그리고, FALSE
                이면 다시 그리지 않는다.
TBM_SETRANGE    트랙바의 범위를 설정한다. LOWORD(lParam)에 최소
                값, HIWORD(lParam)에 최대값을 준다.
TBM_CLEARTICS   틱 마크를 제거한다.

   – 사용자가 트랙 바를 조작하면 WM_HSCROLL, WM_VSCROLL 메
     시지를 부모 윈도우로 보내며 LOWORD(wParam)으로 통지 메시
     지를 보낸다.
 3. 트랙 바
TB_LINEUP         라인 증가
TB_LINEDOWN       라인 감소
TB_PAGEUP         페이지 증가
TB_PAGEDOWN       페이지 감소
TB_THUMBPOSITON   드래그 종료
TB_THUMBTRACK     드래그 중
TB_TOP            최상위 위치로 이동
TB_BOTTOM         최하위 위치로 이동
TB_ENDTRACK       조작 종료
BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
           static HWND hProgress;
           static HWND hEdit;
           static HWND hTrackBar;
           switch (message)
           {
           case WM_INITDIALOG :
                         hProgress = GetDlgItem(hDlg,IDC_PROGRESS1);
                         SendMessage(hProgress,PBM_SETRANGE,0,MAKELPARAM(0,100));
                         SendMessage(hProgress,PBM_SETPOS,0,0);

                         hTrackBar = GetDlgItem(hDlg,IDC_SLIDER1);
                         SendMessage(hTrackBar,TBM_SETRANGE,0,MAKELPARAM(0,100));
                         SendMessage(hTrackBar,TBM_SETPOS,0,0);

                        hEdit = GetDlgItem(hDlg,IDC_EDIT1);
                        return TRUE ;
            case WM_HSCROLL:
                        {
                                       int iReturnCtrl = GetDlgCtrlID((HWND)lParam);
                                       switch (iReturnCtrl)
                                       {
                                       case IDC_SLIDER1:
                                                        {
                                                                       int iPos;
                                                                       iPos = SendMessage(hTrackBar,TBM_GETPOS,NULL,NULL);
                                                                       char temp[256];
                                                                       wsprintf(temp,"%d",iPos);
                                                                       SetWindowText(hEdit,temp);
                                                        }
                                                        break;
                                       }
                        }
                        return TRUE;
    case WM_COMMAND :
               switch (LOWORD (wParam))
               {
               case IDC_BUTTON1:
                            {
                                                 for(int i = 0;i <= 100;i++)
                                                 {
                                                                   SendMessage(hProgress,PBM_SETPOS,i,0);
                                                                   Sleep(50);
                                                 }
                                  }
                                  return TRUE;
                     case IDOK :
                     case IDCANCEL :
                                 EndDialog (hDlg, 0) ;
                                 return TRUE ;
                     }
    break ;
    }
    return FALSE ;
}
4. 업다운
UDS_HORZ          업 다운 버튼을 위아래로 배치한다.
UDS_WRAP          1 – 100까지 일 때 100에서 1증가하면 0이된다.
                  1에서 1감소하면 100이 된다.
UDS_ARROWKEYS     커서 티를 사용하여 위치 값을 변경할 수 있도록 한다.
UDS_SETBUDDYINT   업다움 컨트롤의 값이 변경되면 버디 윈도우의 텍스트
                  를 변경하도록 한다. 값이 변경될 때마다 위치 값을 문
                  자열로 바꾼 후 버디 윈도우로 WM_SETTEXT메시지를
                  보내준다.
UDS_NOTHOUSANDS 매 세자리 마다 콤마를 삽입해 주는 것을 막는다.
UDS_AUTOBUDDY     이 컨트롤보다 Z순서가 바로 앞인 컨트롤을 자동으로
                  버디 윈도우로 지정한다.
UDS_ALIGNRIGHT    버디 윈도우의 오른쪽에 배치한다.
UDS_ALIGNLIGHT    버디 윈도우의 왼쪽에 배치한다.
4. 업다운
UDM_SETRANGE    범위를 지정한다. LOWORD(lParam)에 최대 값을
                HIWORD(lParam)에 최소값을 지정한다.
                디폴트 범위는 최소값 100, 최대값 0이며 범위가 반대
                로 되어 있다.
UDM_SETPOS      LOWORD(lParam)으로 위치를 설정한다.
UDM_SETBUDDY    버디 윈도우를 설정한다. wParam에 버디로 지정할 윈
                도우 핸들을 넘겨준다.
UDM_SETACCELL   업다운 컨트롤에 가속 기능을 부여한다. lParam에
                UDACCEL구조체 배열의 번지를 넘겨주고 wParam에
                배열의 크기를 넘겨준다.
                typedef struct { UINT nSec; UINT nInc; }
                UDACCEL, *LPUDACCEL;
                nSec : 가속할 초 단위 시간
                nlnc : nSec초 이후에 적용될 가속값이다.
                5초 후에는 10씩 증가, 10초 후에는 20씩 증가
BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
           static HWND hProgress;
           static HWND hEdit,hEdit2;
           static HWND hTrackBar;
           static HWND hSpin;

            switch (message)
            {
            case WM_INITDIALOG :
                          {
                                         hProgress = GetDlgItem(hDlg,IDC_PROGRESS1);
                                         SendMessage(hProgress,PBM_SETRANGE,0,MAKELPARAM(0,100));
                                         SendMessage(hProgress,PBM_SETPOS,0,0);

                                         hTrackBar = GetDlgItem(hDlg,IDC_SLIDER1);
                                         SendMessage(hTrackBar,TBM_SETRANGE,0,MAKELPARAM(0,100));
                                         SendMessage(hTrackBar,TBM_SETPOS,0,0);

                                         hEdit = GetDlgItem(hDlg,IDC_EDIT1);
                                         hEdit2 = GetDlgItem(hDlg,IDC_EDIT2);

                                         hSpin = GetDlgItem(hDlg,IDC_SPIN1);
                                         SendMessage(hSpin,UDM_SETRANGE,0,MAKELPARAM(1000,0));
                                         SendMessage(hSpin,UDM_SETBUDDY,(WPARAM)hEdit2,NULL);
                                         static UDACCEL udAccel[3] = {{0,1},{5,10},{8,20}};
                                         SendMessage(hSpin,UDM_SETACCEL,(WPARAM)3,(LPARAM)udAccel);
                         }
                         return TRUE ;
    case WM_HSCROLL:
               {
                              int iReturnCtrl = GetDlgCtrlID((HWND)lParam);
                              switch (iReturnCtrl)
                              {
                              case IDC_SLIDER1:
                                              {
                                                            int iPos;
                                                            iPos = SendMessage(hTrackBar,TBM_GETPOS,NULL,NULL);
                                                            char temp[256];
                                                            wsprintf(temp,"%d",iPos);
                                                            SetWindowText(hEdit,temp);
                                              }
                                              break;
                              }
               }
               return TRUE;
    case WM_COMMAND :
               switch (LOWORD (wParam))
               {
               case IDC_BUTTON1:
                            {
                                             for(int i = 0;i <= 100;i++)
                                             {
                                                              SendMessage(hProgress,PBM_SETPOS,i,0);
                                                              Sleep(50);
                                             }
                              }
                              return TRUE;
                 case IDOK :
                 case IDCANCEL :
                             EndDialog (hDlg, 0) ;
                             return TRUE ;
                 }
    break ;
    }
    return FALSE ;
}
5. 애니메이트
   – 다음에 맞는 AVI파일만 재생할 수 있다.
      • 비디오 스트림이 꼭 하나만 있어야 한다.
      • 사운드 스트림은 없거나 꼭 하나만 있어야 한다.
      • 압축이 되어 있지 않거나 압축되어 있어도 RLE8포맷이어야 한다.

ACS_CENTER        애니메이트 윈도우의 크기를 유지하며 중앙에서 AVI
                  파일을 재생한다. 이 스타일이 주어지지 않으면 크기
                  가 AVI파일 크기에 맞추어 진다.
ACS_TRANSPARENT   투명 배경색을 사용하도록 한다.
ACS_AUTOPLAY      AVI 클립을 열자마자 바로 재생하도록 한다.
ACM_OPEN          AVI 클립을 연다. lParam에 열고자 하는 AVI파일명을
                  주거나 아니면 AVI리소스의 ID를 LOWORD(lParam)
                  에 대입해 주면 된다.
ACM_PLAY          AVI 클립을 재생한다. wParam으로 재생 횟수를 지정
                  하되 –이면 무한 반복 재생, LOWORD(lParam)에 재
                  생 시작 위치를 지정하며, HIWORD(lParam)에 재생
                  끝 위치를 지정한다. 0, -1 (처음부터 끝까지)
ACM_STOP          재생을 중지한다.
switch (message)
{
case WM_INITDIALOG :
            {
                       hProgress = GetDlgItem(hDlg,IDC_PROGRESS1);
                       SendMessage(hProgress,PBM_SETRANGE,0,MAKELPARAM(0,100));
                       SendMessage(hProgress,PBM_SETPOS,0,0);

                       hTrackBar = GetDlgItem(hDlg,IDC_SLIDER1);
                       SendMessage(hTrackBar,TBM_SETRANGE,0,MAKELPARAM(0,100));
                       SendMessage(hTrackBar,TBM_SETPOS,0,0);

                       hEdit = GetDlgItem(hDlg,IDC_EDIT1);
                       hEdit2 = GetDlgItem(hDlg,IDC_EDIT2);

                       hSpin = GetDlgItem(hDlg,IDC_SPIN1);
                       SendMessage(hSpin,UDM_SETRANGE,0,MAKELPARAM(1000,0));
                       SendMessage(hSpin,UDM_SETBUDDY,(WPARAM)hEdit2,NULL);
                       static UDACCEL udAccel[3] = {{0,1},{5,10},{8,20}};
                       SendMessage(hSpin,UDM_SETACCEL,(WPARAM)3,(LPARAM)udAccel);

                       HWND hAnimate = GetDlgItem(hDlg,IDC_ANIMATE1);
                       SendMessage(hAnimate,ACM_OPEN,NULL,MAKELPARAM(IDR_AVI1,0));
                       SendMessage(hAnimate,ACM_PLAY,(WPARAM)-1,MAKELPARAM(0,-1));
          }
          return TRUE ;
6. 핫키
 – 핫 키는 운영체제가 지원하는 키보드 입력 방법 중 하나이다.
 – 다른 키 입력에 비해 우선 순위가 높게 매겨져 잇다.
 – 어떤 작업을 하고 있던 중이라도 핫키 입력이 가능하며 보통 작
   업 중단이나 특별한 신호를 보내기 위해 사용된다.

 – BOOL RegisterHotKey(HWND hWnd, int id, UINT fsModifiers,
   UINT vk);
 – BOOL UnregisterHotKey( HWND hWnd, int id);
    • id : 핫키의 고유 번호
    • fsModifiers : MOD_ALT, MOD_CONTROL, MOD_SHIFT
    • vk : 가상 키코드

 – 등록된 핫키가 눌려지면 시스템은 핫키를 등록한 윈도우로
   WM_HOTKEY메시지를 보내준다.
                      RegisterHotKey(hDlg,1,MOD_ALT|MOD_CONTROL,'X');
             }
             return TRUE ;
   case WM_HOTKEY:
             MessageBox(hDlg,"핫키를 눌렀다.","핫키",MB_OK);
             return TRUE;
6. 핫키
                          RegisterHotKey(hDlg,1,MOD_ALT|MOD_CONTROL,'X');
             }
             return TRUE ;
   case WM_HOTKEY:
             MessageBox(hDlg,"핫키를 눌렀다.","핫키",MB_OK);
             return TRUE;

   case WM_COMMAND :
             switch (LOWORD (wParam))
             {
             case IDC_BUTTON1:
                         {
                                      for(int i = 0;i <= 100;i++)
                                      {
                                                      SendMessage(hProgress,PBM_SETPOS,i,0);
                                                      Sleep(50);
                                      }
                         }
                         return TRUE;
             case IDOK :
             case IDCANCEL :
                         UnregisterHotKey(hDlg,1);
                         EndDialog (hDlg, 0) ;
                         return TRUE ;
             }
   break ;
7. 날짜 선택 컨트롤
DTS_LONGDATEFORMAT    긴 날짜 포맷으로 보여준다.
DTS_SHORTDATEFORMAT   짧은 날짜 포맷으로 보여준다.
DTS_SHORTDATECENTURY 짧은 날짜로 보여주되 연도는 4자리로 표기한다.
FORMAT
DTS_TIMEFORMAT        날짜 대신 시간을 보여준다.
DTS_APPCANPARSE       사용자가 입력한 값을 분석해 보고 값에 따라
                      특정한 동작을 할 수 있도록 통지 메시지를 보
                      내준다.
DTS_RIGHTALIGN        달력 컨트롤은 DTP의 왼쪽에 펼쳐지는데 이 스
                      타일을 지정하면 오른쪽에 펼쳐진다.
DTS_SHOWNONE          선택된 날짜가 없음을 지정하는 체크 박스를 포
                      함한다.
DTS_UPDOWN            달력 컨트롤을 사용하지 않고 업다운 컨트롤로
                      값을 편집하도록 한다.
7. 날짜 선택 컨트롤
 – DWORD DateTime_GetSystemtime(HWND hwndDP,
   LPSYSTEMTIME pSysTime)
    • 사용자가 선택한 날짜를 조사한다.
 – BOOL DateTime_SetSystemtime(HWND hwndDT, DWORD
   flag, LPSYSTEMTIME lpSysTime);
    • flag : GDT_VALID



                      hDTP = GetDlgItem(hDlg,IDC_DATETIMEPICKER1);
            }
            return TRUE ;
   case WM_HOTKEY:
            {
                      SYSTEMTIME st;
                      DateTime_GetSystemtime(hDTP,&st);
                      char temp[256];
                      wsprintf(temp,"%d년 %d월 %d일",st.wYear,st.wMonth,st.wDay);
                      MessageBox(hDlg,temp,"선택한 날짜",MB_OK);
            }
            return TRUE;
8. 상태란
   – HWND CreateStatusWindow(LONG style, LPCTSTR lpszText,
     HWND hwndParent, UINT wID);
      •   상태란을 만드는 함수이다.
      •   style : WS_CHILD | WS_VISIBLE
      •   lpszText : 상태란에 나타날 초기 문자열
      •    hwndParent : 부모 윈도우의 핸들
      •   wID : 상태란의 ID이다.

SB_GETTEXT       wParam으로 지정한 파트의 텍스트를 구한다.lParam으로 텍
                 스트를 돌려받기 위한 버퍼 번지를 넘겨준다.
SB_SETTEXT       wParam으로 지정한 파트의 텍스트를 설정한다. lParam으로
                 문자영을 넘겨준다.
SB_GETTEXTLENG wParam으로 지정한 파트의 문자열 길이를 구한다. 리턴값
TH             의 하위 워드를 읽으면 길이를 구할 수 있다.
SB_SETPARTS      파트를 설정한다. wParam으로 파트의 개수를 주며, lParam
                 으로 각 파트의 오른쪽 끝 좌표를 가진 정수형 배열을 준다.
SB_GETRECT       wParam으로 지정한 파트의 영역을 구한다.
                      hStatus = CreateStatusWindow(WS_CHILD|WS_VISIBLE, "Status Line", hDlg, 0);
                      int PartArray[3] = {100,300,-1};
                      SendMessage(hStatus,SB_SETPARTS,(WPARAM)3,(LPARAM)PartArray);
          }
          return TRUE ;
case WM_MOUSEMOVE:
          {
                       char temp[256];
                       wsprintf(temp,"x => %d. y => %d",LOWORD(lParam),HIWORD(lParam));
                       SendMessage(hStatus,SB_SETTEXT,(WPARAM)1,(LPARAM)temp);
          }
          return TRUE;
 9. 프로퍼티 시트
    – int PropertySheet( LPCPROPSHEETHEADER lppsph );
        • 프로퍼티 시트를 만든다.

typedef struct _PROPSHEETPAGE {
        DWORD           dwSize;
        DWORD           dwFlags;
        HINSTANCE       hInstance;
        union { LPCSTR pszTemplate; LPCDLGTEMPLATE pResource; };
        union { HICON hIcon; LPCSTR pszIcon; };
        LPCSTR          pszTitle;
        DLGPROC         pfnDlgProc;
        LPARAM          lParam;
        LPFNPSPCALLBACK pfnCallback;
        UINT *          pcRefParent;
        #if (_WIN32_IE >= 0x0500)
                 LPCTSTR          pszHeaderTitle;
                 LPCTSTR          pszHeaderSubTitle;
        #endif
        #if (_WIN32_WINNT >= 0x0501) HANDLE hActCtx; #endif
} PROPSHEETPAGE, *LPPROPSHEETPAGE;
9. 프로퍼티 시트
dwSize        구조체의 크기
dwFlags       페이지의 속성과 유효한 멤버를 지정
hInstance     대화상자 템플리트, 아이콘, 문자열 리소스를 가진 인스턴스
              핸들
pszTemplate   대화상자 템플리트이다.
pResource     dwFlags에 PSP_DLGINDIRECT플래그가 설정되어 있을 때만
              사용된다.
hIcon         페이지 탭에 출력될 아이콘의 핸들. dwFlags에
              PSP_USEHICON이 없으면 무시된다.
pszIcon       페이지 탭에 출력될 아이콘 리소스이며 아이콘의 ID이거나 아
              이콘 리소스의 이름 문자열이다. dwFlags에 PSP_USEHICON
              이 없으면 무시된다
pszTitle      페이지 탭에 출력할 문자열을 지정한다. dwFlags에
              PSP_USETITLE플래그가 있어야먄 이 멤버가 사용되며 그렇지
              않을 경우 대화상자 템플릿의 타이틀이 사용된다.
pfnDlgProc    대화상자 프로시저의 포인터이며 대화상자의 메시지를 처리한
              다. EndDialog를 호출해서는 안 된다.
9. 프로퍼티 시트
PSP_DEFAULT       아무 플래그도 지정하지 않는다.
PSP_DLGINDIRECT   pResource 멤버가 지정하는 메모리의 대화상자 템플릿
                  으로 부터 페이지를 만든다.
PSP_HASHELP       이 페이지가 활성화될 때 도움말 버튼을 보여준다.
PSP_USEHICON      hIcon멤버가 지정하는 아이콘을 탭에 출력한다.
PSP_USEICONID     pszIcon멤버가 지정하는 리소스에서 아이콘을 읽어 탭
                  에 출력한다.
PSP_USETITLE      대화상자 템플리트에 지정된 문자열 대신 pszTitle멤버
                  의 문자열을 탭 페이지에 출력한다.
9. 프로퍼티 시트
  – 시트 구조체
      • PROPSHEETPAGE 구조체는 프로퍼티 시트에 포함된 개별 페이지에
        대한 구조체이다.

typedef struct _PROPSHEETHEADER {
        DWORD           dwSize;
        DWORD           dwFlags;
        HWND            hwndParent;
        HINSTANCE       hInstance;
        union { HICON hIcon; LPCTSTR pszIcon; };
        LPCTSTR         pszCaption;
        UINT            nPages;
        union { UINT nStartPage; LPCTSTR pStartPage; };
        union { LPCPROPSHEETPAGE ppsp; HPROPSHEETPAGE *phpage; };
        PFNPROPSHEETCALLBACK pfnCallback;
        #if (_WIN32_IE >= 0x0500)
        union { HBITMAP hbmWatermark; LPCTSTR pszbmWatermark; };
        HPALETTE hplWatermark;
        union { HBITMAP hbmHeader; LPCSTR pszbmHeader; };
        #endif
} PROPSHEETHEADER, *LPPROPSHEETHEADER;
9. 프로퍼티 시트
dwSize       이 구조체의 크기
dwFlags      구조체 중 어떤 멤버가 사용될 것인가를 지정한다.
hwndParent   부모 윈도우의 핸들
hInstance    아이콘과 문자열 리소스를 가진 인스턴스 핸들
hIcon        아이콘 핸들
pszIcon      아이콘 리소스
pszCaption   프로퍼티 시트의 타이틀 문자열을 지정한다.
nPages       프로퍼티 페이지의 개수
nStartPage   프로퍼티 시트가 만들어질 때 처음 나타날 시작 페이지
pStartPage   프로퍼티 시트가 만들어질 때 처음 나타날 시작 페이지
             의 이름
ppsp         프로퍼티 시트에 포함될 개별 페이지를 정의한
             PROPSHEETPAGE 구조체의 배열
phpage       각 페이지의 핸들을 담은 배열의 포인터. 개별 페이지는
             CreatePropertySheetPage함수로 만들 수 있다.
9. 프로퍼티 시트
PSH_DEFAULT      아무 플래그도 지정하지 않는다.
PSH_HASHELP      도움말 버튼을 만든다.
PSH_MODELLESS    모델리스형의 프로퍼티 시트를 만든다.
PSH_NOAPPLYNOW 적용 버튼을 제거한다.
PSH_PROPSHEETP   ppsp멤버를 사용한다. 즉 이 구조체 배열의 정보를 참
AGE              조하여 각 페이지를 생성시킨다.
PSH_PROPTITLE    프로퍼티 시트의 타이틀 뒤에 “등록정보”를 덧붙인다.
PSH_USEHICON     hIcon멤버가 지정하는 아이콘을 사용한다.
PSH_USEICONID    pszIcon 멤버가 지정하는 리소스의 아이콘을 사용한다.
PSH_USESTARTPA   nStartPage가 지정하는 시작 페이지 대신 pStartPage
GE               멤버가 지정하는 페이지를 사용한다.
PSH_WIZARD       마법사 형식의 프로퍼티 시트를 만든다.
#include <windows.h>
#include "resource.h"
#include <commctrl.h>

HINSTANCE hInst;
BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
              hInst = hInstance;
              DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1),NULL,DlgProc);
              return 0;
}

BOOL CALLBACK DialogProc1 (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK DialogProc2 (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
           switch (message)
           {
           case WM_INITDIALOG :
                          return TRUE ;
           case WM_COMMAND :
                          switch (LOWORD (wParam))
                          {
                          case IDC_BUTTON1:
                                        {
                                                    PROPSHEETPAGE psp[2];
                                                    PROPSHEETHEADER psh;

                                                      psp[0].dwSize = sizeof(PROPSHEETPAGE);
                                                      psp[0].dwFlags = PSP_DEFAULT;
                                                      psp[0].hInstance = hInst;
                                                      psp[0].pszTemplate = MAKEINTRESOURCE(IDD_DIALOG2);
                                                      psp[0].pfnDlgProc=(DLGPROC)DialogProc1;
                                                      psp[0].lParam = 0;

                                                      psp[1].dwSize = sizeof(PROPSHEETPAGE);
                                                      psp[1].dwFlags = PSP_DEFAULT;
                                                      psp[1].hInstance = hInst;
                                                      psp[1].pszTemplate = MAKEINTRESOURCE(IDD_DIALOG3);
                                                      psp[1].pfnDlgProc=(DLGPROC)DialogProc2;
                                                      psp[1].lParam = 0;
                                                         psh.dwSize = sizeof(PROPSHEETHEADER);
                                                         psh.dwFlags = PSH_PROPSHEETPAGE | PSH_NOAPPLYNOW;
                                                         psh.hwndParent = hDlg;
                                                         psh.pszCaption = "프로퍼티 페이지";
                                                         psh.nPages = 2;
                                                         psh.nStartPage = 0;
                                                         psh.ppsp = psp;

                                                         PropertySheet(&psh);
                                          }
                                          return TRUE;
                             case IDOK :
                             case IDCANCEL :
                                         EndDialog (hDlg, 0) ;
                                         return TRUE ;
                             }
            break ;
            }
            return FALSE ;
}

BOOL CALLBACK DialogProc1 (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
           return FALSE ;
}

BOOL CALLBACK DialogProc2 (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
           return FALSE ;
}
9. 프로퍼티 시트
PSN_APPLY         적용 버튼을 눌렀다.
PSN_HELP          도움말 버튼을 눌렀다.
PSN_KILLACTIVE    다른 페이지를 선택하거나 대화상자가 닫히기 직전이
                  다.
PSN_QUERYCANCEL   취소버튼을 눌렀을 때 확인한다.
PSN_RESET         취소 버튼을 눌렀다
PSN_SETACTIVE     이 페이지가 활성화되지 직전이다.
PSN_WIZBACK       BACK 버튼을 눌렀다.
PSN_WIZFINISH     종료 버튼을 눌렀다.
PSN_WIZNEXT       다음 버튼을 눌렀다.
9. 프로퍼티 시트
   – 프로퍼티 시트의 메시지
PSM_ADDPAGE      제일 끝에 페이지를 추가한다.
                 CreatePropertySheetPage함수로 페이지를 만든 후 그
                 핸들을 lParam으로 전달한다.
PSM_APPLY        적용 버튼을 누른 것과 같은 효과를 낸다.
PSM_CANCELTOCL   취소 버튼을 사용 금지시키고 확인 버튼의 캡션을 닫기
OSE              로 바꾼다.
PSM_CHANGED      페이지의 정보가 변경되었음을 알린다.프로퍼티 시트
                 는 이 메시지를 받으면 적용 버튼을 활성화시킨다.
PSM_GETCURRENT   현재 페이지의 윈도우 핸들을 구해 리턴해 준다.
PAGEHWND
PSM_PRESSBUTTO   wParam으로 지정한 버튼을 누른 것과 같은 효과를 낸
N                다. wParam으로 전달되는 값은 PSBTN_*인데 *는
                 APPLYNOW, BACK, CANCEL, FINISH, HELP,
                 NEXT,OK중 하나이다.
PSM_SETCURSEL    지정한 페이지를 활성화시킨다. wParam으로 제거할
                 페이지의 인덱스를, lParam으로 제거할 페이지의 핸들
                 을 전달해 준다. (둘 중 하나나 둘 다 ) 핸들 우선
10. 이미지 리스트
 – HIMAGELIST ImageList_LoadBitmap( HINSTANCE hi, LPCSTR
   lpbmp, int cx, int cGrow, COLORREF crMask);
    • 지정한 비트맵 리소스를 읽어와 이미지 리스트를 만들고 그 핸들을
      리턴 해 준다.
    • lpbmp : 비트맵 리소스의 이름
    • cx : 각 이미지의 폭
    • cGrow : 이미지 리스트는 이미지가 추가될 때마다 메모리를 재할당
      하여 추가된 이미지를 저장한다. 한꺼번에 할당할 메모리 양을 지정
      한다.
    • crMask : 투명색으로 사용할 색상을 지정

 – HIMAGELIST ImageList_LoadImage( HINSTANCE hi, LPCSTR
   lpbmp, int cx, int cGrow, COLORREF crMask, UINT uType,
   UINT uFlags);
    • 이 함수는 비트맵뿐만 아니라 아이콘과 커서까지도 읽을 수 있다.
    • uType : IMAGE_BITMAP, IMAGE_CURSOR, IMAGE_ICON
    • uFlags :

 – ImageList_Destroy()
    • 이미지 리스트를 파괴한다.
10. 이미지 리스트
LR_DEFAULTCOLOR    디스플레이의 색상 포맷을 사용한다.
LR_LOADDEFAULTSI   cx가 0일 경우 아이콘과 커서의 크기를 시스템 메트
ZE                 릭스 크기로 사용한다. cx가 0이고 이 플래그가 설정
                   되어 있지 않으면 리소스에 정의된 크기가 사용된다.
LR_LOADFROMFILE    파일로부터 이미지를 읽어오며 이때 lpbmp인수는 파
                   일명을 지정한다.

  – BOOL ImageList_Draw( HIMAGELIST himl , int i, HDC hdcDst,
    int y, UINT fStyle);
     •   이미지 리스트의 이미지를 화면에 출력한다.
     •   himl : 이미지 리스트의 핸들
     •   i : 이미지 번호
     •   hdcDst : 출력 대상 DC
     •   x,y : 좌표
     •   fStyle : IDL_NORMAL : 배경색상으로 이미지를 출력한다.
     •            IDL_TRANSPARENT : 마스크를 사용하여 투명한 이미지를
         그린다.
10. 이미지 리스트
  – BOOL ImageList_SetOverlayImage( HIMAGELIST himl, int
    iImage, int iOverlay);
      • image 번째의 이미지를 iOverlay 번째의 오버레이 이미지로 지정한
        다.

HIMAGELIST IL = ImageList_LoadBitmap(g_hInst,
        MAKEINTRESOURCE(IDB_BITMAP1), 32, 1, RGB(0,0,255));
ImageList_SetOverlayImage(IL,1,1);
ImageList_Draw(IL,0,hdc, 50,50, ILD_NORMAL);
ImageList_Draw(IL,0,hdc, 50,50, ILD_NORMAL|INDEXTOOVERLAYMASK(1));
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
          HDC                     hdc;
          PAINTSTRUCT             ps ;
          RECT        rect ;
          static HINSTANCE hInst;
          static HIMAGELIST IL;

          switch (message)
          {
          case WM_CREATE:
                      hInst = ((LPCREATESTRUCT)lParam)->hInstance;
                      IL = ImageList_LoadBitmap(hInst,MAKEINTRESOURCE(IDB_BITMAP1),16,1,RGB(192,192,192));
                      ImageList_SetOverlayImage(IL,1,1);
                      return 0 ;
          case WM_LBUTTONDOWN:
                      hdc = GetDC(hwnd);
                      ImageList_Draw(IL,0,hdc,50,50,ILD_NORMAL);
                      ImageList_Draw(IL,0,hdc,100,100,ILD_NORMAL|INDEXTOOVERLAYMASK(1));
                      ReleaseDC(hwnd,hdc);
                      return 0;
          case WM_PAINT:
                      hdc = BeginPaint (hwnd, &ps) ;
                      GetClientRect (hwnd, &rect) ;
                      DrawText (hdc, "Hello, Windows 98!", -1, &rect,DT_SINGLELINE |
                      DT_CENTER | DT_VCENTER) ;
                      EndPaint (hwnd, &ps) ;
                      return 0 ;
          case WM_DESTROY:
                      PostQuitMessage (0) ;
                      return 0 ;
          }
          return DefWindowProc (hwnd, message, wParam, lParam) ;
}
11. 리스트 뷰
LVS_ICON              큰 아이콘 모양
LVS_SMALLICON         작은 아이콘 모양
LVS_LIST              리스트 모양
LVS_REPORT            레포트 모양
LVS_AUTOARRANGE       아이콘들이 자동으로 정렬된다
LVS_SINGLESEL         리스트 뷰 컨트롤은 디폴트로 여러 개의 항목을 선
                      택할 수 있다.
LVS_SHAREIAMGELISTS   리스트 뷰 컨트롤은 파괴될 때 자신에게 등록된 이
                      미지 리스트를 같이 파괴시키며 사용자가 이미지 리
                      스트를 직접 파괴시켜주지 않아도 된다. 이 스타일
                      을 지정하면 이미지 리스트를 사용이 끝난 후에 사
                      용자가 직접 파괴해 주어야 한다.
LVS_NOLABELWRAP       문자열이 나타나는데 문자열이 길 경우에는 두 줄로
                      나누어 출력된다. 이 옵션을 선택하면 문자열이 두
                      줄로 나타나지 않는다.
LV_OWNERDRAWFIXED     리스트 뷰 컨트롤의 부모 윈도우가 항목을 직접 그
                      리도록 한다.WM_DRAWITEM메시지를 부모 윈도우
                      로 보낸다.
11. 리스트 뷰
LVM_INSERTCOLUMN     새 컬럼을 추가한다. LV_COLUMN에 추가하고자 하
                     는 컬럼의 정보를 기입하고 wParam에 컬럼 인덱스,
                     lParam에 LV_COLUMN구조체의 포인터를 전달.
LVM_INSERTITEM       새 항목을 추가한다. LV_ITEM 구조체에 추가하고자
                     하는 항목의 정보를 기입하고 lParam으로 그 포인
                     터를 전달한다.
LVM_SETIMAGELIST     리스트 뷰 컨트롤과 연결될 이미지 리스트를 지정한
                     다. lParam으로 이미지 리스트의 핸들을 전달.
                     wParam으로 이미지 리스트의 종류를 지정한다.
                     이미지 리스트의 종류는 LVSIL_NORMAL,
                     LVSIL_SMALL, LVSIL_STATE중 하나이다.
LVM_SETITEM          항목의 속성을 변경한다. lParam으로 LV_ITEM 구조
                     체의 포인터를 전달한다.
LVM_DELETEITEM       항목을 삭제한다. wParam에 삭제할 항목의 인덱스
                     를 전달해 준다.
LVM_DELETEALLITEMS   모든 항목을 삭제한다.
LVM_GETITEM          항목의 속성을 조사한다.
LVM_GETNEXTITEM      조건에 맞는 항목을 조사한다.
11. 리스트 뷰
LVN_BEGINDRAG        왼쪽 마우스 버튼으로 항목을 드래그하기 시작할 때
                     발생한다.
LVN_BEGINLABELEDIT   레이블을 편집할 때 발생한다.
LVN_BEGINRDRAG       오른쪽 마우스 버튼으로 항목을 드래그하기 시작할 때
                     발생한다.
LVN_COLUMNCLICK      헤어 컨트롤이 클릭될 때 발생한다.
LVN_DELETEALLITEM    리스트 뷰 컨트롤의 모든 항목이 삭제될 때 발생한다.
LVN_DELETEITEM       한 항목이 삭제될 때 발생한다.
LVN_ENDLABELEDIT     레이블 편집이 완료되었을 때 발생한다.
LVN_GETDISPINFO      항목의 출력이나 정렬을 위한 정보를 부모 윈도우에게
                     요청한다.
LVN_INSERTITEM       새로운 항목이 삽입될 때 발생한다.
LVN_ITEMCHANGED      사용자가 다른 항목을 선택했을 때 발생한다.
LVN_ITEMCHANGING     사용자가 다른 항목을 선택하려고 할 때 발생한다.
LVN_KEYDOWN          리스트 뷰 컨트롤이 포커스를 가지고 있는 상태에서
                     키보드 입력이 있을 때 발생
BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
           static HIMAGELIST hIL;
           static HWND      hListView;
           switch (message)
           {
           case WM_INITDIALOG :
                         {
                                       hIL = ImageList_LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1), 16, 1, RGB(192,192,192));
                                       hListView = GetDlgItem(hDlg,IDC_LIST1);
                                       SendMessage(hListView,LVM_SETIMAGELIST,(WPARAM)LVSIL_SMALL,(LPARAM)hIL);
                                       SendMessage(hListView,LVM_SETIMAGELIST,(WPARAM)LVSIL_NORMAL,(LPARAM)hIL);
                                       LVCOLUMN col;

                                        col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
                                        col.fmt = LVCFMT_LEFT;
                                        col.cx = 100;
                                        col.pszText = "이름";
                                        col.iSubItem = 0;
                                        SendMessage(hListView,LVM_INSERTCOLUMN,0,(LPARAM)&col);

                                        col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
                                        col.fmt = LVCFMT_LEFT;
                                        col.cx = 100;
                                        col.pszText = "주소";
                                        col.iSubItem = 1;
                                        SendMessage(hListView,LVM_INSERTCOLUMN,1,(LPARAM)&col);

                                        col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
                                        col.fmt = LVCFMT_LEFT;
                                        col.cx = 100;
                                        col.pszText = "전화번호";
                                        col.iSubItem = 2;
                                        SendMessage(hListView,LVM_INSERTCOLUMN,2,(LPARAM)&col);

                                        LVITEM li;
                                        li.mask = LVIF_TEXT | LVIF_IMAGE;
                                        li.state = 0;
                                        li.stateMask = 0;
                                 li.iImage = 0;
                                 li.iSubItem = 0;
                                 li.iItem = 0;
                                 li.pszText = "홍길동";
                                 SendMessage(hListView, LVM_INSERTITEM, 0 , (LPARAM)&li);

                                 li.iSubItem = 1;
                                 li.pszText = "서울";
                                 SendMessage(hListView, LVM_SETITEM, 0 , (LPARAM)&li);

                                 li.iSubItem = 2;
                                 li.pszText = "123-1234";
                                 SendMessage(hListView, LVM_SETITEM, 0 , (LPARAM)&li);

                                 li.iImage = 1;
                                 li.iSubItem = 0;
                                 li.iItem = 1;
                                 li.pszText = "김길동";
                                 SendMessage(hListView, LVM_INSERTITEM, 0 , (LPARAM)&li);

                                 li.iSubItem = 1;
                                 li.pszText = "강원도";
                                 SendMessage(hListView, LVM_SETITEM, 0 , (LPARAM)&li);

                                 li.iSubItem = 2;
                                 li.pszText = "234-2345";
                                 SendMessage(hListView, LVM_SETITEM, 0 , (LPARAM)&li);
                  }

                   return TRUE ;
    case WM_COMMAND :
                   switch (LOWORD (wParam))
                   {
                   case IDOK :
                   case IDCANCEL :
                                 EndDialog (hDlg, 0) ;
                                 return TRUE ;
                   }
    break ;
    }
    return FALSE ;
}
 11. 리스트 뷰
typedef struct _LVCOLUMN {
        UINT            mask;
        int             fmt;
        int             cx;
        LPTSTR          pszText;
        int             cchTextMax;
        int             iSubItem;
        #if (_WIN32_IE >= 0x0300)
                 int    iImage;
                 int    iOrder;
        #endif
} LVCOLUMN, *LPLVCOLUMN;

LVCF_FMT              fmt 멤버에 있는 값을 사용한다.
LVCF_SUBITEM          iSubItem 멤버에 있는 값을 사용한다.
LVCF_TEXT             pszText 멤버에 있는 값을 사용한다.
LVCF_WIDTH            cx 멤버에 있는 값을 사용한다.
LVCF_IMAGE            image 멤버에 있는 값을 사용한다.
LVCF_ORDER            iOrder 멤버에 있는 값을 사용한다.
11. 리스트 뷰
LVCFMT_LEFT         문자열을 왼쪽으로 정렬한다.
LVCFMT_RIGHT        문자열을 오른쪽으로 정렬한다.
LVCFMT_CENTER       문자열을 중앙으로 정렬한다.
LVCFMT_BITMAP_ON_   비트맵을 문자열의 오른쪽으로 배치한다.
RIGHT
LVCFMT_COL_HAS_IM   헤더에 이미지가 나타난다.
AGE
LVCFMT_IMAGE        이미지 리스트의 이미지를 출력한다.

pszText             헤더에 나타날 문자열을 지정
cchTextMax          pszText멤버의 크기를 지정. 헤더를 만들 때는 사용
                    하지 않는다.
cx                  헤더의 폭을 픽셀 단위로 지정
iSubItem            리스트 뷰 컨트롤의 몇 번째 세부 항목에 대한 제목인
                    가를 지정한다.
iImage              컬럼에 나타날 이미지의 인덱스
iOrder              컬럼의 순서를 지정
11. 리스트 뷰
• 선택 항목 조사
   – 리스트 뷰는 항목에 변경이 발생하기 전에 LVN_ITEMCHANGING
     통지 메시지를 보내주고 변경이 발생한 후에
     LVN_ITEMCHANGED 통지 메시지를 보내준다.
   – 이 두 메시지의 lPara에는 어떠한 변경이 발생했는지에 대한 정보
     인 NMLISTVIEW 구조체의 포인터가 전달된다.

typedef struct tagNMLISTVIEW {
        NMHDR           hdr;
        int             iItem;
        int             iSubItem;
        UINT            uNewState;
        UINT            uOldState;
        UINT            uChanged;
        POINT           ptAction;
        LPARAM          lParam;
} NMLISTVIEW, *LPNMLISTVIEW;
typedef struct tagNMHDR {
        HWND           hwndFrom;
        UINT           idFrom;
        UINT code;
} NMHDR;
case WM_NOTIFY:
{
         LPNMHDR                        hdr;
         LPNMLISTVIEW        nlv;
         hdr = (LPNMHDR)lParam;
         nlv = (LPNMLISTVIEW)lParam;

           if (hdr->hwndFrom == hListView)
           {
                      switch(hdr->code)
                      {
                      case LVN_ITEMCHANGED:
if(nlv->uChanged == LVIF_STATE && nlv->uNewState == (LVIS_SELECTED|LVIS_FOCUSED))
                                 {
                                 char szName[255];
                                 char szAddr[255];
                                 char szTelNo[255];
                                 ListView_GetItemText(hListView, nlv->iItem,0,szName,255);
                                 ListView_GetItemText(hListView, nlv->iItem,1,szAddr,255);
                                 ListView_GetItemText(hListView, nlv->iItem,2,szTelNo,255);
                                 char temp[256];
                      wsprintf(temp,"이름 : %s,주소 : %s,전화번호 : %s",szName, szAddr,szTelNo);
                                 MessageBox(hDlg,temp,"",MB_OK);
                                 }
                      }
           }
}
case WM_NOTIFY:
{
         LPNMHDR                         hdr;
         LPNMLISTVIEW         nlv;
         hdr = (LPNMHDR)lParam;
         nlv = (LPNMLISTVIEW)lParam;
         if (hdr->hwndFrom == hListView)
         {
                    switch(hdr->code)
                    {
                    case NM_DBLCLK:
                              {
                              LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE) lParam;
                                         if (nlv->iItem != -1)
                                         {
                                                      char temp[256];
         ListView_GetItemText(hListView,lpnmitem->iItem,lpnmitem->iSubItem,temp,256);
                                                      MessageBox(NULL,temp,"",MB_OK);
                                         }
                              }
                    }
         }
}
12. 트리 뷰
 – 트리 뷰 컨트롤 스타일
   • TVS_HASBUTTONS
      – 자식 항목을 가진 부모 항목 옆에 +, - 버튼을 보여 준다.
   • TVS_HASLINES
      – 항목간의 계층구조를 좀 더 명확히 보여주기 위해 점선으로 항목간을 연
        결하여 표시하도록 한다.
   • TVS_LINESATROOT
      – 루트 항목끼리 선으로 연결한다. 단 이 스타일은 TVS_HASLINES속성이
        선택되어 있을 때만 효력을 발휘한다.
   • TVS_EDITLABES
      – 사용자가 항목의 텍스트를 직접 수정할 수 있도록 해준다.
   • TVS_DISABLEDRAGDROP
      – 스타일의 이름대로 항목을 드래그하지 못하도록 한다.
   • TVS_SHOWSELALWAYS
      – 트리 뷰가 포커스를 가지지 않은 상태에서 선택된 항목이 반전된 상태로
        남아 있도록 한다.
   • TVS_CHECKBOXES
      – 항목 옆에 체크 박스를 보여준다.
12. 트리 뷰
  – 트리 뷰 컨트롤 메시지
TVM_CREATEDRAGIMAGE 이미지 리스트를 만들고 lParam으로 지정한 항목
                    의 드래그 비트맵을 만들어 이미지 리스트에 포함
                    시킨다.
TVM_DELETEITEM      lParam으로 지정한 항목을 삭제한다. lParam이
                    TVI_ROOT이면 트리의 모든 항목이 삭제된다.
TVM_EDITLABLE       lParam으로 지정한 항목을 즉시 편집한다. 이 메
                    시지 후에 곧바로 TVN_BEGINLABELEDIT통지 메
                    시지가 전달된다.
TVM_EDITLABLENOW    편집중인 항목의 편집을 중단하도록 한다.
                    wParam이 TRUE이면 편집 동작은 취소되며,
                    FALSE이면 편집동작을 저장한 후 끝낸다.
TVM_ENSUREVISIBLE   lParam으로 지정한 항목이 화면에 보이도록 한다.
TVM_EXPAND          lParam으로 지정된 항목을 확장하거나 축소한다.
                    wParam은 다음 중 하나이다.
                    TVE_COLLAPSE : 축소한다.
                    TVE_COLLAPSERESET : 축소함과 동시에 차일드
                    를 파괴한다.
12. 트리 뷰
                     TVE_EXPAND : 확장한다.
                     TVE_TOGGLE : 확장되어 있으면 축소하고 축소되
                     어 있으면 확장한다.
TVM_GETCOUNT         항목의 개수를 구한다.
TVM_GETEDITCONTROL   항목 편집에 사용되는 에디트 컨트롤의 핸들을 구
                     한다.
TVM_GETIMAGELIST     트리 컨트롤과 연결된 이미지 리스트의 핸들을 구
                     한다. wParam에는 TVSIL_NORMAL,
                     TVSIL_STATE중 하나를 지정한다.
TVM_GETITEM          특정 항목을 찾는다.찾고자 하는 항목의 정보를
                     TVITEM구조체의 hItem멤버에 대입한 후 이구조체
                     를 lParam으로 넘겨주면 구조체에 항목의 정보를
                     채워준다.
TVM_GETITEMRECT      항목이 차지하고 있는 사각영역을 구한다.
                     lParam으로 RECT구조체를 넘겨주고, wParam이
                     TRUE이면 문자열 영역에 대해서만 사각영역이 구
                     해지며, FALSE이면 선을 포함한 영역까지 다 구해
                     준다.
12. 트리 뷰
TVM_GETNEXTITEM    lParam으로 지정한 항목과 wParam의 관계를 가
                   지는 항목을 구한다. wParam : TVGN_CHILD,
                   TVGN_NEXT
TVM_HITTEST        특정한 한 점이 트리 컨트롤의 어디쯤인가를 조사
                   한다.
TVM_INSERTITEM     lParam으로 전달되는 TV_INSERTSTRUCT구조체
                   의 정보를 참조하여 항목을 추가한다.
TVM_SELECTITEM     lParam으로 지정한 항목을 선택하거나 보이게 한
                   다.
TVM_SETIMAGELIST   트리 컨트롤과 연결될 이미지 리스트를 지정한다.
                   lParam으로 이미지 리스트의 핸들, wParam으로
                   TVSIL_NORMAL, TVSIL_STATE
TVM_SETITEM        항목의 속성을 설정한다. TVITEM구조체를 작성한
                   후 lParam으로 전달한다.
12. 트리 뷰
  – 트리 뷰 컨트롤 통지 메시지
TVN_BEGINDRAG        왼쪽 마우스 버튼으로 항목을 드래그하기 시작할 때
                     발생한다.
TVN_BEGINLABELEDIT   레이블을 편집할 때 발생한다.
TVN_DELTEITEM        한 항목이 삭제될 때 발생한다.
TVN_ENDLABELEDIT     레이블 편집이 완료되었을 때 발생한다.
TVN_GETDISPINFO      항목의 출력이나 정렬을 위한 정보를 부모 윈도우에게
                     요청한다.
TVN_ITEMEXPANDED     트리의 확장 또는 축소되었을 때 발생한다.
TVN_KEYDOWN          트리 컨트롤이 포커스를 가지고 있는 상태에서 키보드
                     입력이 있을 때 발생한다.
TVN_SELCHANGED       사용자가 다른 항목을 선택했을 때 발생한다.
TVN_SELCHANGING      사용자가 다른 항목을 선택하려고 할 때 발생한다.
TVN_SETDISPINFO      부모 윈도우가 정력이나 출력을 위해 유지하고 있는
                     항목의 정보가 갱신되어야 함을 알린다.
 12. 트리 뷰
    – TVINSERTSTRUCT


typedef struct tagTVINSERTSTRUCT {
        HTREEITEM          hParent;
        HTREEITEM          hInsertAfter;
        #if (_WIN32_IE >= 0x0400)
                  union { TVITEMEX itemex; TVITEM item; }
                  DUMMYUNIONNAME;
        #else
                  TVITEM item;
        #endif
} TVINSERTSTRUCT, *LPTVINSERTSTRUCT;
 12. 트리 뷰
    – TVITEM

typedef struct tagTVITEM
{
        UINT               mask;
        HTREEITEM          hItem;
        UINT               state;
        UINT               stateMask;
        LPTSTR             pszText;
        int                cchTextMax;
        int                iImage;
        int                iSelectedImage;
        int                cChildren;
        LPARAM             lParam;
} TVITEM, *LPTVITEM;

    – mask :
        • TVIF_CHILDREN : cChildren 멤버
        • TVIF_HANDLE : hItem 멤버
        • TVIF_IMAGE : iImage 멤버
12. 트리 뷰
    •   TVIF_PARAM : lParam 멤버
    •   TVIF_SELECTEDIMAGE : iSelectedImage 멤버
    •   TVIF_STATE : state, stateMask 멤버
    •   TVIF_TEXT : pszText, cchTextMax멤버
 – state, stateMask
    •   TVIS_BOLD : 두꺼운 문자
    •   TVIS_CUT : 잘라내기 된 상태이며 흐리게 나타난다.
    •   TVIS_DROPHILITED : 드래그 앤 드롭의 타겟이 되어 잇는 상태
    •   TVIS_EXPENDED : 현재 항목이 확장되어 있는 상태이며 세부 항목들
        이 보이는 상태
    •   TVIS_EXPANEDEONCE : 현재 항목이 최소한 한 번 이상 확장된 적이
        있는 상태
    •   TVIS_EXPANDPARTIAL : 항목이 부분적으로 확장되어 있다. 세부 항
        목이 보이기는 하지만 전부 다 보이는 상태는 아니다.
    •   TVIS_OVERLAYMASK : 항목이 그려질 때 오버레이 이미지가 포함되
        어 있다.
    •   TVIS_SELECTED : 선택되어 있는 상태이며 일반적으로 파란색으로
        반전된다.
12. 트리 뷰
 – pszText
    • 항목의 실제 내용이 되는 문자열
 – cchTextMax
    • pszText의 길이를 지정한다.
 – iImage
    • 항목의 왼쪽에 나타날 이미지의 번호를 지정한다.
 – iSelectedImage
    • 항목이 선택되었을 때 나타날 이미지의 번호를 지정한다.
 – cChildren
    • 이 항목이 세부 항목을 가지고 있는지를 조사한다.
BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
           static HIMAGELIST hIL;
           static HWND      hTreeView;
           switch (message)
           {
           case WM_INITDIALOG :
                         {
                                       hIL = ImageList_LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1), 16, 1, RGB(192,192,192));
                                       hTreeView = GetDlgItem(hDlg,IDC_TREE1);
                                       SendMessage(hTreeView,TVM_SETIMAGELIST,(WPARAM)TVSIL_NORMAL,(LPARAM)hIL);

                                           TVINSERTSTRUCT tvi;
                                           tvi.hParent = 0;
                                           tvi.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
                                           tvi.item.iImage = 0;
                                           tvi.item.iSelectedImage = 4;
                                           tvi.item.pszText = "친구";
                                           HTREEITEM hRoot = (HTREEITEM)SendMessage(hTreeView,TVM_INSERTITEM,0,(LPARAM)&tvi);

                                           tvi.hParent = hRoot;
                                           tvi.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
                                           tvi.item.iImage = 0;
                                           tvi.item.iSelectedImage = 4;
                                           tvi.item.pszText = "홍길동";
                                           SendMessage(hTreeView,TVM_INSERTITEM,0,(LPARAM)&tvi);

                                           tvi.hParent = hRoot;
                                           tvi.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
                                           tvi.item.iImage = 0;
                                           tvi.item.iSelectedImage = 4;
                                           tvi.item.pszText = "김길동";
                                           SendMessage(hTreeView,TVM_INSERTITEM,0,(LPARAM)&tvi);
                           }
                           return TRUE ;
    case WM_NOTIFY:
                {
                }
    case WM_COMMAND :
                switch (LOWORD (wParam))
                {
                case IDOK :
                case IDCANCEL :
                            ImageList_Destroy(hIL);
                            EndDialog (hDlg, 0) ;
                            return TRUE ;
                }
    break ;
    }
    return FALSE ;
}
 12. 트리 뷰
• 선택
    – 트리 뷰는 사용자가 다른 항목을 선택하면 항목이 바뀌기 전에
      TVN_SELCHANGING을 항목이 바뀐 후에 TVN_SELCHANGED 통
      지 메시지를 보내준다.
    – TVN_SELCHANGED메시지를 받았을 때 사용자가 선택한 항목을
      조사하여 특정 처리를 수행하면 된다.
    – 이 메시지는 lParam으로 NMLISTVIEW구조체를 보내준다.

typedef struct tagNMLISTVIEW
{
        NMHDR           hdr;
        int             iItem;
        int             iSubItem;
        UINT            uNewState;
        UINT            uOldState;
        UINT            uChanged;
        POINT           ptAction;
        LPARAM          lParam;
} NMLISTVIEW, *LPNMLISTVIEW;
12. 트리 뷰
    • action : 선택 변경이 키보드 (TVC_BYKEYBOARD)로부터 발생했는지
      마우스(TVC_BYMOUSE)로부터 발생했는지를 알려준다.
    • itemOld : 이전에 선택되어 있던 항목에 대한 정보
    • itemNew : 새로 선택된 항목에 대한 정보
    • ptDrag : 통지 메시지가 발생할 때의 마우스 좌표


 – BOOL TreeView_GetItem( HWND hwndTV, LPTVITEMEX pitem);
 – BOOL TreeView_SetItem( HWND hwndTV, LPTVITEMEX pitem);
12. 트리 뷰
  case WM_NOTIFY:
           {
                    LPNMHDR hdr;
                    LPNMTREEVIEW ntv;
                    hdr = (LPNMHDR)lParam;
                    ntv = (LPNMTREEVIEW)lParam;
                    TVITEMEX TvEx;
                    char temp[256];
                    if (hdr->hwndFrom == hTreeView)
                    {
                               switch(hdr->code)
                               {
                               case TVN_SELCHANGED:
                                         TvEx.mask = TVIF_IMAGE | TVIF_TEXT;
                                         TvEx.hItem = ntv->itemNew.hItem;
                                         TvEx.pszText = temp;
                                         TvEx.cchTextMax = 256;
                                         TreeView_GetItem(hTreeView,&TvEx);
                                         MessageBox(hDlg,temp,"",MB_OK);
                               }
                    }
          }
case WM_NOTIFY:
{
          LPNMHDR hdr;
          LPNMTREEVIEW ntv;
          hdr = (LPNMHDR)lParam;
          ntv = (LPNMTREEVIEW)lParam;
          TVITEMEX TvEx;
          char temp[256];
          if (hdr->hwndFrom == hTreeView)
          {
                      switch(hdr->code)
                      {
                      case NM_DBLCLK:
                                  HTREEITEM hItem = TreeView_GetSelection(hTreeView);
                                  if (hItem == NULL)
                                  {
                                               MessageBox(hDlg,"선택된 항목이 없습니다.",NULL,MB_OK);
                                  }
                                  else
                                  {
                                               TvEx.mask = TVIF_TEXT;
                                               TvEx.pszText = temp;
                                               TvEx.cchTextMax = 256;
                                               TvEx.hItem = hItem;
                                               TreeView_GetItem(hTreeView,&TvEx);
                                               MessageBox(hDlg,temp,"",MB_OK);
                                  }
                      }
          }
}
12. 트리 뷰
 – 임의의 시점에 어떤 항목을 조사
   • HTREEITEM TreeView_GetSelection(HWND hwndTV);
 – 특정항목을 강제로 선택
   • BOOL TreeView_Select(HWND hwndTV, HTREEITEM hitem, UINT
     flag);
 12.
비트맵
1. 비트맵의 종류
• DDB
  – 윈도우가 지원하는 비트맵 포맷은 두 가지 종류가 있다.
    • 1. DDB (Device Dependent Bitmap)
        – 출력장치에 의존된다.
        – 이미지의 크기와 색상에 관한 기본적인 정보와 이미지 데이터로만 구성
          되어 있다.
        – 다양한 해상도의 장치에 광범위하게 사용되지 못하며 만들어진 장치 외
          의 다른 장치에서 출력하면 제대로 출력되지 못한다.
    • 2. DIB (Device Independent Bitmap)
        – 비트맵은 장치에 독립적이기 때문에 제 모양대로 출력될 수 있다.
        – DIB는 DDB에 비해 색상 테이블과 해상도 정보 등의 추가 정보를 가지므
          로 장치에 종속되지 않으며 활용 용도가 훨씬 광범위 하다.
        – 확장자가 bmp를 가지는 비트맵 파일들은 모두 DIB 포맷으로 저장된 파
          일이며 리소스 에디터에서 만들어주는 비트맵들도 모두 DIB이다.
  – dc에서 선택할 수 있는 비트맵은 DDB이다.
  – DIB는 직접 DC에 선택될 수 없기 때문에 프로그램에서 곧바로 사
    용하기가 어렵다.
1. 비트맵의 종류
  – wind32에서 HBITMAP으로 지칭되는 비트맵 오브젝트는 DDB를
    말한다.
  – DDB만이 DC에 선택될 수 있다.
  – 리소스 에디터에서 만들어지는 비트맵 리소스들은 모두 DIB이지
    만 이 리소스는 LoadBitmap함수에 의해 읽혀지면서 현재 비디오
    모드와 호환되는 DDB로 변경된다.

typedef struct tagBITMAP
{
        LONG             bmType;
        LONG             bmWidth;
        LONG             bmHeight;
        LONG             bmWidthBytes;
        WORD             bmPlanes;
        WORD             bmBitsPixel;
        LPVOID           bmBits;
} BITMAP, *PBITMAP; (DDB포맷)
1. 비트맵의 종류
 – BITMAP구조체
    • bmType : 비트맵의 타입을 지정 ( 0으로 고정)
    • bmWidth, bmHeight : 비트맵의 폭과 높이를 픽셀 단위로 지정
    • bmWidthBytes : 한 줄의 바이트 수, 비트맵은 WORD단위로 정렬되
      기 때문에 반드시 짝수여야만 한다.
    • bmPlanes : 색상수면의 수 (보통 1)
    • bmBitsPixel : 한 픽셀을 표현하기 위해 필요한 비트 수 ( 1:흑백,
      4:16색, 8:256색, 256이면 트루컬러 비트맵)
    • bmBits 멤버는 비트맵의 실제 데이터, 즉 비트맵의 이미지 모양을 가
      지는 레스터 데이터에 대한 포인터.


 – HBITMAP CreateBitmap(int nWidth, int nHeight, UINT cPlanes,
   UNIT cBitsPerPel, CONST VOID * lpvBits);
 – HBITMAP CreateBitmapIndirect( CONST BITMAP * lpbm);
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
         HDC                                        hdc,hMemDC;
         PAINTSTRUCT           ps ;
         RECT                  rect ;
         BYTE Bits[] = {0xc3,0xff,0xbd,0xff,0x66,0xff,0x5a,0xff,0x5a,0xff,0x66,0xff,0xbd,0xff,0xc3,0xff}
         HBITMAP                          hBitmap, hOldBitmap;
         switch (message)
         {
         case WM_CREATE:
         case WM_PAINT:
                    hdc = BeginPaint (hwnd, &ps) ;
                    hMemDC = CreateCompatibleDC(hdc);
                    hBitmap = CreateBitmap(8,8,1,1,Bits);
                    hOldBitmap = (HBITMAP)SelectObject(hMemDC,hBitmap);
                    BitBlt(hdc,2,2,8,8,hMemDC,0,0,SRCCOPY);
                    SelectObject(hMemDC,hOldBitmap);
                    DeleteDC(hMemDC);
                    DeleteObject(hBitmap);
                    EndPaint (hwnd, &ps) ;
                    return 0 ;
         case WM_DESTROY:
                    PostQuitMessage (0) ;
                    return 0 ;
         }
         return DefWindowProc (hwnd, message, wParam, lParam) ;
}
1. 비트맵의 종류
  – ROP 모드

BLACKNESS     0        무조건 검정색으로 칠한다.
DSTINVERT     ~D       화면 색을 반전시킨다.
MERGECOPY     S&P      브러시와 비트맵 색을 AND연산한다.
MERGEPAINT    ~S|D     비트맵을 반전한 후 화면 색과 OR연산을 한다.
NOTSRCCOPY    ~S       비트맵을 반전시킨다.
NOTSRCERASE   ~(S|D)   화면 색과 비트맵 색을 OR연산한 후 반전시킨다.
PATCOPY       P        현재 선택된 브러시로 칠한다.
PATINVERT     P^D      브러시와 화면 색을 XOR연산한다.
PATPAINT      P|~(S|D) NOTSRCERASE의 결과를 브러시와 OR연산한다.
SRCAND        S&D      비트맵과 화면 색을 AND연산한다.
SRCCOPY       S        비트맵을 그래도 화면에 출력한다.
SRCERASE      S&~D     비트맵과 화면의 반전 색을 AND연산한다.
SRCINVERT     S^D      비트맵과 화면을 XOR연산한다.
SRCPAINT      S|D      비트맵과 화면을 OR연산한다.
WHITENESS     1        무조건 흰색으로 칠한다.
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            HDC                                                       hdc,hMemDC;
            PAINTSTRUCT ps ;
            RECT          rect ;
            HBITMAP                                    hBitmap, hOldBitmap;
            static HINSTANCE hInst;
            switch (message)
            {
            case WM_CREATE:
                          hInst = ((LPCREATESTRUCT)lParam)->hInstance;
                          return 0;
            case WM_PAINT:
                          {
                                         hdc = BeginPaint (hwnd, &ps) ;
                                         hMemDC = CreateCompatibleDC(hdc);
                                         hBitmap = LoadBitmap(hInst,MAKEINTRESOURCE(IDB_BITMAP1));
                                         hOldBitmap = (HBITMAP)SelectObject(hMemDC,hBitmap);

                                        HBRUSH hBrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
                                        HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc,hBrush);
                                        Ellipse(hdc,100,100,200,200);
                                        SelectObject(hdc,hOldBrush);

                                        BITMAP bm;
                                        GetObject(hBitmap,sizeof(BITMAP),&bm);
                                        BitBlt(hdc,100,100,bm.bmWidth,bm.bmHeight,hMemDC,0,0,SRCPAINT);
                                        SelectObject(hMemDC,hOldBitmap);
                                        DeleteDC(hMemDC);
                                        DeleteObject(hBitmap);
                                        EndPaint (hwnd, &ps) ;
                           }
                           return 0 ;
             case WM_DESTROY:
                           PostQuitMessage (0) ;
                           return 0 ;
             }
             return DefWindowProc (hwnd, message, wParam, lParam) ;
}
1. 비트맵의 종류
 – 확대 및 축소

   BOOL StretchBlt(
   HDC    hdcDest,        //   handle to destination DC
   int    nXOriginDest,   //   x-coord of destination upper-left corner
   int    nYOriginDest,   //   y-coord of destination upper-left corner
   int    nWidthDest,     //   width of destination rectangle
   int    nHeightDest,    //   height of destination rectangle
   HDC    hdcSrc,         //   handle to source DC
   int    nXOriginSrc,    //   x-coord of source upper-left corner
   int    nYOriginSrc,    //   y-coord of source upper-left corner
   int    nWidthSrc,      //   width of source rectangle
   int    nHeightSrc,     //   height of source rectangle
   DWORD dwRop            //   raster operation code
   );
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
          HDC                                           hdc,hMemDC;
          PAINTSTRUCT              ps ;
          HBITMAP                           hBitmap, hOldBitmap;
          static HINSTANCE hInst;
          switch (message)
          {
          case WM_CREATE:
                      hInst = ((LPCREATESTRUCT)lParam)->hInstance;
                      return 0;

          case WM_PAINT:
                    {
                                hdc = BeginPaint (hwnd, &ps) ;
                                hMemDC = CreateCompatibleDC(hdc);
                                hBitmap = LoadBitmap(hInst,MAKEINTRESOURCE(IDB_BITMAP1));
                                hOldBitmap = (HBITMAP)SelectObject(hMemDC,hBitmap);

                                    BITMAP bm;
                                    GetObject(hBitmap,sizeof(BITMAP),&bm);
StretchBlt(hdc,100,100,bm.bmWidth*2,bm.bmHeight*2,hMemDC,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY);
                                    SelectObject(hMemDC,hOldBitmap);
                                    DeleteDC(hMemDC);
                                    DeleteObject(hBitmap);
                                    EndPaint (hwnd, &ps) ;
                         }
                         return 0 ;
             case WM_DESTROY:
                         PostQuitMessage (0) ;
                         return 0 ;
             }
             return DefWindowProc (hwnd, message, wParam, lParam) ;
}
1. 비트맵의 종류
  – BOOL TransparentBlt(… , UINT crTransparent);
        • 마지막 인수로 지정한 컬러를 투명하게 처리한다.


• DIB
  – DIB는 다양한 장치에 사용할 수 있도록 하기 위해 비트맵 출력에
    대한 상세 정보를 포함하고 있다.

               BITMAPFILEHEADER 구조체

               BITMAPINFOHEADER 구조체

                  RGBQUAD 구조체 배열


                        비트 정보
1. 비트맵의 종류
    – BITMAPFILEHEADER 구조체

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

bfType        파일의 형태를 지정하되 반드시 BM이어야 한다. (0x42,0x4d)
bfSize        비트맵 파일의 크기를 바이트 단위로 지정한다.
bfReserved1   예약. 0으로 설정한다.
bfReserved2   예약. 0으로 설정한다.
bfOffBits     이 구조체와 실제 비트맵 데이터와의 오프셋 값
              이 값은 BITMAPFILEHEADER의 크기 + BITMAPINFOHEADER
              의 크기 + RGBQUAD구조체 배열의 크기이다.
 1. 비트맵의 종류
    – 이 구조체는 DIB가 디스크의 파일로 저장될 때만 사용되며 비트맵
      을 출력할 때는 사용되지 않는다.
    – 파일로 저장된 BMP파일에만 이 구조체가 있고 메모리상의 DIB에
      는 이 구조체가 없다.


typedef struct tagBITMAPINFOHEADER{
        DWORD           biSize;
        LONG            biWidth;
        LONG            biHeight;
        WORD            biPlanes;
        WORD            biBitCount;
        DWORD           biCompression;
        DWORD           biSizeImage;
        LONG            biXPelsPerMeter;
        LONG            biYPelsPerMeter;
        DWORD           biClrUsed;
        DWORD           biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;
biSize      이 구조체의 크기. 이 구조체의 시작 번지에 biSize를 더하면 색상
            테이블의 위치를 구할 수 있다.
biWidth     비트맵의 가로 픽셀 수
biHeight    비트맵의 세로 픽셀 수. 이 값이 양수이면 DIB는 아래에서 위로 구
            성되며, 원점은 아래 왼쪽이 된다. 이 값이 음수이면 DIB는 위에서
            아래로 구성되며 원점은 위왼쪽이 된다.
biPlanes    플레인 개수를 나타내는 멤버이되 이 값은 1로 고정되어 있다.
biBitCount 한 픽셀이 몇 개의 비트로 이루어지는가를 나타내며 이 값에 따라
           픽셀이 가질 수 있는 색상수가 결정된다.
biCompre    압축 방법을 지정한다. 아래에서 위로 비트맵일 경우만 압축이 가
ssion       능하며 위에서 아래로 비트맵은 압축할 수 없다. 이 값이 BI_RGB
            이면 압축되지 않은 비트맵이며, bi_RLE8이면 8비트 압축,
            BI_RLE4이면 4비트 압축 방법으로 압축되어 있는 것이다.
biSizeIma   이미지의 크기를 바이트 단위로 나타내며 BI_RGB(압축되지 않음)
ge          에서는 0이다.
biXPelsPe   미터당 가로 픽셀수, 즉 가로 해상도를 지정한다.
rMeter
biYPelsPe   세로 해상도
rMeter
1. 비트맵의 종류
biClrUsed   색상테이블의 색상 중 실제로 비트맵에서 사용되는 색상수를 나타
            낸다. 이 값이 0이면 비트맵은 사용 가능한 모든 색상을 다 사용한
            다. 이 값이 0이 아닐 경우 RGBQUAD구조체 배열의 크기는 이 멤
            버 값 만큼이 된다.
biClrImpor 비트맵을 출력하는데 필수적인 색상수를 나타내며 이 값이 0이면
tant       모든 색상이 다 사용되어야 한다.

int SetDIBitsToDevice(
         HDC     hdc, // handle to DC
         int     XDest, // x-coord of destination upper-left corner
         int     YDest, // y-coord of destination upper-left corner
         DWORD dwWidth, // source rectangle width
         DWORD dwHeight, // source rectangle height
         int     XSrc, // x-coord of source lower-left corner
         int     YSrc, // y-coord of source lower-left corner
         UINT    uStartScan, // first scan line in array
         UINT    cScanLines, // number of scan lines
         CONST VOID *lpvBits, // array of DIB bits
         CONST BITMAPINFO *lpbmi, // bitmap information
         UINT fuColorUse // RGB or palette indexes );
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            HDC                                                 hdc,hMemDC;
            PAINTSTRUCT ps ;
            HBITMAP                               hBitmap, hOldBitmap;
            static BITMAPFILEHEADER * fh;
            static BITMAPINFOHEADER * ih;
            static int bx;
            static int by;
            static BYTE * pRaster;

            static HINSTANCE hInst;
            switch (message)
            {
            case WM_CREATE:
                          hInst = ((LPCREATESTRUCT)lParam)->hInstance;
                          return 0;
            case WM_LBUTTONDOWN:
                          {
                                         OPENFILENAME OFN;
                                         char lpstrFile[256]="";
                                         memset(&OFN,0,sizeof(OPENFILENAME));
                                         OFN.lStructSize = sizeof(OPENFILENAME);
                                         OFN.hwndOwner = hwnd;
                                         OFN.lpstrFile = lpstrFile;
                                         OFN.lpstrFilter="Bitmap File(*.bmp)\0*.bmp\0";
                                         OFN.nMaxFile = 256;
                                         if (GetOpenFileName(&OFN) != 0)
                                         {
                                                         HANDLE hFile;
                                                         DWORD FileSize, dwRead;

                          hFile = CreateFile(lpstrFile,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
                          if (hFile == INVALID_HANDLE_VALUE)
                                                          {
                                                                      return 0;
                                                          }

                                                       FileSize = GetFileSize(hFile,NULL);
                                             if (fh)
                                                             free(fh);

                                             fh = (BITMAPFILEHEADER *)malloc(FileSize);
                                             ReadFile(hFile,fh,FileSize,&dwRead,NULL);
                                             CloseHandle(hFile);

                                             pRaster = (PBYTE)fh + fh->bfOffBits;
                                             ih = (BITMAPINFOHEADER *)((PBYTE)fh + sizeof(BITMAPFILEHEADER));
                                             bx = ih->biWidth;
                                             by = ih->biHeight;

                                             InvalidateRect(hwnd,NULL,TRUE);
                               }
                }
                return 0;
    case WM_PAINT:
                {
                               hdc = BeginPaint (hwnd, &ps) ;
                               if (fh)
                               {
                               SetDIBitsToDevice(hdc,0,0,bx,by,0,0,0,by,pRaster,(BITMAPINFO *)ih, DIB_RGB_COLORS);
                               }
                               EndPaint (hwnd, &ps) ;
                  }
                  return 0 ;
    case WM_DESTROY:
                  PostQuitMessage (0) ;
                  return 0 ;
    }
    return DefWindowProc (hwnd, message, wParam, lParam) ;
}
1. 비트맵의 종류
• DIB로 변환
  – DDB는 주로 프로그램 실행 중에 CreateBitmap나
    CreateCompatibleBitmap함수로 만들어 진다.
  – 이 포맷을 파일로 저장하려면 DIB로 변환해 주어야 한다.
    13.
가상 메모리
1. 메모리 할당
• Win32에서 추가된 가상 메모리 할당 함수들
 – 1. 메모리를 예약 상태로 할당할 수 있다.
    • 예약이란 물리적인 메모리를 소비하지 않으면서 주소 공간만을 미리
      할당해 놓는 방법을 말한다.
 – 2. 할당한 메모리의 액세스 권한을 지정할 수 있다.
    • 가상 메모리 함수로 할당한 메모리는 읽기 전용, 액세스 금지 속성을
      가질 수 있어 실수로 인한 데이터 파괴를 막을 수 있다.


 – LPVOID VirtualAlloc(LPVOID lpAddress, DWORD dwSize,
   DWORD flAllocationType, DWORD flProtect);
    • lpAddress : 할당하고자 하는 메모리 번지를 지정하되 NULL이면 시
      스템이 알아서 할당 번지를 지정해 준다.
    • dwSize : 할당하고자 하는 메모리의 양을 바이트 단위로 지정한다.
    • flAllocationType : 할당 방법 ( MEM_RESERVE, MEM_COMMIT)
    • flProtect : 할당한 페이지의 액세스 타입을 지정하며 보통
      PAGE_READWRITE로 지정한다.


 – LPVOID VirtualFree (LPVOID lpAddress, DWORD dwSize,
1. 메모리 할당
 – LPVOID VirtualFree (LPVOID lpAddress, DWORD dwSize,
   DWORD dwFreeType);
    • lpAddress : 해제하고자 하는 메모리의 선두 번지
    • dwSize : 해제하고자 하는 메모리의 크기
    • dwFreeType : MEM_DECOMMIT (확정된 페이지를 확정 해제)
      MEM_RELEASE(예약된 페이지를 예약 해제 한다.)
2. 예약과 확정
 – win32 프로세스가 가지는 4G의 가상 메모리는 “페이지”라는 단위
   로 구성된다.
 – 인텔 계열의 CPU에서는 한 페이지의 크기는 4K바이트이다.
 – 윈도우는 페이지 단위로 가상 메모리를 관리한다.
   • 할당하거나 해제하는 단위가 페이지 단위이다.
 – 가상 메모리를 구성하는 각 페이지는 다음 세 가지 상태 중 하나의
   상태로 존재한다.
   • 1. 자유영역 (Free) : 사용되지 않는 자유 영역이다.
   • 2. 예약 (Reserved) : 장래 사용을 위해 예약만 되어 있는 페이지이며
     물리적인 메모리가 할당되어 있지 않다.
   • 3. 확정(Committed) : 물리적 메모리가 할당되어 잇는 상태이며 바로
     사용할 수 있다.
 – 할당 영역의 크기는 반드시 페이지 단위의 배수가 된다.
   • 10K의 크기만큼 할당을 요청했다면 실제로 할당되는 영역의 크기는
     12K가 될 것이다.
3. 보호 속성
 – VirtualAlloc의 네 번째 인수 fProtect는 할당하고자 하는 메모리의
   액세스 타입을 지정한다.
   • PAGE_READONLY : 읽기만 가능하도록 한다.
   • PAGE_READWRITE : 읽기 쓰기를 가능하도록 한다.
   • PAGE_EXECUTE : 실행만 가능하도록 한다.
   • PAGE_EXECUTE_READ : 실행 및 일기기만 가능하도록 한다.
   • PAGE_EXECUTE_READWRITE : 실행,읽기,쓰기를 가능하도록 한다.
   • PAGE_GUARD : 보호페이지로 지정한다. 이 페이지에 읽기, 쓰기를
     시도하면 STATUS_GUARD_PAGE예외가 발생하며 보호 페이지 상태
     가 해제된다. 메모리의 끝을 표시하는 용도로 주로 사용된다.
   • PAGE_NOACCESS: 어떤 액세스도 하지 못하도록 한다.
   • PAGE_NOCACHE : 캐시를 금지시킨다.
4. 메모리 맵 파일
 – 메모리 맵 파일은 하드 디스크에 존재하는 파일의 내용을 프로세
   스의 주소 공간에 연결하는 기법이다.
   • 파일을 메모리처럼 사용하는 기법


 – 가상 주소 공간에 파일을 맵한 후 그 포인터를 사용하면 파일의 내
   용을 마치 메모리 다루듯이 똑같이 사용할 수 있다.
 – 파일을 열고 닫고 파일 포인터를 옮기고 버퍼를 유지하는 복잡한
   처리를 할 필요 없이 마치 메모리에 데이터를 읽고 쓰듯이 파일을
   조작할 수 있다.
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            HDC                                                         hdc;
            PAINTSTRUCT ps ;
            switch (message)
            {
            case WM_CREATE:
                          return 0;
            case WM_LBUTTONDOWN:
                          {
                                         hdc = GetDC(hwnd);
            HANDLE hFile = CreateFile("123.txt",GENERIC_READ,0,NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL);
                                         if (hFile == INVALID_HANDLE_VALUE)
                                         {
                                                         MessageBox(hwnd,"파일이 없습니다.","에러",MB_OK);
                                         }
                                         else
                                         {
                                                         HANDLE hFMap = CreateFileMapping(hFile,NULL,PAGE_READONLY,0,0,NULL);
                                                         char * pData = (char *)MapViewOfFile(hFMap,FILE_MAP_READ,0,0,0);
                                                         RECT rect;
                                                         GetClientRect(hwnd,&rect);
                                                         DrawText(hdc,pData,GetFileSize(hFile,NULL),&rect,DT_EXPANDTABS);
                                                         UnmapViewOfFile(hFMap);
                                                         CloseHandle(hFile);
                                         }
                                         ReleaseDC(hwnd,hdc);
                          }
                          return 0;
            case WM_DESTROY:
                          PostQuitMessage (0) ;
                          return 0 ;
            }
            return DefWindowProc (hwnd, message, wParam, lParam) ;
}
  4. 메모리 맵 파일
HANDLE CreateFileMapping (
      HANDLE                  hFile,
      LPSECURITY_ATTRIBUTES   lpAttributes,
      DWORD                   flProtect,
      DWORD                   dwMaximumSizeHigh,
      DWORD                   dwMaximumSizeLow,
      LPCTSTR                 lpName );

    – hFile : 대상 파일의 핸들 (INVALID_HANDLE_VALUE)
    – lpAttribute : NULL
    – flProtect :
        • PAGE_READONLY : 읽기 전용의 파일 맵핑 오브젝트를 만든다. 이렇
          게 만들어진 메모리 맵 파일에 쓰기를 해서는 안 된다.
        • PAGE_READWRITE : 읽고 쓸 수 있는 파일 맵핑 오브젝트를 만든다.
        • PAGE_WRITECOPY : 쓰기를 수행하는 시점에서 별도의 복사본이 생
          성된다.
    – dwMaximumSizeHigh, dwMaximumSizeLow : 이 인수들이 모두
      0이면 hFile에서 지정한 파일의 크기가 그대로 사용된다.
 4. 메모리 맵 파일
    – 파일 맵핑의 크기는 hFile이 INVALID_HANDLE_VALUE일 경우, 즉
      하드디스크의 파일이 아닌 페이징 파일에 메모리 맵 파일을 생성
      할 경우에 사용된다.

    – 파일 맵핑 오브젝트를 만든 후에는 이 오브젝트를 프로세스의 주
      소 공간에 맵해야 한다.
    – 주소 공간에 맵한 후 그 주소 공간을 사용한다.
    – 주소 공간에 맵된 파일의 일부분을 파일 뷰라고 한다.


LPVOID MapViewOfFile(
       HANDLE           hFileMappingObject,
       DWORD            dwDesiredAccess,
       DWORD            dwFileOffsetHigh,
       DWORD            dwFileOffsetLow,
       SIZE_T           dwNumberOfBytesToMap
);
4. 메모리 맵 파일
 – hFileMappingObject :
    • 주소 공간에 맵하려는 파일 오브젝트의 핸들
 – dwDesiredAccess :
    •   FILE_MAP_WRITE : 읽고 쓸 수 있다.
    •   FILE_MAP_READ : 읽을 수 있다.
    •   FILE_MAP_ALL_ACCESS : 읽을 수도 있고 쓸 수도 있다.
    •   FILE_MAP_COPY : 읽고 쓸 수 있다. 쓰기 시도가 발생하면 데이터의
        복사본을 만든 후 쓴다.
 – dwFileOffsetHigh, dwFileOffsetLow
    • 맵핑을 시작할 오프셋 위치를 나타내는 64비트 정수를 지정한다.
    • 이 값이 0이면 파일의 선두부터 맵핑이 된다.
    • 오프셋은 시스템의 할당 단위(64K)의 배수여야 한다.
 – dwNumberOfBytesToMap
    • 맵핑할 뷰의 크기
    • 0이면 파일 전체가 맵핑된다.


 – UnmapViewOfFile(LPCVOID lpBaseAddress);
5. 메모리 공유
 – 파일 맵핑의 크기는 hFile이 INVALID_HANDLE_VALUE일 경우, 즉
   하드디스크의 파일이 아닌 페이징 파일에 메모리 맵 파일을 생성
   할 경우에 사용된다.
  14.
프로세스
1. 프로세스와 스레드
 – 프로세스는 실행중인 프로그램의 한 인스턴스이다.
 – 운영체제는 실행된 프로그램을 프로세스 단위로 관리한다.
 – 프로세스는 각각 4GB의 주소 공간과 파일, 메모리, 스레드 등의
   객체들을 소유하며 프로세스가 종료될 때 프로세스가 소유한 자원
   은 운영체제에 의해 파괴된다.
 – 프로세스는 실행과 동시에 스레드를 하나 만들고 스레드를 호출함
   으로써 스레드에게 모든 작업을 맡긴다.
 – 프로세스는 최소한 한 개 이상의 스레드를 가진다.
 – 프로세스와 동시에 만들어지는 스레드를 주 스레드(Primary
   Thread)라고 한다.
 – 하나의 프로세스가 여러 개의 스레드를 만들 수 있다.
1. 프로세스와 스레드
BOOL CreateProcess(
       LPCTSTR                 lpApplicationName,
       LPTSTR                  lpCommandLine,
       LPSECURITY_ATTRIBUTES   lpProcessAttributes,
       LPSECURITY_ATTRIBUTES   lpThreadAttributes,
       BOOL                    bInheritHandles,
       DWORD                   dwCreationFlags,
       LPVOID                  lpEnvironment,
       LPCTSTR                 lpCurrentDirectory,
       LPSTARTUPINFO           lpStartupInfo,
       LPPROCESS_INFORMATION   lpProcessInformation );

  – lpApplicationName :
      • 실행하고자 프로그램의 이름을 준다.
      • 완전 경로를 주거나 파일명만 지정한 경우는 현재 디렉토리에서 파일
        을 찾으며 검색 경로는 사용하지 않는다.
      • 이 인수를 NULL로 주고 두 번째 인수에 실행 파일명을 줄 수도 있다.
  1. 프로세스와 스레드
      – lpCommandLine
           • 명령행 인수를 지정한다.
           • 첫 번째 인수가 NULL일 경우 실행 파일명을 가질 수도 있으며 실행
             파일명과 명령행 인수를 동시에 지정하는 것도 가능하다.
      – lpStartupInfo
           • 새로 만든 프로세스의 메인 윈도우가 어떻게 초기화될지를 지정하는
             구조체이다.
           • 이 구조체의 cb멤버에는 구조체의 크기가 반드시 대입되어야 한다.
      – lpProcessInformation
           • 생성된 프로세스의 정보를 대입 받기 위한 구조체이며 생략할 수 없다.


case WM_LBUTTONDOWN:
{
          STARTUPINFO si;
          memset(&si,0,sizeof(STARTUPINFO));
          PROCESS_INFORMATION pi;
          CreateProcess(NULL,"Notepad.exe",NULL,NULL,FALSE,NULL,NULL,NULL,&si,&pi);
}
return 0;
 1. 프로세스와 스레드
typedef struct _STARTUPINFO {
        DWORD           cb;
        LPTSTR          lpReserved;
        LPTSTR          lpDesktop;
        LPTSTR          lpTitle;
        DWORD           dwX;
        DWORD           dwY;
        DWORD           dwXSize;
        DWORD           dwYSize;
        DWORD           dwXCountChars;
        DWORD           dwYCountChars;
        DWORD           dwFillAttribute;
        DWORD           dwFlags;
        WORD            wShowWindow;
        WORD            cbReserved2;
        LPBYTE          lpReserved2;
        HANDLE          hStdInput;
        HANDLE          hStdOutput;
        HANDLE          hStdError;
} STARTUPINFO,*LPSTARTUPINFO;
1. 프로세스와 스레드
  – cb      : sizeof(STARTUPINFO)값을 대입한다.
  – dwFlags : 어떤 속성을 지정할 것인가에 따라 플래그를 설정한다.
STARTF_USEPOSITON       dwX,dwY 멤버가 지정하는 위치에 메인 윈도
                        우를 배치한다.
STARTF_USESHOWWINDOW    wShowWindow멤버가 지정하는 방식대로 메
                        인 윈도우를 보여준다.
STARTF_USESIZE          dwXSize, dwYSize멤버가 지정하는 크기대로
                        메인 윈도우를 배치한다.



typedef struct _PROCESS_INFORMATION
{
        HANDLE         hProcess;
        HANDLE         hThread;
        DWORD          dwProcessId;
        DWORD          dwThreadId;
} PROCESS_INFORMATION,*LPPROCESS_INFORMATION;
1. 프로세스와 스레드
 – lpProcessAttributes
 – lpThreadAttributes
    • 프로세스와 주 스레드의 보안 속성을 지정한다.
 – bInheritHandles
    • 새로 생성되는 프로세스가 페이런트로부터 핸들을 상속받을 수 있는
      지를 지정한다.
 – dwCreationFlags
    • 새로 생성되는 프로세스의 우선 순위 클래스와 프로세스 생성 옵션을
      지정한다.
    • REALTIME_PRIORITY_CLASS : 최상위 우선권
    • HIGH_PRIORITY_CLASS : 상위 우선권
    • ABOVE_PRIORITY_CLASS : 상위 우선권
    • NORMAL_PRIORITY_CLASS : 보통 우선권
    • BELOW_PRIORITY_CLASS : 하위 우선권
    • IDLE_PRIORITY_CLASS : 최하위 우선권
 – lpEnvironment
    • 새 프로세스의 환경 블록을 지정하는 포인터. 이 값이 NULL이면 페이
      런트의 환경 블록을 사용하며 보통 NULL이다.
1. 프로세스와 스레드
 – lpCurrentDirectory
    • 새 프로세스의 작업 디렉토리를 지정한다.
    • NULL일 경우 페이런트의 현재 디렉토리가 새 프로세스의 작업디렉토
      리가 된다.


 – DWORD WaitForInputIdle( HANDLE hProcess, DWORD
   dwMilliseconds);
    • 이 함수는 hProcess가 지정하는 프로세스가 사용자의 입력을 대기할
      수 있을 때까지, 즉 초기화가 완료될 때까지 기다려준다.


 – LPTSTR GetCommandLine(VOID);
    • 이 함수는 현재 프로세스의 명령행 인수를 조사해 리턴해 준다.
 – CommandLineToArgvW()
    • 함수를 사용하여 토큰별로 분리 할 수도 있다.
1. 프로세스와 스레드
 – VOID ExitProcess( UINT uExitCode );
    • 이 함수가 호출되면 프로세스는 정리작업에 들어가 즉각 종료된다.
    • 1. 프로세스와 연결된 모든 DLL을 종료시키기 위해 각 DLL의 DllMain
      함수가 호출되며 DLL들은 스스로 정리 작업을 한다.
    • 2. 모든 열려진 핸들을 닫는다.
    • 3. 실행중인 모든 스레드는 종료한다.
    • 4. 프로세스 커널 객체와 스레드 객체는 신호상태가 되며 이 객체를
      기다리는 다른 프로세스는 대기상태를 해제할 수 있다.
    • 5. 프로세스의 종료코드는 STILL_ACTIVE와 ExitProcess가 지정한 종
      료값이 된다.


 – BOOL TerminateProcess( HANDLE hProcess, UINT uExitCode);
    • 이 함수는 ExitProcess에 비해 종료 대상이 되는 프로세스의 핸들을
      인수로 가지므로 다른 프로세스를 강제로 종료시킬 수도 있다.
    • 이 함수는 ExitProcess보다 훨씬 더 위험하다.
    • TerminateProcess함수가 호출될 때 ExitProcess와 동일한 정리작업
      이 수행되나 단 연결된 DLL에게 종료사실이 통지되지 않는다.
    • 어쩔 수 없이 강제로 종료해야 할 경우에만 사용한다.
2. 프로세스 핸들
 – 커널 객체는 프로세스 한정적이다.
    • 커널 객체를 만드는 프로세스만이 자신의 핸들로 해당 객체를 액세스
      할 수 있다는 뜻이다.
 – 핸들은 프로세스 내에서 해당 객체를 액세스할 때 사용하는 한정
   적인 값이며 이 핸들을 사용하여 객체를 마음대로 조작할 수 있다.
 – ID는 시스템 전역적인 값이며 다른 프로세스 ID와 절대 중복되지
   않는다.
 – 프로세스끼리 ID를 전달해 줌으로써 목적이 되는 프로세스 핸들을
   다시 오픈할 수 있다.

 – HANDLE GetCurrentProcess(VOID);
 – HANDLE GetCurrentProcessId(VOID);
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            HDC                                                       hdc;
            PAINTSTRUCT ps ;
            static HINSTANCE hInst;
            static STARTUPINFO si;
            static PROCESS_INFORMATION pi;
            switch (message)
            {
            case WM_CREATE:
                          hInst = ((LPCREATESTRUCT)lParam)->hInstance;
                          return 0;
            case WM_LBUTTONDOWN:
                          {
                                         memset(&si,0,sizeof(STARTUPINFO));
                                         si.cb = sizeof(STARTUPINFO);
                                         si.dwFlags = STARTF_USEPOSITION | STARTF_USESIZE;
                                         si.dwX = 200;
                                         si.dwY = 200;
                                         si.dwXSize = 100;
                                         si.dwYSize = 100;
                                         CreateProcess(NULL,"Notepad.exe",NULL,NULL,FALSE,NULL,NULL,NULL,&si,&pi);
                          }
                          return 0;
            case WM_RBUTTONDOWN:
                          {
                                         HWND hProcessWnd = FindWindow(NULL,"Process");
                                         MessageBox(NULL,"윈도우를 찾았습니다.","",MB_OK);
                                         PostMessage(hProcessWnd,WM_MYMSG,(WPARAM)pi.dwProcessId,NULL);
                          }
                          return 0;
            case WM_PAINT:
                          hdc = BeginPaint (hwnd, &ps) ;
                          EndPaint (hwnd, &ps) ;
                          return 0 ;
            case WM_DESTROY:
                          PostQuitMessage (0) ;
                          return 0 ;
            }
            return DefWindowProc (hwnd, message, wParam, lParam) ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            HDC                                                        hdc;
            PAINTSTRUCT ps ;
            static HINSTANCE hInst;
            static DWORD pID;
            static HANDLE hProc;
            switch (message)
            {
            case WM_CREATE:
                          hInst = ((LPCREATESTRUCT)lParam)->hInstance;
                          return 0;
            case WM_MYMSG:
                          {
                                         pID = (DWORD)wParam;
                                         hProc = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pID);
                                         char temp[256];
                                         wsprintf(temp,"Process ID : %x, Process Handle : %x",pID,hProc);
                                         MessageBox(NULL,temp,"",MB_OK);
                          }
                          return 0;
            case WM_LBUTTONDOWN:
                          {
                                         DWORD ExitCode;
                                         GetExitCodeProcess(hProc,&ExitCode);
                                         if (ExitCode != STILL_ACTIVE)
                                         {
                                                         MessageBox(NULL,"프로세스 핸들이 유효하지 않습니다.","",MB_OK);
                                         }
                                         else
                                         {
                                                         TerminateProcess(hProc,NULL);
                                                         pID = NULL;
                                         }
                          }
                          return 0;
            case WM_PAINT:
                          hdc = BeginPaint (hwnd, &ps) ;
                          EndPaint (hwnd, &ps) ;
                          return 0 ;
            case WM_DESTROY:
                          PostQuitMessage (0) ;
                          return 0 ;
            }
            return DefWindowProc (hwnd, message, wParam, lParam) ;
3. Thread
 – Project/Settings c/c++탭의 Code Generation에서 Use run-time
   Library옵션을 선택
 3. Thread
uintptr_t _beginthreadex(
          void *           security,
          unsigned         stack_size,
          unsigned ( __stdcall *start_address )( void * ),
          void *           arglist,
          unsigned         initflag,
          unsigned *       thrdaddr );

     – security : SECURITY_ATTRIBUTES 구조체의 주소, 대부분 NULL
     – stack_size : 0을 입력하면 기본 스텍사이즈를 사용한다.
     – unsigned ( __stdcall *start_address )( void * )
     – arglist : 스레드 함수로 넘어가는 변수
     – initflag : CREATE_SUSPENDED면 스레드만 만들고 실행은 하지
       않는다.
     – thrdaddr : 스레드 ID
typedef struct threadParam
{
                HWND hwnd;
                BOOL bCont;
}THREAD_PARAM;

unsigned int WINAPI MyThreadFunc(LPVOID lpParameter)
{
               THREAD_PARAM * pTp = (THREAD_PARAM *)lpParameter;
               HWND hwnd = pTp->hwnd;

              while(pTp->bCont)
              {
                           HBRUSH hBrush = CreateSolidBrush(RGB(rand()%256,rand()%256,rand()%256));
                           RECT ClietRect,rect;
                           GetClientRect(hwnd,&ClietRect);

                              SetRect(&rect,rand()%ClietRect.right,rand()%ClietRect.bottom,rand()%ClietRect.right,rand()%ClietRect.bottom);
                              HDC hdc = GetDC(hwnd);
                              HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc,hBrush);
                              Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);
                              SelectObject(hdc,hOldBrush);
                              ReleaseDC(hwnd,hdc);
                              Sleep(100);
              }
              return 0;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
            HDC                                               hdc;
            PAINTSTRUCT ps ;
            static unsigned int      ThreadID;
            static HANDLE hThread;
            static THREAD_PARAM tp;
    switch (message)
    {
    case WM_COMMAND:
                  switch(LOWORD(wParam))
                  {
                  case IDM_THREAD_START:
                                if (hThread == NULL)
                                {
                                               tp.hwnd = hwnd;
                                               tp.bCont = TRUE;
                                hThread = (HANDLE)_beginthreadex(NULL,NULL,MyThreadFunc,(void *)&tp,NULL,&ThreadID);
                                }
                                else
                                {
                                               DWORD ExitCode;
                                               GetExitCodeThread(hThread,&ExitCode);
                                               if (ExitCode == STILL_ACTIVE )
                                                               MessageBox(NULL,"현재 스레드는 살아있습니다.","",MB_OK);
                                               else
                                                               MessageBox(NULL,"스레드는 죽었습니다.","",MB_OK);
                                }
                                break;
                  case IDM_THREAD_QUIT:
                                tp.bCont = FALSE;
                                break;
                  case IDM_THERAD_SUSPEND:
                                SuspendThread(hThread);
                                break;
                  case IDM_THREAD_RESUME:
                                ResumeThread(hThread);
                                break;
                  }
                  return 0;
    case WM_PAINT:
                  hdc = BeginPaint (hwnd, &ps) ;
                  EndPaint (hwnd, &ps) ;
                  return 0 ;
    case WM_DESTROY:
                  PostQuitMessage (0) ;
                  return 0 ;
    }
    return DefWindowProc (hwnd, message, wParam, lParam) ;
}
CRITICAL_SECTION cs;
int XPos;
unsigned int WINAPI MyThreadFunc1(LPVOID lpParameter)
{
             THREAD_PARAM * pTp = (THREAD_PARAM *)lpParameter;
             HWND hwnd = pTp->hwnd;

            while(pTp->bCont)
            {
                       HBRUSH hBrush = CreateSolidBrush(RGB(rand()%256,rand()%256,rand()%256));
                       RECT ClietRect,rect;
                       GetClientRect(hwnd,&ClietRect);

SetRect(&rect,rand()%ClietRect.right,rand()%ClietRect.bottom,rand()%ClietRect.right,rand()%ClietRect.bottom);
                         HDC hdc = GetDC(hwnd);

                         EnterCriticalSection(&cs);
                         XPos = 100;
                         Sleep(10);
                         TextOut(hdc,XPos,0,"1번 스레드 펑션",15);
                         LeaveCriticalSection(&cs);

                         HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc,hBrush);
                         Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);
                         SelectObject(hdc,hOldBrush);
                         ReleaseDC(hwnd,hdc);
                         Sleep(120);
            }
            return 0;
}
unsigned int WINAPI MyThreadFunc2(LPVOID lpParameter)
{
             THREAD_PARAM * pTp = (THREAD_PARAM *)lpParameter;
             HWND hwnd = pTp->hwnd;

            while(pTp->bCont)
            {
                       HBRUSH hBrush = CreateSolidBrush(RGB(rand()%256,rand()%256,rand()%256));
                       RECT ClietRect,rect;
                       GetClientRect(hwnd,&ClietRect);

SetRect(&rect,rand()%ClietRect.right,rand()%ClietRect.bottom,rand()%ClietRect.right,rand()%ClietRect.bottom);
                         HDC hdc = GetDC(hwnd);

                         EnterCriticalSection(&cs);
                         XPos = 400;
                         Sleep(15);
                         TextOut(hdc,XPos,0,"2번 스레드 펑션",15);
                         LeaveCriticalSection(&cs);

                         HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc,hBrush);
                         Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);
                         SelectObject(hdc,hOldBrush);
                         ReleaseDC(hwnd,hdc);
                         Sleep(130);
            }
            return 0;
}
3. 동기화
• 크리티컬 섹션
 – void InitializeCriticalSection( LPCRITICAL_SECTION
   lpCriticalSection );
 – void DeleteCriticalSection( LPCRITICAL_SECTION
   lpCriticalSection );
    • 둘 다 CRITICAL_SECTON형의 포인터를 인수로 요구한다.
 – void EnterCriticalSection( LPCRITICAL_SECTION
   lpCriticalSection );
 – void LeaveCriticalSection( LPCRITICAL_SECTION
   lpCriticalSection );
    • 보호될 코드를 다음과 같이 두 함수로 감싸준다.


             EnterCriticalSection(&cs);
             //이 사이에서 공유 자원을 안전하게 액세스한다.
             LeaveCriticalSection(&cs);
3. 동기화
  case WM_CREATE:
         InitializeCriticalSection(&cs);
         return 0;
  case WM_DESTROY:
         DeleteCriticalSection(&cs);
         PostQuitMessage (0) ;
         return 0 ;
 – 교착 상태

  EnterCriticalSection(&cs1);
  EnterCriticalSection(&cs2);
  //이 사이에서 공유 자원을 안전하게 액세스한다.
  LeaveCriticalSection(&cs2);
  LeaveCriticalSection(&cs1);

  EnterCriticalSection(&cs2);
  EnterCriticalSection(&cs1);
  //이 사이에서 공유 자원을 안전하게 액세스한다.
  LeaveCriticalSection(&cs1);
  LeaveCriticalSection(&cs2);
3. 동기화
• 동기화 객체
 – 동기화에 사용되는 객체이다.
 – 프로세스,스레드처럼 커널 객체이며 프로세스 한정적인 핸들을 가
   진다.
 – 동기화 객체는 크리티컬 섹션보다 느리기는 하지만 여러 프로그램
   에서 동시에 동기화가 가능하다.

 – 신호 상태 : 스레드의 실행을 허가하는 상태. 신호상태의 동기화
   객체를 가진 스레드는 계속 실행할 수 있다.
 – 비 신호 상태 : 스레드의 실행을 허가하지 않는 상태이며 신호 상
   태가 될 때까지 스레드는 블록 된다.

 – DWORD WaitForSingleObject( HANDLE hHandle, DWORD
   dwMilliseconds );
    •   dwMilliseconds : 1/1000초 단위로 지정한다. INFINITE로 지정하면
        무한정 기다린다.
3. 동기화
    • WAIT_OBJECT_0 : hHandle 객체가 신호 상태가 되었다.
    • WAIT_TIMEOUT : 타임 아웃 시간이 경과하였다.
    • WAIT_ABANDONED : 포기된 뮤텍스


• 뮤텍스
 – 크리티컬 섹션과 유사하다.
 – 이름을 가질 수 있고 프로세스간에서도 사용이 가능하다.
 – HANDLE CreateMutex( LPSECURITY_ATTRIBUTES
   lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName );
    • lpMutexAttributes : 보통 NULL
    • bInitialOwner : 뮤텍스를 생성함과 동시에 소유할 것인지를 지정한
      다. TRUE이면 소유하며, 뮤텍스가 비 신호상태로 생성됨으로써 다른
      스레드는 이 뮤텍스를 소요할 수 없게 된다.
 – 생성한 뮤텍스를 파괴할 때는 CloseHandle함수를 이용한다.
 – BOOL ReleaseMutex( HANDLE hMutex );
    • 뮤텍스 소유를 해제한다.
#include <windows.h>
#include "resource.h"

BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
            DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ;
            return TRUE;
}

BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
          static HWND hEdit1,hEdit2;
          static HANDLE hFileMapping;
          static char * pMapView;
          static HANDLE hMutex;
          switch (message)
          {
          case WM_INITDIALOG :

hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,1042,"MappingMutexSample");
                         pMapView = (char *)MapViewOfFile(hFileMapping,FILE_MAP_ALL_ACCESS,0,0,1024);
                         hEdit1 = GetDlgItem(hDlg,IDC_EDIT1);
                         hEdit2 = GetDlgItem(hDlg,IDC_EDIT2);
                         hMutex = CreateMutex(NULL,FALSE,"MyMutexSample");
                         return TRUE ;
    case WM_COMMAND :
                switch (LOWORD (wParam))
                {
                case IDC_BUTTON1:
                            {
                                         WaitForSingleObject(hMutex,INFINITE);
                                         char temp[256];
                                         GetWindowText(hEdit1,temp,256);
                                         SetWindowText(hEdit2,temp);
                                         strcpy(pMapView,temp);
                                         ReleaseMutex(hMutex);
                            }
                            return TRUE;
                case IDC_BUTTON2:
                            {
                                         WaitForSingleObject(hMutex,INFINITE);
                                         SetWindowText(hEdit2,pMapView);
                                         ReleaseMutex(hMutex);
                            }
                            return TRUE;
                case IDOK :
                case IDCANCEL :
                            EndDialog (hDlg, 0) ;
                            return TRUE ;
                }
    break ;
    }
    return FALSE ;
}
3. 동기화
• 이벤트
   – 어떤 사건이 일어났음을 알려주는 동기화 객체이다.
   – 크리티컬 섹션, 뮤텍스, 세마포어는 주로 공유 자원을 보호하기 위
     해 사용되는 데 비해 이벤트는 그보다는 스레드간의 작업 순서나
     시기를 조정하기 위해 사용한다.
   – 자동 리셋
       • 대기 상태가 종료되면 자동으로 비신호상태가 된다.
   – 수동 리셋
       • 스레드가 비신호상태로 만들어 줄 때까지 신호상태를 유지한다.

HANDLE CreateEvent(
      LPSECURITY_ATTRIBUTES   lpEventAttributes,
      BOOL                    bManualReset,
      BOOL                    bInitialState,
      LPCTSTR lpName
);
3. 동기화
    • bManualReset : 이 이벤트가 수동 리셋 이벤트인지 자동 리셋 이벤트
      인지를 지정한다.
       – TRUE : 수종 리셋 이벤트, FALSE : 자동 리셋 이벤트
    • bInitialState : 이벤트 생성과 동시에 신호상태로 만들어 이벤트를 기
      다리는 스레드가 곧바로 실행하도록 한다.
 – BOOL SetEvent( HANDLE hEvent );
 – BOOL ResetEvent( HANDLE hEvent );
 – BOOL PulseEvent(HANDLE hEvent );
#include <windows.h>
#include "resource.h"
#include <process.h>
BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
              DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ;
              return TRUE;
}
typedef struct param
{
              HWND hEdit2;
              BOOL bCont;
              HANDLE hEvent;
              char * pView;
}PARAM;
unsigned __stdcall MyThreadProc( void * pArguments )
{
              PARAM * pParam = (PARAM *)pArguments;
              while(pParam->bCont)
              {
                            WaitForSingleObject(pParam->hEvent,INFINITE);
                            SetWindowText(pParam->hEdit2,pParam->pView);
              }
    _endthreadex( 0 );
    return 0;
}
BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
              static HWND hEdit1,hEdit2;
              static HANDLE hFileMapping;
              static char * pMapView;
              static HANDLE hMutex;
              static HANDLE hThread;
    static PARAM param;
    static HANDLE hEvent;
    switch (message)
    {
    case WM_INITDIALOG :
    hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,1042,"MappingMutexSample");
                   pMapView = (char *)MapViewOfFile(hFileMapping,FILE_MAP_ALL_ACCESS,0,0,1024);
                   hEdit1 = GetDlgItem(hDlg,IDC_EDIT1);
                   hEdit2 = GetDlgItem(hDlg,IDC_EDIT2);
                   hMutex = CreateMutex(NULL,FALSE,"MyMutexSample");
                   hEvent = CreateEvent(NULL,TRUE,FALSE,"MyMutexSampleEvent");
                   param.bCont = TRUE;
                   param.hEdit2 = hEdit2;
                   param.hEvent = hEvent;
                   param.pView = pMapView;
                   hThread = (HANDLE)_beginthreadex(NULL,NULL,MyThreadProc,&param,NULL,NULL);
                   return TRUE ;
    case WM_COMMAND :
                   switch (LOWORD (wParam))
                   {
                   case IDC_BUTTON1:
                                  {
                                                WaitForSingleObject(hMutex,INFINITE);
                                                char temp[256];
                                                GetWindowText(hEdit1,temp,256);
                                                strcpy(pMapView,temp);
                                                PulseEvent(hEvent);
                                                ReleaseMutex(hMutex);
                                  }
                                  return TRUE;
                   case IDOK :
                   case IDCANCEL :
                                  EndDialog (hDlg, 0) ;
                                  return TRUE ;
                   }
    break ;
    }
    return FALSE ;
}
    15.
파일 입출력
 1. 파일 입출력
BOOL ReadFile(
       HANDLE        hFile,
       LPVOID        lpBuffer,
       DWORD         nNumberOfBytesToRead,
       LPDWORD       lpNumberOfBytesRead,
       LPOVERLAPPED lpOverlapped );

       •   hFile : 데이터를 읽을 대상 파일의 핸들
       •   lpBuffer : 읽은 데이터를 저장할 버퍼
       •   nNumberOfBytesToRead : 읽고자 하는 양
       •   lpNumberOfBytesRead : 실질적으로 읽은 바이트 양
       •   lpOverlapped : 비동기 입출력을 할 때 사용한다.


   – BOOL CloseHandle( HANDLE hObject );
       • 파일 핸들도 메모리를 차지하므로 다 사용하고 난 후에 해제해 주어야
         한다.
 1. 파일 입출력
HANDLE CreateFile(
       LPCTSTR                  lpFileName,
       DWORD                    dwDesiredAccess,
       DWORD                    dwShareMode,
       LPSECURITY_ATTRIBUTES    lpSecurityAttributes,
       DWORD                    dwCreationDisposition,
       DWORD                    dwFlagsAndAttributes,
       HANDLE                   hTemplateFile );
   – lpFileName : 열거나 만들고자 하는 파일의 완전경로
   – dwDesiredAccess : 파일의 액세스 타입, 즉 파일로부터 데이터를
     읽을 것인지 쓸 것인지를 지정한다.
       • 파일로부터 데이터를 읽기만 하면 GENERIC_READ
       • 쓰기만 하면 GENERIC_WRITE
       • 읽기 쓰기 : GENERIC_READ | GENERIC_WRITE
   – hTemplateFile : 만들고자 하는 파일의 추가 속성을 지원하는 템플
     리트 파일의 핸들을 지정 . 일반적으로 NULL지정
1. 파일 입출력
 – dwShareMode : 열려진 파일의 공유 모드를 지정한다.
   • 이 인수가 0이면 파일은 공유되지 않으며 이미 열려진 파일을 또 열
     수 없게 된다.
   • FILE_SHARE_DELETE : 삭제 액세스에 대해서만 파일을 열 수 있다.
   • FILE_SHARE_READ : 읽기 모드로 열 때만 파일을 열 수 있다.
   • FILE_SHARE_WRITE : 쓰기 모드로 열 때만 파일을 열 수 있다.
 – lpSecurityAttributes : 리턴된 핸들을 차일드 프로세스로 상속할
   것인지를 지정하는 SECURITY_ATTRIBUTES 구조체의 핸들
 – dwCreationDisposition : 만들고자 하는 파일이 이미 있을 경우,
   또는 열고자 하는 파일이 없을 경우의 처리를 지정한다.
   • CREATE_NEW : 새로운 파일을 만들되 만약 지정한 파일이 이미 있으
     면 만들지 않는다.
   • CREATE_ALWAYS : 새로운 파일을 만들되 만약 지정한 파일이 이미
     있으면 기존 파일을 삭제하고 다시 만든다.
   • OPEN_EXISTING : 이미 있는 파일을 열되 만약 파일이 없으면 에러
     코드를 되돌린다.
   • OPEN_ALWAYS : 파일을 열되 만약 없으면 새로 만든다.
   • TRUNCATE_EXISTING : 파일을 열되 파일 크기를 0으로 만든다.
1. 파일 입출력
  – dwFlagsAndAttributes
FILE_ATTRIBUTE_ARCHIVE     아카이브 속성
FILE_ATTRIBUTE_HIDDEN      히든 파일
FILE_ATTRIBUTE_NORMAL      아무 속성도 가지지 않은 보통 파일
FILE_ATTRIBUTE_READONLY    읽기 전용
FILE_ATTRIBUTE_SYSTEM      운영체제가 배타적으로 사용하는 파일
FILE_ATTRIBUTE_TEMPORARY   임시 저장소에 저장되는 파일. 이 속성의 파
                           일은 디스크에 저장되지 않고 메모리에 저
                           장되므로 입출력 속도가 빠르다. 사용 후에
                           는 반드시 지워주어야 한다.

FILE_FLAG_WRITE_THROUGH    데이터 출력시 캐시를 통해 곧바로 디스크
                           로 출력하도록 한다. 플러쉬가 더 빨라진다.
FILE_FLAG_OVERLAPPED       비동기 입출력을 행한다.
FILE_FLAG_RANDOM_ACCESS    임의 접근 파일임을 알린다.
FILE_FLAG_SEQUENTIAL_SCAN 순차 접근 파일임을 알린다. 이상의 두 플래
                          그는 시스템이 캐시를 최적화하는데 도움을
                          줄 뿐이다. (임의 접근 가능)
 1. 파일 입출력
     – 리턴 값 : 파일의 핸들
          • 어떤 이유로 파일 열기에 실패하면 INVALID_HANDLE_VALUE를 리
            턴한다.

BOOL WriteFile(
        HANDLE         hFile,
        LPCVOID        lpBuffer,
        DWORD          nNumberOfBytesToWrite,
        LPDWORD        lpNumberOfBytesWritten,
        LPOVERLAPPED   lpOverlapped );

     –   hFile : 대상 파일의 핸들
     –   lpBuffer : 데이터가 들어 있는 버퍼
     –   nNumberOfBytesToWrite : 쓰고자 하는 바이트 수
     –   lpNumberOfBytesWritten : 실제로 쓰여진 바이트 수
     –   lpOverlapped : 비동기 입출력을 할 때 사용한다.
 2. 비동기 입출력
    – ReadFile이나 WriteFile같은 입출력 함수는 입출력이 완전히 끝
      날 때까지 리턴하지 않는다.
    – 비동기 입출력을 하려면 CreateFile함수로 파일을 열 때
      FILE_FLAG_OVERLAPPED플래그를 주고 ReadFile, WriteFile함
      수의 마지막 인수에 OVERLAPPED구조체의 포인터를 전달해 준
      다.
    – 입출력 함수는 입출력이 시작되는 즉시 리턴하여 다른 작업을 계
      속할 수 있도록 해준다.
    – 입출력 함수느 데이터를 완전히 다 입출력하지 않았다는 의미로
      FALSE를 리턴하며 GetLastError함수로 에러 코드를 점검해보면
      ERROR_IO_PENDING이 리턴된다.
BOOL GetOverlappedResult(
      HANDLE                hFile,
      LPOVERLAPPED          lpOverlapped,
      LPDWORD               lpNumberOfBytesTransferred,
      BOOL                  bWait );
     – 다음 함수로 입출력 진행 상태를 언제든지 확인할 수 있다.
     – lpNumberOfBytesTransferred : 현재까지 입출력한 바이트 수
     – bWait : 입출력이 완료될 때까지 대기할 것인가를 지정
#include <windows.h>
#include "resource.h"
#include <process.h>
#include <commctrl.h>
BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
               DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ;
               return TRUE;
}

typedef struct param
{
                HWND hButton;
                HWND hProgress;
}PARAM;

void DispErrorMessage()
{
               DWORD ErrorCode = GetLastError();
               char errMsg[1024];
               FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,ErrorCode,0,errMsg,1204,NULL);
               MessageBox(NULL,errMsg,"",MB_OK);
}

BOOL GetFileName(char temp[])
{
             strcpy(temp,"123.txt");
             OPENFILENAME ofn;
             ZeroMemory(&ofn,sizeof(ofn));
             ofn.lStructSize = sizeof(OPENFILENAME);
             ofn.hwndOwner = NULL;
             ofn.lpstrFilter = "모든 파일(*.*)\0*.*\0텍스트 파일(*.txt)\0*.txt\0\0\0";
             ofn.lpstrFile = temp;
             ofn.nFilterIndex = 2;
             ofn.nMaxFile = 256;
             ofn.Flags = OFN_EXPLORER | OFN_ALLOWMULTISELECT | OFN_ENABLESIZING ;
             if (GetOpenFileName(&ofn) == NULL)
                              return TRUE;
             else
                              return FALSE;
}
unsigned __stdcall MyThreadProc( void * pArguments )
{
              PARAM * pParam = (PARAM *)pArguments;
              EnableWindow(pParam->hButton,FALSE);

             char temp[256];
             if (GetFileName(temp))
             {
                            return FALSE;
             }

             HANDLE hReadFile = CreateFile(temp,GENERIC_READ,NULL,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL);
             if (hReadFile == INVALID_HANDLE_VALUE)
             {
                             DispErrorMessage();
                             return FALSE;
             }

             unsigned long dFileSize = 0;
             unsigned long dFileSizeHigh = 0;
             dFileSize = GetFileSize(hReadFile,&dFileSizeHigh);
             BYTE * pData = (BYTE *)malloc(dFileSize);
             BYTE * pBuffer = (BYTE *)malloc(1024*1024);

             SendMessage(pParam->hProgress,PBM_SETRANGE32,0,dFileSize);
             SendMessage(pParam->hProgress,PBM_SETPOS,0,0);

             DWORD ReadLen = 0;
             OVERLAPPED ov;
             memset(&ov,0,sizeof(OVERLAPPED));
             ov.Offset = 0;
             ov.OffsetHigh = 0 ;
             ov.hEvent = NULL;

             int nTotalRead = 0;
             do
             {
                            ReadLen = 0;
                            if (ReadFile(hReadFile,pBuffer,1024*1024,&ReadLen,&ov) == FALSE)
                            {
                                            DispErrorMessage();
                            }
                              WaitForSingleObject(hReadFile,INFINITE);
                              GetOverlappedResult(hReadFile,&ov,&ReadLen,TRUE);
                              SendMessage(pParam->hProgress,PBM_SETPOS,nTotalRead,0);

                              CopyMemory(pData+nTotalRead,pBuffer,ReadLen);

                               nTotalRead += ReadLen;
                               ov.Offset += ReadLen;
               }while( dFileSize != nTotalRead );
               EnableWindow(pParam->hButton,TRUE);

               CloseHandle(hReadFile);
               //free(pData);
               free(pBuffer);


               DWORD dWriteLen;
               memset(&ov,0,sizeof(OVERLAPPED));
               WriteFile(hWriteFile,pData,dFileSize,&dWriteLen,&ov);

               SendMessage(pParam->hProgress,PBM_SETPOS,0,0);

               DWORD nTotalWrite = 0;
               do{
                               DWORD WriteLen = 0;
                               GetOverlappedResult(hWriteFile,&ov,&WriteLen,FALSE);
                               nTotalWrite += WriteLen;
                               SendMessage(pParam->hProgress,PBM_SETPOS,nTotalWrite,0);
               }while(dFileSize != nTotalWrite);

               CloseHandle(hWriteFile);

               free(pData);
    _endthreadex( 0 );
    return 0;
}
BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
            static HWND hProgress,hButton;
            static HANDLE hThread;
            static PARAM param;
            switch (message)
            {
            case WM_INITDIALOG :
                           hProgress = GetDlgItem(hDlg,IDC_PROGRESS1);
                           hButton = GetDlgItem(hDlg,IDC_BUTTON1);
                           return TRUE ;
            case WM_COMMAND :
                           switch (LOWORD (wParam))
                           {
    case IDC_BUTTON1:
                                         {
                                                        param.hButton = hButton;
                                                        param.hProgress = hProgress;
                                                        hThread = (HANDLE)_beginthreadex(NULL,NULL,MyThreadProc,&param,NULL,NULL);
                                         }
                                         return TRUE;
                           case IDOK :
                           case IDCANCEL :
                                         EndDialog (hDlg, 0) ;
                                         return TRUE ;
                           }
            break ;
            }
            return FALSE ;
}
 3. 파일 관리
BOOL CopyFile(
      LPCTSTR      lpExistingFileName,
      LPCTSTR      lpNewFileName,
      BOOL         bFailIfExists );
       • lpExistingFileName : 복사 대상 피일명
       • lpNewFileName : 새로 만들어질 파일명
       • bFailIfExists : 새로 만들어질 파일이 이미 있을 경우의 처리
          – FALSE : 기존 파일을 지워 버리고 새로운 파일을 복사한다.
          – TRUE : 기존 파일이 있을 경우 이 함수는 복사하지 않고 에러 코드를 리
            턴한다.

BOOL CopyFileEx(
      LPCTSTR              lpExistingFileName,
      LPCTSTR              lpNewFileName,
      LPPROGRESS_ROUTINE   lpProgressRoutine,
      LPVOID               lpData,
      LPBOOL               pbCancel,
      DWORD                dwCopyFlags );
 3. 파일 관리
        • lpExistingFileName : 복사 대상 피일명
        • lpNewFileName : 새로 만들어질 파일명
        • lpProgressRoutine : 이 파라미터로 전달한 콜백함수를 주기적으로
          호출해 준다.
        • lpData : Argument to be passed to the callback function.
        • pbCancel : 복사 중에 중지할 수 있는 기능이 있다.



DWORD CALLBACK CopyProgressRoutine(
      LARGE_INTEGER        TotalFileSize,
      LARGE_INTEGER        TotalBytesTransferred,
      LARGE_INTEGER        StreamSize,
      LARGE_INTEGER        StreamBytesTransferred,
      DWORD                dwStreamNumber,
      DWORD                dwCallbackReason,
      HANDLE               hSourceFile,
      HANDLE               hDestinationFile,
      LPVOID               lpData );
 3. 파일 관리
BOOL MoveFile( LPCTSTR lpExistingFileName, LPCTSTR lpNewFileName );

BOOL DeleteFile( LPCTSTR lpFileName );

DWORD GetFileSize( HANDLE hFile, LPDWORD lpFileSizeHigh );

BOOL CreateDirectory(
       LPCTSTR                           lpPathName,
       LPSECURITY_ATTRIBUTES             lpSecurityAttributes );

BOOL RemoveDirectory( LPCTSTR lpPathName );

DWORD GetCurrentDirectory( DWORD nBufferLength, LPCTSTR lpBuffer );

DWORD GetSystemDirectory( LPTSTR lpBuffer, UINT uSize );

DWORD GetWindowsDirectory( LPTSTR lpBuffer, UINT uSize );
 3. 파일 관리
UINT GetDriveType( LPCTSTR lpRootPathName );


     – lpRootPathName에 조사 대상 디스크의 루트 디렉토리를 지정
       하는 문자열을 준다. NULL이면 현재 디렉토리가 사용된다.
 리턴 값                   설명
 DRIVE_UNKNOWN          알 수 없는 타입이다.
 DRIVE_NO_ROOT_DIR      루트 디렉토리가 없다.
 DRIVE_REMOVABLE        이동식 디스크이다. 플로피 디스크
 DRIVE_FIXED            고정된 디스트이며, 하드 디스크
 DRIVE_REMOTE           네트웍에 연결된 드라이브다.
 DRIVE_CDROM            CD_ROM 드라이브이다.
 DRIVE_RAMDISK          램디스크이다.
#include <windows.h>
#include "resource.h"
BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
               DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ;
               return TRUE;
}

BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
            static HWND hList,hButton;
            switch (message)
            {
            case WM_INITDIALOG :
                          hList = GetDlgItem(hDlg,IDC_LIST1);
                          hButton = GetDlgItem(hDlg,IDC_BUTTON1);
                          return TRUE ;
            case WM_COMMAND :
                          switch (LOWORD (wParam))
                          {
    case IDC_BUTTON1:
                                          {
                                                        char temp[256];
                                                        for(char ch = 'a'; ch <= 'z';ch++)
                                                        {
                                                                        wsprintf(temp,"%c:\\",ch);
                                                                        switch(GetDriveType(temp))
                                                                        {
                                          case DRIVE_UNKNOWN:
                                                        strcat(temp," : 알 수 없는 타입이다.");
                                                        SendMessage(hList,LB_ADDSTRING,0,(LPARAM)temp);
                                                        break;
                                          case DRIVE_NO_ROOT_DIR:
                                                        //strcat(temp," : 루트 디렉토리가 없다.");
                                                        //SendMessage(hList,LB_ADDSTRING,0,(LPARAM)temp);
                                                        break;
                                          case DRIVE_REMOVABLE:
                                                        strcat(temp," : 이동식 디스크이다.");
                                                        SendMessage(hList,LB_ADDSTRING,0,(LPARAM)temp);
                                                        break;
                            case DRIVE_FIXED:
                                         strcat(temp," : 하드 디스크");
                                         SendMessage(hList,LB_ADDSTRING,0,(LPARAM)temp);
                                         break;
                            case DRIVE_REMOTE:
                                         strcat(temp," : 네트웍에 연결된 드라이브다.");
                                         SendMessage(hList,LB_ADDSTRING,0,(LPARAM)temp);
                                         break;
                            case DRIVE_CDROM:
                                         strcat(temp," : CD_ROM 드라이브이다.");
                                         SendMessage(hList,LB_ADDSTRING,0,(LPARAM)temp);
                                         break;
                            case DRIVE_RAMDISK:
                                         strcat(temp," : 램디스크이다.");
                                         SendMessage(hList,LB_ADDSTRING,0,(LPARAM)temp);
                                         break;
                                                      }
                                         }
                            }
                            return TRUE;
                case IDOK :
                case IDCANCEL :
                            EndDialog (hDlg, 0) ;
                            return TRUE ;
                }
    break ;
    }
    return FALSE ;
}
 3. 파일 관리
HANDLE FindFirstFile(
      LPCTSTR                          lpFileName,
      LPWIN32_FIND_DATA                lpFindFileData );

BOOL FindNextFile(
       HANDLE                          hFindFile,
       LPWIN32_FIND_DATA               lpFindFileData );

BOOL FindClose( HANDLE hFindFile );

    – lpFileName : 검색식을 준다. 검색 시작 위치와 검색 대상 파일을
      와일드 카드식으로 표현한다. (C:\\Windows\\*.exe)
    – lpFindFileData : 검색 결과가 구조체에 담겨서 넘어온다.
WIN32_FIND_DATA FindData;
HANDLE hFindFile = FindFirstFile("C:\\Windows\\*.*",&FindData);

BOOL bResult = TRUE;
while(bResult)
{
          bResult = FindNextFile(hFindFile,&FindData);
          SendMessage(hList,LB_ADDSTRING,0,(LPARAM)FindData.cFileName);
}
FindClose(hFindFile);
 3. 파일 관리
HANDLE FindFirstChangeNotification(
      LPCTSTR         lpPathName,
      BOOL            bWatchSubtree,
      DWORD           dwNotifyFilter );

    – 윈도우는 특정 디렉토리의 내용이 변경될 때 통지를 해줄 수 있으
      며 응용 프로그램은 이 통지를 받았을 때 자신이 가지고 있는 목
      록을 갱신하거나 특별한 동작을 할 수 있다.
    – lpPathName : 감시의 대상이 되는 디렉토리 경로
        • 이 객체는 변화가 생기면 신호상태가 되므로 대기 함수와 함께 사용
          하면 변화의 시점을 정확하게 통지 받을 수 있다.
    – bWatchSubtree : 서브 디렉토리까지 검사할 것인지를 지정한다.
    – dwNotifyFilter : 어떤 변화를 감시할 것인가를 지정하며 다음 플
      래그들의 조합으로 지정한다.
        •   FILE_NAME : 파일명이 변경되었다. 파일 생성, 삭제
        •   DIR_NAME : 디렉토리가 변경되었다. 생성, 삭제
        •   ATTRIBUTE : 속성중 일부가 변경되었다.
        •   SIZE : 파일의 크기가 변경되었다.
        •   LAST_WRITE : 파일의 최후 기록 시간이 변경되었다.
16.
DLL
1. DLL (Dynamic Link Library)
 – 동적 연결 라이브러리 (DLL) :
    • Microsoft Windows의 가장 중요한 구조적 요소 중 하나이다.
 – 라이브러리의 기초
    •   DLL은 직접 실행될 수 없다.
    •   메시지를 받지 않는다.
    •   프로그램에서 호출되는 함수들을 가진 별도의 파일이다.
    •   프로그램이 라이브러리 내의 함수들 중 하나를 호출할 때만 동작한다.
    •   확장자가 DLL이면 자동으로 로드 된다.
 – DLL의 목적
    • 다수의 서로 다른 프로그램들에 의해 사용될 수 있는
      함수와 자원을 제공
 – DllMain
    • DLL이 실행 파일에 의해 요청되거나 해제될 때 호출된다.
    • DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID
      lpReserved)
    • hInstance : 라이브러리의 인스턴스 핸들
    • dwReason : Windows가 DllMain을 호출하는 이유
         – DLL_PROCESS_ATTACH
         – 동적 연결 라이브러리가 프로세스의 주소 공간으로 매핑되어 있음을 나
           타낸다.
         – 프로세스가 수행되는 동안 오직 한번의 호출된다.
1. DLL (Dynamic Link Library)
        –   DLL_PROCESS_DETACH
        –   해당 프로세스에 DLL이 더 이상 필요로 하지 않는다는것을 의미한다.
        –   라이브러리가 자신을 정리한다.
        –   DLL_THREAD_ATTACH
        –   추가된 프로세스가 새로운 스레드를 만든다.
        –   DLL_THREAD_DETACH
        –   스레드가 종료될 때 Window는 호출한다.
 – 우선 함수를 제공하는 DLL에서는 자신이 제공하는 함수에 대한 정
   보를 밖으로 공개해 놓아야 한다. Export
 – DLL을 사용하는 클라이언트에서는 어떤 DLL에 있는 어떤 함수를
   사용하겠다고 선언해야 한다. Import
 – __declspec ( extended-decl-modifier-seq )
    • 함수에 대한 정보를 제공하는 선언문이며 엑스포트 또는 임포트하는
      함수 앞에 수식어로 이문구가 있어야 한다.
    • extern "C" __declspec(dllexport) int InitHook( HINSTANCE hDll,
      HWND hWndHost )
    • extern "C" typedef __declspec(dllimport) int
      (*PFNInitHook)( HINSTANCE hDll, HWND hWndHost );
    • extern "C" typedef __declspec(dllimport) void
      (*PFNGetDeadWndTxt)( char *pszBuf, int nMaxBuf );
    • extern "C" typedef __declspec(dllimport) void
      (*PFNReleaseHook)();
1. DLL (Dynamic Link Library)
  – extern “C”
       • mangled name을 만들지 않도록 지정함으로써 C형식으로 함수의 정
         보를 공개하도록 한다.
• 명시적 연결

m_hInstDll = ::LoadLibrary( "l3t_hook.dll" );
if( !m_hInstDll ) {
    MessageBox( "l3t_hook.dll 을 찾을 수 없습니다.", "오류" );
    return FALSE;
}
m_pfnInitHook = (PFNInitHook)::GetProcAddress( m_hInstDll, "InitHook" );
m_pfnReleaseHook = (PFNReleaseHook)::GetProcAddress( m_hInstDll, "ReleaseHook" );
m_pfnGetDeadWndTxt = (PFNGetDeadWndTxt)::GetProcAddress( m_hInstDll, "GetDeadW

if( (!m_pfnInitHook) || (!m_pfnReleaseHook) ) {
    MessageBox( "잘못된 dll입니다.", "오류" );
    ::FreeLibrary( m_hInstDll );
    m_hInstDll = 0;
    return FALSE;
}
    17.
소켓의 기초
1. 서버 소켓
• 소켓이 작업하는 방식
 – 1. 연결의 기다리는 소켓 : 서버 소켓
 – 2. 연결을 시도하는 소켓 : 클라이언트 소켓

• 서버 소켓
 – 연결을 시도하는 클라이언트 소켓과의 연결을 구축한다.
 – 서버 소켓은 데이터의 흐름에 대해서는 신경 쓰지 않는다. 오직
   연결만 처리한다.
 – 데이터의 흐름은 같은 프로그램 내의 다른 소켓이 담당하게 된다.

                               클라이언트
   서버              1. 연결 시도
                               (원격 소켓)


        2. 소켓 생성
                              3. 연결 및 데이터 송수신
                    클라이언트
                    (로컬 소켓)
1. 서버 소켓
   •   1.   서버   소켓이 존재하는 상태에서 원격 소켓이 연결을 시도
   •   2.   서버   소켓은 패킷 송수신을 담당할 로컬 소켓을 생성
   •   3.   로컬   소켓과 연결을 시도한 원격 소켓을 연결
   •   4.   서버   로컬 소켓과 원격 소켓 간의 데이터 송수신


 – 연결을 시도하는 소켓은 무조건 큐에 들어간다.
 – 큐에 들어가서 자신이 처리될 순서를 기다린다.
 – 서버 소켓은 큐에 들어 있는 원격 소켓을 하나씩 처리한다.
1. 서버 소켓
• 서버 소켓 흐름도

     소켓 생성 : socket()

       결합 : bind()      1회 실행


       듣기 : listen ()


          항상, 참


      대기 : Accept()
                        무한 반복
       로컬 소켓 생성

  연결구축, 로컬 소켓과 원격 소켓
#include <winsock2.h>
#include <windows.h>
#include "resource.h"

BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
              WSADATA wsaData;
              WSAStartup(MAKEWORD(2,2), &wsaData);

             DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ;

             WSACleanup();
             return TRUE;
}

BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
           static HWND hEdit1,hEdit2;
           static HWND hButton1,hButton2;
           static SOCKET hServerSock;
           static SOCKET hClientSock;
           switch (message)
           {
           case WM_INITDIALOG :
                         {
                                       hEdit1 = GetDlgItem(hDlg,IDC_EDIT1);
                                       hEdit2 = GetDlgItem(hDlg,IDC_EDIT2);

                                         hButton1 = GetDlgItem(hDlg,IDC_BUTTON1);
                                         hButton2 = GetDlgItem(hDlg,IDC_BUTTON2);
                                         EnableWindow(hButton1,FALSE);
                                         EnableWindow(hButton2,FALSE);

                                         hServerSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

                                         sockaddr_in ServerAddr;
                                         ZeroMemory(&ServerAddr,sizeof(sockaddr_in));
                                         ServerAddr.sin_addr.s_addr = ADDR_ANY;
                                         ServerAddr.sin_family = AF_INET;
                                         ServerAddr.sin_port = htons(50000);
                         bind(hServerSock,(sockaddr *)&ServerAddr,sizeof(sockaddr_in));
                         listen(hServerSock,SOMAXCONN);
                         SetWindowText(hEdit1,"클라이언트 접속을 기다리고 있습니다.");
           }
           return TRUE ;
case WM_COMMAND :
           switch (LOWORD (wParam))
           {
           case IDC_ACCEPT:
                         {
                                      sockaddr_in ClientAddr;
                                      int nAddrLen = sizeof(ClientAddr);
                                      hClientSock = accept(hServerSock,(sockaddr *)&ClientAddr,&nAddrLen);
                                      if (hClientSock == INVALID_SOCKET)
                                      {
                                                      int ErrorCode = WSAGetLastError();
                                                      char errMsg[1024];
                         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,ErrorCode,0,errMsg,1204,NULL);
                                                      MessageBox(NULL,errMsg,"",MB_OK);
                                      }
                                      char temp[256];
                                      wsprintf(temp,"%s 클라이언트 접속 요청",inet_ntoa(ClientAddr.sin_addr));
                                      SetWindowText(hEdit1,temp);
                                      EnableWindow(hButton1,TRUE);
                                      EnableWindow(hButton2,TRUE);
                                      return TRUE;
                         }
            case IDC_BUTTON1:
                         {
                                      char temp[256];
                                      recv(hClientSock,temp,256,NULL);
                                      SetWindowText(hEdit2,temp);
                                      return TRUE;
                         }
                     case IDC_BUTTON2:
                                  {
                                                char temp[256];
                                                GetWindowText(hEdit2,temp,256);
                                                send(hClientSock,temp,256,NULL);
                                                return TRUE;
                                 }
                     case IDOK :
                     case IDCANCEL :
                                 closesocket(hServerSock);
                                 closesocket(hClientSock);
                                 EndDialog (hDlg, 0) ;
                                 return TRUE ;
                     }
    break ;
    }
    return FALSE ;
}
1. 서버 소켓
 – accept 함수
   • 클라이언트 소켓이 연결을 시도할 때까지 블로킹상태에 빠지기 때
     문에 프로그램이 죽은 것처럼 보인다.
   • 첫 번째 인자 : 대기 모드에 들어가 있는 소켓의 핸들. 반드시 listen
     함수 호출에서 성공한 핸들이어야 한다.
   • 두 번째 인자 : 연결을 시도한 클라이언트 소켓의 번호를 받을
     sockaddr_in 구조체의 주소
   • 세 번째 인자 : 두 번째 인자로 들어가는 구조체의 크기


   • 원격지에서 연결을 시도한 소켓과 연결을 담당한다.
   • 두 번째 인자를 통하여 넘어온 IP를 확인하여 연결을 허락할지 끊어
     야 할지를 결정한다.
      – 연결을 끊을 때는 closesocket함수를 이용하낟.
   • 리턴 값 : 원격지 소켓과의 데이터 송수신을 처리할 소켓의 핸들을
     반환한다. (accept함수 내부에서 생성)
      – 실패하면 INVALID_SOCKET 이 리턴된다.
   • accept함수가 성공하면 프로그램에는 소켓이 두 개가 존재하게 된
     다.
2. 클라이언트(원격) 소켓
 – 원격 소켓
   • 원격 소켓의 역할은 서버 소켓에 연결을 시도하는 것이다.
   • 원격 소켓이 서버에 연결하기 위해서는 반드시, 서버의 주소와 포트
     번호를 알고 있어야 한다.
   • 클라이언트 프로그램이 종료되면, 원격 소켓은 자동으로 종료된다.
   • 소켓은 종료할 때, 자신과 연결된 상대 소켓에게 종료되었다고 알려
     준다.
2. 클라이언트(원격) 소켓
• 클라이언트(원격) 소켓 흐름도

                      소켓 생성 : socket()

                     서버에 연결 : connect ()   1회 실행

             실패
                            연결 ?
                                  성공
                 예
                          작업 완료?


종료 : closesocket()       데이터 송수신
                        send(),receive()   무한 반복
#include <winsock2.h>
#include <windows.h>
#include "resource.h"

BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
              WSADATA wsaData;
              WSAStartup(MAKEWORD(2,2), &wsaData);

             DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ;

             WSACleanup();
             return TRUE;
}

BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
           static HWND hEdit1,hEdit2;
           static HWND hButton1,hButton2;
           static SOCKET hClientSock;
           switch (message)
           {
           case WM_INITDIALOG :
                         {
                                       hEdit1 = GetDlgItem(hDlg,IDC_EDIT1);
                                       hEdit2 = GetDlgItem(hDlg,IDC_EDIT2);

                                             hButton1 = GetDlgItem(hDlg,IDC_BUTTON1);
                                             hButton2 = GetDlgItem(hDlg,IDC_BUTTON2);
                                             EnableWindow(hButton1,FALSE);
                                             EnableWindow(hButton2,FALSE);

                                             hClientSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
                             }
                             return TRUE ;
    case WM_COMMAND :
               switch (LOWORD (wParam))
               {
               case IDC_CONNECT:
                            {
                                                sockaddr_in ServerAddr;
                                                ZeroMemory(&ServerAddr,sizeof(sockaddr_in));
                                                ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
                                                ServerAddr.sin_family = AF_INET;
                                                ServerAddr.sin_port = htons(50000);

                                                connect(hClientSock,(sockaddr *)&ServerAddr,sizeof(ServerAddr));
                                                SetWindowText(hEdit1,"서버에 접속되었습니다.");
                                                EnableWindow(hButton1,TRUE);
                                                EnableWindow(hButton2,TRUE);

                                                return TRUE;
                                  }
                     case IDC_BUTTON1:
                                  {
                                                char temp[256];
                                                recv(hClientSock,temp,256,NULL);
                                                SetWindowText(hEdit2,temp);
                                                return TRUE;
                                  }
                     case IDC_BUTTON2:
                                  {
                                                char temp[256];
                                                GetWindowText(hEdit2,temp,256);
                                                send(hClientSock,temp,256,NULL);
                                                return TRUE;
                                 }
                     case IDOK :
                     case IDCANCEL :
                                 closesocket(hClientSock);
                                 EndDialog (hDlg, 0) ;
                                 return TRUE ;
                     }
    break ;
    }
    return FALSE ;
}
3. TCP
• UDP
  – 연결이 존재하지 않는다.
  – 최선형 (best effort) 프로토콜로 최선을 다해서 데이터가 도착하
    도록 노력은 하지만, 도착하지 않아도 책임은 지지 않는다.
  – 도착하지 않았을 때의 책임은 각각의 프로그램에서 담당한다.

  – 연결이 존재하지 않기 때문에 각각의 패킷은 서로 연관성이 없어
    야 한다.

  – 전송 패킷은 반드시 한 번에 전송되어야 한다.
    • TCP의 경우는 여러 번에 걸쳐 전송될 수도 있다.


• TCP
  – TCP프로토콜은 가상으로 연결된 상태이다.
  – 한 번에 전송한 패킷이 한번에 전송될 수도 있고 여러 번에 걸쳐
    서 전송될 수도 있다.
3. TCP
  – 여러 번에 걸쳐서 전송된 패킷이 한번에 전송될 수도 있다.
  – 연결이 된 이후부터 연결을 닫을 때까지 전송한 데이터가 모두 하
    나의 데이터이다.
  – TCP에서는 어디서부터 어디까지가 의미 있는 하나의 패킷인지 확
    인하는 작업이 필수적이다.
  – 전송 측과 수신 측 모두 두 개의 버퍼를 사용한다.
    • 애플리케이션 버퍼, 전송 또는 수신을 위한 소켓 라이브러리 버퍼
    • send 함수를 호출했다고 해서 패킷이 실제로 상대 소켓으로 전달되
      었다고 가정해서는 안 된다.
    • send함수는 소켓 라이브러리의 버퍼로 데이터를 옮겨놓는 순간 반환
      된다.
    • 만약에 소켓 라이브러리 버퍼가 꽉 차면 블로킹 상태가 된다.
    • 상대 소켓으로 데이터를 전송하고 나서 버퍼에 여유가 생겨서 send
      함수가 모든 데이터를 이동하면, 그때 블로킹이 풀린다.


  – TCP 프로토콜에서 연결이 구축되어 있는 동안 전송되는 패킷은
    모두 연속되어 있다. 패킷을 처리할 때는, 각각의 패킷 길이만큼
    잘라서 처리해야 한다.
 4. 패킷
      – 패킷은 크게 두 부분으로 나뉜다. ( 헤더와 데이터 )
      – 헤더에는 반드시 전체 패킷의 크기가 들어가야 한다.
        • 한 패킷을 잘라 오기 위하여


      – 패킷 헤더는 일반적으로 어떠한 패킷이던지 동일한 길이로 구성
        한다.


 헤더     패킷 전체 길이       식별자
          (4byte)      (4byte)


 로그인                                회원ID          PassWord
           24          LOGIN
                                    (8byte)        (8byte)


파일요청                                회원ID        파일의 HashKey
           80       FILE_REQUEST
                                    (8byte)       (64byte)


파일 전송                                         파일내용
        파일길이+8      FILE_TRANSFER
                                              가변길이
    18.
소켓 초기화
1. WSAStartup함수
 – 윈도우 소켓을 초기화한다.
 – Ws2_32.dll함수를 응용 프로그램의 영역으로 로드한다.

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

 – WSADATA구조체 변수는 WSAStartup함수가 반환하는 윈도우 소
   켓의 세부 정보를 가져 온다.
 – 첫 번째 인자에는 소켓 라이브러리의 버전을 저장한다.
    • 하위 바이트에는 메이저 버전 : 2
    • 상위 바이트에는 마이너 버전 : 2
    • MAKEWORD(하위, 상위)
       – MAKEWORD(3,5) : 이 코드는 윈도우 버전 3.5를 의미한다.
2. WSACleanup함수
 – 윈도우 소켓을 종료하는 함수이다.
 – Ws2_32.dll을 사용하지 못하게 한다.

  WSACleanup();
3. 메모리 바이트 순서
  – 인텔 계열의 CPU는 리틀 엔디안이라는 방식으로 데이터를 메모
    리에 저장한다.
    • 16진수 “2F78”은 782F로 메모리에 저장된다.
  – 모토로라의 CPU는 빅 엔디안이라는 방식으로 데이터를 메모리에
    저장한다.
    • 16진수 “2F78”은 2F78
  – 네트워크 바이트 순서는 빅 엔디언을 이용합니다.
• htons 함수
  – htons 함수는 “host to network short”의 약자이다.
  – 호스트 바이트 순서로 되어있는 unsigned short를 네트워크 바이
    트 순서로 변환한다.
  – 이 함수는 소켓 프로그래밍에서 포트 번호를 변환하기 위해서 사
    용한다.
• ntohs 함수
  – ntohs 함수는 “network to host short”의 약자이다.
  – 네트워크 바이트 순서로 되어 있는 unsigned short자료형을 호스
    트 바이트 순서로 변환한다.
3. 메모리 바이트 순서
• htonl함수
  – htonl함수는 “host to network long”의 약자이다.
  – 이 함수는 호스트 바이트 순서로 된 unsigned long자료형을 네트
    워크 바이트 순서로 변환한다.
  – 이 함수는 소켓 프로그래밍에서 IP주소를 변환하기 위해 사용한다.
• ntohl함수
  – ntohl함수는 “network to host long”의 약자이다.
  – 네트워크 바이트 순서로 된 unsigned long자료형을 호스트 바이
    트 순서로 변환한다.
• inet_ntoa함수
  – 4바이트로 된 IP주소를 도트 표기법에 기반한 문자열로 변환한다.
  – 클라이언트의 주소를 문자열로 변환해서 보여줄 때 사용
• inet_addr함수
  – inet_addr함수는 internet address의 약자이다.
  – 도트 표기법으로 되어 있는 주소 문자열을 4바이트 IP주소로 변환
   19.
파일 전송
 1. TransmitFile함수
BOOL TransmitFile(
       SOCKET                    hSocket,
       HANDLE                    hFile,
       DWORD                     nNumberOfBytesToWrite,
       DWORD                     nNumberOfBytesPerSend,
       LPOVERLAPPED              lpOverlapped,
       LPTRANSMIT_FILE_BUFFERS   lpTransmitBuffers,
       DWORD                     dwFlags );

   – hSocket : 파일을 보낼 소켓의 핸들
   – hFile : 전송할 파일의 핸들
   – nNumberOfBytesToWrite : 전송할 양으로 파일 전체를 전송할 때
     는 0을 사용한다.
   – nNumberOfBytesPerSend : 한 번에 전송할 패킷의 크기. 패킷의
     크기를 시스템에 맡길 경우, 0을 사용한다.
   – lpOverlapped : 중첩 입출력 구조체로 비동기 작업을 수행할 수
     있도록 한다.
   – lpTransmitBuffers : 파일을 전송하기 전에 전송할 헤더와 전송 후
     에 전송할 테일을 가리키는 구조체 포인터이다.
#include   <winsock2.h>
#include   <windows.h>
#include   "resource.h"
#include   <commctrl.h>
#include   <Mswsock.h>

BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
              WSADATA wsaData;
              WSAStartup(MAKEWORD(2,2), &wsaData);

                DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ;

                WSACleanup();
                return TRUE;
}

void DispErrorMessage()
{
               DWORD ErrorCode = GetLastError();
               char errMsg[1024];
               FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,ErrorCode,0,errMsg,1204,NULL);
               MessageBox(NULL,errMsg,"",MB_OK);
}

BOOL GetFileName(char temp[])
{
             strcpy(temp,"123.txt");
             OPENFILENAME ofn;
             ZeroMemory(&ofn,sizeof(ofn));
             ofn.lStructSize = sizeof(OPENFILENAME);
             ofn.hwndOwner = NULL;
             ofn.lpstrFilter = "모든 파일(*.*)\0*.*\0텍스트 파일(*.txt)\0*.txt\0\0\0";
             ofn.lpstrFile = temp;
             ofn.nFilterIndex = 2;
             ofn.nMaxFile = 256;
             ofn.Flags = OFN_EXPLORER | OFN_ALLOWMULTISELECT | OFN_ENABLESIZING ;
             return GetOpenFileName(&ofn);
}
typedef struct Tansmitstruct
{
                char pFileName[256];
                int            nFileSize;
}TRANSMITSTRUCT;

BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
           static HWND hEdit1;
           static HWND hButton1;
           static SOCKET hServerSock;
           static SOCKET hClientSock;
           switch (message)
           {
           case WM_INITDIALOG :
                         {
                                       hEdit1 = GetDlgItem(hDlg,IDC_EDIT1);

                                              hButton1 = GetDlgItem(hDlg,IDC_BUTTON1);
                                              EnableWindow(hButton1,FALSE);

                                              hServerSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

                                              sockaddr_in ServerAddr;
                                              ZeroMemory(&ServerAddr,sizeof(sockaddr_in));
                                              ServerAddr.sin_addr.s_addr = ADDR_ANY;
                                              ServerAddr.sin_family = AF_INET;
                                              ServerAddr.sin_port = htons(50000);

                                              bind(hServerSock,(sockaddr *)&ServerAddr,sizeof(sockaddr_in));

                                              listen(hServerSock,SOMAXCONN);

                                              SetWindowText(hEdit1,"클라이언트 접속을 기다리고 있습니다.");
                              }
                              return TRUE ;
case WM_COMMAND :
           switch (LOWORD (wParam))
           {
           case IDC_ACCEPT:
                        {
                                      sockaddr_in ClientAddr;
                                      int nAddrLen = sizeof(ClientAddr);
                                      hClientSock = accept(hServerSock,(sockaddr *)&ClientAddr,&nAddrLen);
                                      if (hClientSock == INVALID_SOCKET)
                                      {
                                                      int ErrorCode = WSAGetLastError();
                                                      char errMsg[1024];
                                                      FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,ErrorCode,0,
                                                      MessageBox(NULL,errMsg,"",MB_OK);
                                      }
                                      char temp[256];
                                      wsprintf(temp,"%s 클라이언트 접속 요청",inet_ntoa(ClientAddr.sin_addr));
                                      SetWindowText(hEdit1,temp);

                                      EnableWindow(hButton1,TRUE);

                                      return TRUE;
                         }
            case IDC_BUTTON1:
                         {
                                      char TransFileName[256];
                                      if (GetFileName(TransFileName) == FALSE)
                                      {
                                                     return TRUE;
                                      }
                                      char temp[256];
                                      wsprintf(temp,"%s 파일을 전송합니다.",TransFileName);
                                      SetWindowText(hEdit1,temp);

                                      HANDLE hFile = CreateFile(TransFileName,GENERIC_READ,FILE_SHARE_READ,NULL,OP
                                      if (hFile == INVALID_HANDLE_VALUE)
                                      {
                                                      DispErrorMessage();
                                                      return TRUE;
                                      }
                                                BY_HANDLE_FILE_INFORMATION fileinfo;
                                                GetFileInformationByHandle(hFile,&fileinfo);

                                                char FileName[256];
                                                char FileExt[256];
                                                _splitpath(TransFileName,NULL,NULL,FileName,FileExt);
                                                strcat(FileName,FileExt);

                                                TRANSMITSTRUCT transmitstruct;
                                                transmitstruct.nFileSize = fileinfo.nFileSizeLow;
                                                strcpy(transmitstruct.pFileName,FileName);

                                                TRANSMIT_FILE_BUFFERS TransBuf;
                                                ZeroMemory(&TransBuf,sizeof(TRANSMIT_FILE_BUFFERS));

                                                char pTailMsg[32] = "End Of File";
                                                TransBuf.Head = &transmitstruct;
                                                TransBuf.HeadLength = sizeof(transmitstruct);
                                                TransBuf.Tail = pTailMsg;
                                                TransBuf.TailLength = sizeof(pTailMsg);

                                                BOOL bTrans = TransmitFile(hClientSock,hFile,0,0,NULL,&TransBuf,0);
                                                if (bTrans == FALSE)
                                                {
                                                               DispErrorMessage();
                                                }
                                                CloseHandle(hFile);

                                                SetWindowText(hEdit1,"파일 전송을 완료했습니다.");
                                                return TRUE;
                                 }
                     case IDOK :
                     case IDCANCEL :
                                 closesocket(hServerSock);
                                 closesocket(hClientSock);
                                 EndDialog (hDlg, 0) ;
                                 return TRUE ;
                     }
    break ;
    }
    return FALSE ;
}
#include   <winsock2.h>
#include   <windows.h>
#include   <commctrl.h>
#include   "resource.h"

BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
              WSADATA wsaData;
              WSAStartup(MAKEWORD(2,2), &wsaData);

                DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ;

                WSACleanup();
                return TRUE;
}

void DispErrorMessage()
{
               DWORD ErrorCode = GetLastError();
               char errMsg[1024];
               FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,ErrorCode,0,errMsg,1204,NULL);
               MessageBox(NULL,errMsg,"",MB_OK);
}

typedef struct Tansmitstruct
{
                char pFileName[256];
                int            nFileSize;
}TRANSMITSTRUCT;
BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
           static HWND hEdit1;
           static HWND hButton1,hProgress;
           static SOCKET hClientSock;
           switch (message)
           {
           case WM_INITDIALOG :
                         {
                                       hEdit1 = GetDlgItem(hDlg,IDC_EDIT1);

                                        hButton1 = GetDlgItem(hDlg,IDC_BUTTON1);
                                        hProgress = GetDlgItem(hDlg,IDC_PROGRESS1);
                                        EnableWindow(hButton1,FALSE);

                                     hClientSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
                       }
                       return TRUE ;
            case WM_COMMAND :
                       switch (LOWORD (wParam))
                       {
                       case IDC_CONNECT:
                                     {
                                                    sockaddr_in ServerAddr;
                                                    ZeroMemory(&ServerAddr,sizeof(sockaddr_in));
                                                    ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
                                                    ServerAddr.sin_family = AF_INET;
                                                    ServerAddr.sin_port = htons(50000);

                                                      connect(hClientSock,(sockaddr *)&ServerAddr,sizeof(ServerAddr));

                                                      SetWindowText(hEdit1,"서버에 접속되었습니다.");

                                                      EnableWindow(hButton1,TRUE);

                                                      return TRUE;
                                        }
case IDC_BUTTON1:
             {
                    SetWindowText(hEdit1,"서버에서 파일을 수신 중입니다.");

                    SendMessage(hProgress,PBM_SETRANGE32,0,0);
                    SendMessage(hProgress,PBM_SETPOS,0,0);

                    TRANSMITSTRUCT transstruct;
                    int nTotalSize = sizeof(TRANSMITSTRUCT);
                    int nTotalRecv = 0;
                    do{
                                    int nReceived = recv(hClientSock,(char *)(&transstruct+nTotalRecv),nTo
                                    nTotalRecv += nReceived;
                    }while(nTotalSize != nTotalRecv);

                    SendMessage(hProgress,PBM_SETRANGE32,0,transstruct.nFileSize);
                    SendMessage(hProgress,PBM_SETPOS,0,0);

                    char temp[256];
                    wsprintf(temp,"%s(크기 : %d k)파일 수신 중 입니다.",transstruct.pFileName,transstruct.n
                    SetWindowText(hEdit1,temp);

                    HANDLE hFile = CreateFile(transstruct.pFileName,GENERIC_WRITE,FILE_SHARE_READ
                    if (hFile == INVALID_HANDLE_VALUE)
                    {
                                    DispErrorMessage();
                                    return TRUE;
                    }

                    BYTE pFileBuf[1024];
                    nTotalSize = transstruct.nFileSize;
                    nTotalRecv = 0;
                                                do{
                                                               DWORD dwByteRead;
                                                               if ( (nTotalSize-nTotalRecv) > sizeof(pFileBuf))
                                                                                dwByteRead = sizeof(pFileBuf);
                                                               else
                                                                                dwByteRead = nTotalSize-nTotalRecv;
                                                               int nReceived = recv(hClientSock,(char *)&pFileBuf,dwByteRead,0);
                                                               if (nReceived == SOCKET_ERROR)
                                                               {
                                                                                DispErrorMessage();
                                                                                CloseHandle(hFile);
                                                                                return TRUE;
                                                               }
                                                               nTotalRecv += nReceived;

                                                                DWORD dwByteWritten = 0;
                                                                WriteFile(hFile,pFileBuf,dwByteRead,&dwByteWritten,NULL);
                                                                SendMessage(hProgress,PBM_SETPOS,nTotalRecv,0);
                                                }while(nTotalSize != nTotalRecv);

                                                CloseHandle(hFile);

                                                nTotalSize = 32;
                                                nTotalRecv = 0;
                                                do{
                                                                int nReceived = recv(hClientSock,(char *)&pFileBuf+nTotalRecv,nTotalSiz
                                                                nTotalRecv += nReceived;
                                                }while(nTotalSize != nTotalRecv);

                                                SetWindowText(hEdit1,"파일 수신을 완료했습니다.");
                                                return TRUE;
                                 }
                     case IDOK :
                     case IDCANCEL :
                                 closesocket(hClientSock);
                                 EndDialog (hDlg, 0) ;
                                 return TRUE ;
                     }
    break ;
    }
    return FALSE ;
}
     20.
1 대 1 채팅
#include   <winsock2.h>
#include   <windows.h>
#include   <commctrl.h>
#include   "resource.h"

#define WM_SOCKTEVENT WM_USER+100
void DispErrorMessage()
{
               DWORD ErrorCode = GetLastError();
               char errMsg[1024];
               FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,ErrorCode,0,errMsg,1204,NULL);
               MessageBox(NULL,errMsg,"",MB_OK);
}

BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
HINSTANCE hInst;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
              WSADATA wsaData;
              WSAStartup(MAKEWORD(2,2), &wsaData);
              hInst = hInstance;

                HMODULE hMod = LoadLibrary("RICHED32.DLL");

                InitCommonControls();
                DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ;
                FreeLibrary(hMod);

                WSACleanup();
                return TRUE;
}
BOOL CALLBACK DlgProc2 (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
           static HWND hIPCtrl;
           static HWND hRadio1,hRadio2;
           switch (message)
           {
           case WM_INITDIALOG :
                          {
                                        hIPCtrl = GetDlgItem(hDlg,IDC_IPADDRESS1);
                                        hRadio1 = GetDlgItem(hDlg,IDC_RADIO1);
                                        hRadio2 = GetDlgItem(hDlg,IDC_RADIO2);
                                        SendMessage(hRadio1,BM_SETCHECK,TRUE,NULL);
                                        EnableWindow(hIPCtrl,FALSE);
                          }
                          return TRUE ;
           case WM_COMMAND :
                          switch (LOWORD (wParam))
                          {
                          case IDC_RADIO1:
                                        EnableWindow(hIPCtrl,FALSE);
                                        return TRUE ;
                          case IDC_RADIO2:
                                        EnableWindow(hIPCtrl,TRUE);
                                        return TRUE ;
                          case IDOK :
                          case IDCANCEL :
                                        if (SendMessage(hRadio1,BM_GETCHECK,NULL,NULL))
                                        {
                                                       EndDialog (hDlg, 0) ;
                                        }
                                        else
                                        {
                                                       DWORD nAddress;
                                                       int nIP = (int)SendMessage(hIPCtrl,IPM_GETADDRESS,0,(LPARAM)&nAddress);
                                                       if (nIP != 4)
                                                                        MessageBox(hDlg,"IP주소를 모두 입력하세요",NULL,MB_OK);
                                                       else
                                                                        EndDialog (hDlg, nAddress) ;
                                        }
                                        return TRUE ;
                          }
           break ;
           }
           return FALSE ;
}
BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
           static HWND hRichEdit1,hEdit1;
           static HWND hButton1;
           static SOCKET hClientSock;
           static SOCKET hServerSock;
           switch (message)
           {
           case WM_INITDIALOG :
                         {
                                        hEdit1 = GetDlgItem(hDlg,IDC_EDIT1);
                                        hRichEdit1 = GetDlgItem(hDlg,IDC_RICHEDIT1);
                                        hButton1 = GetDlgItem(hDlg,IDC_BUTTON1);

                                         EnableWindow(hButton1,FALSE);
                                         UpdateWindow(hDlg);

                                         DWORD dwIP = DialogBox(hInst,MAKEINTRESOURCE(IDD_DIALOG2), NULL, DlgProc2) ;
                                         if (dwIP == 0)
                                         {
                                                        hServerSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

                                                       sockaddr_in ServerAddr;
                                                       ZeroMemory(&ServerAddr,sizeof(sockaddr_in));
                                                       ServerAddr.sin_addr.s_addr = ADDR_ANY;
                                                       ServerAddr.sin_family = AF_INET;
                                                       ServerAddr.sin_port = htons(50000);

                                                       bind(hServerSock,(sockaddr *)&ServerAddr,sizeof(sockaddr_in));

                                                       listen(hServerSock,SOMAXCONN);

                                                       WSAAsyncSelect(hServerSock,hDlg,WM_SOCKTEVENT,FD_ACCEPT);
                                                       SetWindowText(hDlg,"서버");
                                         }
                            else
                            {
                                          hClientSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

                                          sockaddr_in ServerAddr;
                                          ZeroMemory(&ServerAddr,sizeof(sockaddr_in));
                                          ServerAddr.sin_addr.s_addr = htonl(dwIP);
                                          ServerAddr.sin_family = AF_INET;
                                          ServerAddr.sin_port = htons(50000);

                                          connect(hClientSock,(sockaddr *)&ServerAddr,sizeof(ServerAddr));

                                          WSAAsyncSelect(hClientSock,hDlg,WM_SOCKTEVENT,FD_READ|FD_CLOSE);
                                          SetWindowText(hDlg,"클라언트");
                            }
                            EnableWindow(hButton1,TRUE);
            }
            return TRUE ;
case WM_SOCKTEVENT:
            {
                            int nEvent = LOWORD(lParam);
                            int nError = HIWORD(lParam);
                            switch(nEvent)
                            {
                            case FD_ACCEPT:
                                            {
                                                         sockaddr_in ClientAddr;
                                                         int nAddrLen = sizeof(ClientAddr);

                                                         hClientSock = accept(hServerSock,(sockaddr *)&ClientAddr,&nAddrLen
                                                         closesocket(hServerSock);
                                                         hServerSock = INVALID_SOCKET;

                                                         WSAAsyncSelect(hClientSock,hDlg,WM_SOCKTEVENT,FD_READ|FD_CL
                                         }
                                         break;
                            case FD_READ:
                                         {
                                                         char temp[256];
                                                         int nReceived = recv(hClientSock,temp,256,NULL);
                                                         SetWindowText(hRichEdit1,temp);
                                          }
                                          break;
                                  case FD_CLOSE:
                                              {
                                                                closesocket(hClientSock);
                                                                hClientSock = INVALID_SOCKET;

                                                                MessageBox(hDlg,"상대방에서 연결을 종료했습니다.",NULL,MB_OK);
                                                  }
                                                  break;
                            }
               }
               return TRUE;
    case WM_COMMAND :
               switch (LOWORD (wParam))
               {
               case IDC_BUTTON1:
                            {
                                                  char temp[256];
                                                  GetWindowText(hEdit1,temp,256);
                                                  send(hClientSock,temp,256,NULL);
                                  }
                                  return TRUE ;
                     case IDOK :
                     case IDCANCEL :
                                 closesocket(hClientSock);
                                 EndDialog (hDlg, 0) ;
                                 return TRUE ;
                     }
    break ;
    }
    return FALSE ;
}

								
To top