리스트 컨트롤

 

1. 리스트 컨트롤

- 리스트 박스 vs 리스트 컨트롤

리스트 박스 : 단일 항목 나열

리스트 컨트롤 : 리스트를 여러개 가짐

- 다양한 뷰 스타일 제공 (Icon, Small Icon, List, Report 스타일)

 

- 다양한 매크로 함수 제공

- 포함 헤더 : Commctrl.h, Windowsx.h

 

Report 스타일로 설정

 

이(Report)는 엑셀과 비슷한 구조로, 행과 열로 구분할 수 있다.

열은 컬럼(Column),  행은 아이템(Item)이라고 부른다. 열의 모든 내용은 서브 아이템(Sub Item)이라고 부른다.

시작 인덱스는 모두 0 이다.

 

 

ListView_InsertColumn() 함수를 통한 컬럼 생성과 문자열 등록 

void ListView_InsertColumn(
   HWND hwnd, //윈도우 핸들
   int iCol, //컬럼 인덱스
   const LPLVCOLUMN pcol //LVCOLUMN의 메모리 주소
);

 

+ LVCOLUMN 구조체

typedef struct tagLVCOLUMNA {
  UINT  mask; //컬럼에 적용할 속성 설정(뭐뭐 조합할지)
  int   fmt; 
  int   cx;
  LPSTR pszText;
  int   cchTextMax;
  int   iSubItem;
  int   iImage;
  int   iOrder;
  int   cxMin;
  int   cxDefault;
  int   cxIdeal;
} LVCOLUMNA, *LPLVCOLUMNA;

 

+ LVCLOUMN mask : 컬럼에 적용할 속성 결정 (OR연산 이용)

LVCF_FMT 정렬(왼쪽, 가운데, 오른쪽)
LVCF_WIDTH 길이 적용
LVCF_TEXT 문자열 적용
LVCF_SUBITEM 서브 아이템 적용

 

+ LVCOLUMN fmt : 컬럼과 서브 아이템의 데이터 정렬 형식 지정

LVCFMT_LEFT Text is left-aligned.
LVCFMT_RIGHT Text is right-aligned.
LVCFMT_CENTER Text is centered.

주의할 점 : 인덱스가 0인 컬럼과 서브아이템은 항상 왼쪽 정렬

 

+ LVCOLUMN cx, pszText, iSubItem 

cx          : 가로 길이

pszText   : 문자열 지정

iSubItem : 서브 아이템 인덱스 

 


#include "framework.h"
#include "WindowsProject9_ListControl.h"
#include "Commctrl.h"
#include "Windowsx.h"

INT_PTR CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);
    return 0;
}

INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    static HWND hList;
    char* strMenu[] = { "항목1","항목2", "항목3", "항목4" };
    LVCOLUMN lvColumn;

    switch (message)
    {
    case WM_INITDIALOG:
        hList = GetDlgItem(hDlg, IDC_LIST1);
        lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM | LVCF_TEXT;
        lvColumn.fmt = LVCFMT_CENTER;
        
        for (int i = 0; i < 4; i++) {
            lvColumn.cx = strlen(strMenu[i]) * 10;//너무 붙어있지 말라고
            lvColumn.pszText = strMenu[i];
            ListView_InsertColumn(hList, i, &lvColumn);
        }
        return (INT_PTR)TRUE;

    case WM_CLOSE:
        EndDialog(hDlg, 0);
        return (INT_PTR)TRUE;
    }
    return (INT_PTR)FALSE;
}

 

항목1,2,3,4 컬럼이 생성된다.

 


※ 아이템 관련 매크로 함수 ListView_GetItemCount

void ListView_GetItemCount(
   HWND hwnd
);
void ListView_InsertItem(
   HWND hwnd,
   const LPLVITEM pitem
);

 

+ LVITEM 구조체

typedef struct tagLVITEMA {
  UINT   mask; //보통 LVIF_TEXT 만 지정하면 됨
  int    iItem; //ListView_GetItemCount
  int    iSubItem; //0 고정
  UINT   state;
  UINT   stateMask;
  LPSTR  pszText; //문자열 설정
  int    cchTextMax;
  int    iImage;
  LPARAM lParam;
  int    iIndent;
  int    iGroupId;
  UINT   cColumns;
  PUINT  puColumns;
  int    *piColFmt;
  int    iGroup;
} LVITEMA, *LPLVITEMA;

 

변수 추가
버튼 눌림을 위한 WM_COMMAND문 추가
버튼을 누를때 마다 아이템이 자신의 인덱스를 넣으면서 추가된다.

 


※ 아이템 항목 설정 ListView_SetItemText() 이용 예제

#include "framework.h"
#include "WindowsProject9_ListControl.h"
#include "Commctrl.h"
#include "Windowsx.h"
#include <stdio.h>

INT_PTR CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);
    return 0;
}

INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    static HWND hList;
    char* strMenu[] = { "순번","C","C++","WIN32" };
    LVCOLUMN lvColumn;
    LVITEM lvItem;
    char string[100];

    switch (message)
    {
    case WM_INITDIALOG:
        hList = GetDlgItem(hDlg, IDC_LIST1);
        lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM | LVCF_TEXT;
        lvColumn.fmt = LVCFMT_CENTER;
        
        for (int i = 0; i < 4; i++) {
            lvColumn.cx = 50;
            lvColumn.pszText = strMenu[i];
            ListView_InsertColumn(hList, i, &lvColumn);
        }
        return (INT_PTR)TRUE;

    case WM_CLOSE:
        EndDialog(hDlg, 0);
        return (INT_PTR)TRUE;
    
    case WM_COMMAND:
        if (LOWORD(wParam) == IDC_BUTTON1) {
            lvItem.iItem = ListView_GetItemCount(hList);
            lvItem.iSubItem = 0;
            lvItem.mask = LVIF_TEXT;
            sprintf_s(string, "%d", lvItem.iItem);
            lvItem.pszText = string;
            ListView_InsertItem(hList, &lvItem);

            GetDlgItemText(hDlg, IDC_EDIT1, string, 10);
            ListView_SetItemText(hList, lvItem.iItem, 1, string);
            GetDlgItemText(hDlg, IDC_EDIT2, string, 10);
            ListView_SetItemText(hList, lvItem.iItem, 2, string);
            GetDlgItemText(hDlg, IDC_EDIT3, string, 10);
            ListView_SetItemText(hList, lvItem.iItem, 3, string);

            //에디트 컨트롤 클리어
            SetDlgItemText(hDlg, IDC_EDIT1, NULL);
            SetDlgItemText(hDlg, IDC_EDIT2, NULL);
            SetDlgItemText(hDlg, IDC_EDIT3, NULL);
            
            return (INT_PTR)TRUE;
        }
    }

    return (INT_PTR)FALSE;
}

값을 입력하면 삽입과 동시에 칸이 초기화 되는 것까지 확인 가능하다


※ 서브 아이템 데이터 가져오기 ListView_GetItemText()

void ListView_GetItemText(
   HWND hwndLV,
   int iItem,
   int iSubItem_,
   LPTSTR pszText_, //문자열 저장을 위한 메모리 공간
   int cchTextMax_
);

 

1단계) 아이템 선택에 대한 메세지 알아내기

- 발생 메세지 : WM_NOTIFY, 부가적 정보가 담긴 메모리 주소가 lparam에 담김

(부가적 정보 = 통지 코드, 컨트롤 핸들, 아이디가 저장된 메모리 주소)

 

lParam 다루는 방법 : NMHDR 구조체 사용

typedef struct tagNMHDR {
  HWND     hwndFrom; //윈도우 핸들
  UINT_PTR idFrom; //컨트롤 아이디
  UINT     code; //통지 코드, NM_CLICK
} NMHDR;

 

2단계) 아이템 인덱스 알아내기 ListView_GetNextItem()

void ListView_GetNextItem(
   HWND hwnd,
   int i, //임의의 인덱스, 선택이 없을 때:-1
   UINT flags //LVNI_SELECTED
);

 

+) 리스트 컨트롤 뷰 설정  ListView_SetExtendedListViewStyle()

void ListView_SetExtendedListViewStyle(
   HWND hwndLV,
   DWORD dwExStyle //LVS_EX_FULLROWSELECT : 하나의 행 전체 선택, LVS_EX_GRIDLINES : 아이템(열) 전체 선택
);

 

DlgProc에 인덱스 변수 추가

 

WM_INITDIALOG에 ListView_SetExtendedListViewStyle 속성을 설정한다

 

WM_NOTIFY 문 추가
선택 결과가 아래 Edit Control에서 보인다


※ 데이터 값 수정하기

ListView_GetItemText()

ListView_SetItemText()

nIndex 값 다시 설정
nIndex 가 -1이 아닐때. 즉, 선택되었을 때를 의미
수정

'Project > WIN32 API' 카테고리의 다른 글

컨트롤(2)  (0) 2021.07.22
컨트롤  (0) 2021.07.21
다이얼로그  (0) 2021.07.21
키보드, 마우스  (0) 2021.07.13
그래픽 오브젝트  (0) 2021.07.12
1. 에디트 컨트롤(박스)
2. 콤보 박스
3. 리스트 박스

 

※ 컨트롤도 윈도우이다. 따라서 윈도우 핸들이 존재한다.

GetDlgItem()을 통해 핸들을 얻는다.

 

1. Edit Control

: 사용자로부터 데이터 입력 받음

데이터 입력, 수정, 삭제를 위한 컨트롤

 

사용방법? 함수 사용

 

여러 속성을 제공하고 있음

- Password : 비밀번호(값이 보이지 않음)

- Visible : 화면에 출력

- Read Only : 오로지 읽도록(처리된 결과를 보여줄 때 사용)

- Border : 경계선 있/없

- Number : 값을 숫자로만 받음

...

 

※ 내용 읽기 함수  GetDlgItemText, GetDlgItemInt

UINT GetDlgItemTextA( //문자열 읽기
  HWND  hDlg,
  int   nIDDlgItem,
  LPSTR lpString,
  int   cchMax
);
UINT GetDlgItemInt( //정수값 받기
  HWND hDlg,
  int  nIDDlgItem,
  BOOL *lpTranslated, //메모리 주소(보통 NULL)
  BOOL bSigned //TRUE면 음수도 받을 수 있고, FALSE면 양수만 입력 받음
);

 

<EditControl에 쓰여진 데이터 읽기 코드>

#include "framework.h"
#include "WindowsProject7_control2.h"
#include <stdio.h>

INT_PTR CALLBACK    DlgProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);
    return 0;
}

INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    char string[100];
    int nVal;

    switch (message)
    {
    case WM_CLOSE:
        EndDialog(hDlg, 0);
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDC_BUTTON1:
            GetDlgItemText(hDlg, IDC_EDIT1, string, 99);
            MessageBoxA(hDlg, string, "확인", MB_OK);
            return (INT_PTR)TRUE;

        case IDC_BUTTON2:
            nVal = GetDlgItemInt(hDlg, IDC_EDIT1, NULL, FALSE);
            sprintf_s(string, "%d", nVal);
            MessageBoxA(hDlg, string, "확인", MB_OK);
            return (INT_PTR)TRUE;
        }       
    }
    return (INT_PTR)FALSE;
}

GetDlgItemText 버튼으로 문자열을 읽어들이고, GetDlgItemInt 버튼으로 양의 정수만(마지막 매개변수를 False로 설정)을 읽어들이고 있다.

 

문자열 입력 후 Text()버튼
문자열 입력 후 Int() 버튼

 

숫자 입력 후 Text() 버튼과 Int()버튼


※ 데이터 설정 함수 SetDlgItemText, SetDlgItemInt

BOOL SetDlgItemText(
  HWND   hDlg,
  int    nIDDlgItem,
  LPCSTR lpString //설정할 문자열 지정
);
BOOL SetDlgItemInt(
  HWND hDlg,
  int  nIDDlgItem,
  UINT uValue, //지정하고자 하는 값
  BOOL bSigned //부호 설정
);

 

Get -> Set 으로 바꾸면 된다.

 

각각의 버튼을 누르면 Edit Text에 내용이 설정된다.


2. Combo Box

여러 항목을 리스트 형태로 출력 및 선택

※ Simple, Drop Down, Drop List 형태 제공

Dialog 설정

'정렬' 속성은 기본적으로 TRUE로 설정이 되어 있다. 

따라서 우리가 항목을 추가할 때 자동으로 정렬이 되므로 자동 정렬되는 것을 원치 않으면 FALSE로 설정하면 된다.

'형식' 속성을 보면 Simple, Drop Down, Drop List 형태를 제공하고 있음을 알 수 있다.

 

 

※ 항목이 펼쳐지는 영역 설정 가능

항목이 펼쳐지는 영역을 설정할 수도 있다.

 

※ 매크로 함수 사용

- Windowsx.h 포함

실제 MSDN에 combo box를 검색하면 사용가능한 매크로 함수 목록이 뜬다.

 

※ 윈도우 핸들 구하는 함수 GetDlgItem

HWND GetDlgItem(
  HWND hDlg,
  int  nIDDlgItem
);

 

ComboBox_AddString() 사용 실습 

void ComboBox_AddString(
   hwndCtl,
   lpsz
);

 

#include "framework.h"
#include "WindowsProject8_ComboBox.h"
#include <stdio.h>
#include "Windowsx.h"

INT_PTR CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);
    return 0;
}

INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HWND hComboBox; //정적 변수 선언
    static const char* strMenu[] = { "메뉴1","메뉴2","메뉴3" }; //문자열 각각의 메모리 주소가 저장됨
    switch (message)
    {
    case WM_INITDIALOG:
        hComboBox = GetDlgItem(hDlg, IDC_COMBO1); //핸들 얻음
        for (int i = 0; i < 3; i++) {
            ComboBox_AddString(hComboBox, strMenu[i]);
        }
        return (INT_PTR)TRUE;

    case WM_CLOSE:
        EndDialog(hDlg, 0);
        return (INT_PTR)TRUE;
    }
    return (INT_PTR)FALSE;
}

hComboBox 핸들을 선언하고, 각각의 문자열 주소를 저장할 strMenu를 선언한다.

Dialog를 실행할 때 콤보박스의 핸들을 얻어와서 hComboBox에 저장한 뒤 AddString()을 통해 설정한 문자열을 넣어주고 있다.

 

결과

 

 

※ ComboBox_DeleteString() 사용 실습 - 역순으로 삭제

void ComboBox_DeleteString(
   hwndCtl,
   index
);

DlgProc 함수에서 표시된 부분을 추가한다. DeleteString 함수 사용

 

버튼을 클릭할 때마다 항목이 하나씩 사라지고, 모두 지운 뒤에 버튼을 클릭하면 설정한 메세지 창이 뜬다.

 

 

ComboBox_InsertString() 사용하여 항목 추가 실습

void ComboBox_InsertString(
   hwndCtl,
   index,
   lpsz
);

 

코드 추가
WM_COMMAND 문을 switch-case문으로 바꾼 후 삽입 버튼을 누를 때 삽입이 되도록 한다.

 

해당 인덱스에 알맞게 추가되었다.

 

ComboBox_GetCurSel()을 통해 인덱스 조사

void ComboBox_GetCurSel(
   hwndCtl
);

IDC_BUTTON3 추가 후 인덱스 조사하기

 

정상적으로 처리하는 것을 알 수 있다.

단지, 메뉴를 추가한 뒤 인덱스가 늘어난 경우에 대해선 처리하지 못하므로 추후에 이를 case 0,1,2로 나누지 말고 for문으로 바꿔야할 것 같다.

 


3. List Box

- 리스트 출력 영역 안에서 항목 선택

- 지정된 영역보다 많은 항목은 스크롤 바를 통해 선택

- windowsx.h 포함

 

MSDN에서 list box macro를 검색하면 해당 매크로 함수들의 목록이 나온다

 

IDC_BUTTON4 추가
리스트 박스 메뉴 추가

여기서 10이 넘어가는 순간 자동정렬 되므로 속성창에 가서 '정렬'을 FALSE로 바꿔줘야 한다.

 

 

 

'Project > WIN32 API' 카테고리의 다른 글

컨트롤(3)  (0) 2021.07.22
컨트롤  (0) 2021.07.21
다이얼로그  (0) 2021.07.21
키보드, 마우스  (0) 2021.07.13
그래픽 오브젝트  (0) 2021.07.12
1. 컨트롤 기초
2. 버튼 컨트롤

 

1. 컨트롤 기초

※ 생성 방법

- CreateWindow()

- 폼 + 도구 상자 

다이얼로그 폼
도구 상자에서 드래그 앤 드롭
이런 식으로 설정이 가능하다. 입력칸 아래는 '도구 상자' 속에 있는 Static Text 기능을 가져온 것이다.

- Static Text는 단순 문자열 출력 도구이다.

 

 

 

컨트롤 아이디 : 중복되지 않는 양의 정수값

→ Visual Studio에서 자동으로 할당. IDC_XXXX로 시작한다.

 

 

※ 컨트롤의 윈도우 핸들 GetDlgItem()

HWND GetDlgItem(
  HWND hDlg,
  int  nIDDlgItem
);

 

 

※ 컨트롤 메시지 처리 WM_COMMAND 

- LOWORD(wParam) 아이디 → 여러 컨트롤 구분

- HIWORD(wParam) 컨트롤 상태, 통지 코드 

   :: 통지코드 예시

   BN_CLICKED 버튼이 클릭된 상태

   BN_DBCLICK 버튼이 더블클릭된 상태

   BN_SETFOCUS 버튼이 선택된 상태

   BN_DISABLE 버튼 사용 불가 상태

 

 

※ 컨트롤 속성

기본 속성

속성 역할
Caption 컨트롤에 표시되는 텍스트
ID 컨트롤의 아이디
Visible 컨트롤의 출력 여부를 결정

 

ID, Visible 속성
Caption 속성


2. 버튼 컨트롤

: 푸시, 라디오, 체크 버튼

 

→ 생성 방법 ?

폼 + 도구 상자 + 드래그 앤 드롭 (위에서 한 방식과 같다)

 

사용 시 기본 메세지 WM_COMMAND가 발생하고, 각각의 버튼에 해당되는 LOWORD(wParam) 아이디를 이용하면 된다.

 

① 푸시 버튼

푸시 버튼 추가
폼 기반의 대화 상자라 코드가 아주 간단하다. 저 39줄이 전부
버튼 클릭 시

 


② 라디오 버튼

역할 : 여러 항목 중에서 하나 선택

 

라디오 버튼 2개 생성 후 아이디 확인
WM_COMMAND에서 추가

 

 

※ 상태 조사 함수 IsDlgButtonChecked

UINT IsDlgButtonChecked(
  HWND hDlg,
  int  nIDButton
);

리턴 코드 : BST_CHECKED, BST_UNCHECKED, BST_INDETERMINATE

 

 

기존 코드를 조금 수정한다.

체크된 라디오 버튼 각각을 구분하기 위해서 배열을 선언한 후, 몇 번째 라디오 버튼이 눌렸는지 상태를 확인하여 출력하는 코드이다.

 

각각 선택되었을 때 결과가 다르다

 

※ 상태(선택/비선택) 변경

상태 변경 함수 

 

1) BOOL CheckDlgButton()

: 임의로 개별 선택 및 비선택 설정

BOOL CheckDlgButton(
  HWND hDlg,
  int  nIDButton,
  UINT uCheck //BST_CHECKE, BST_UNCHECKED
);

 

2) BOOL CheckRadioButton()

그룹 안에 있는 라디오 버튼 하나만 임의로 선택

그룹? Group 속성을 TRUE로 설정하면 됨

BOOL CheckRadioButton(
  HWND hDlg,
  int  nIDFirstButton,
  int  nIDLastButton,
  int  nIDCheckButton
);

라디오 버튼 수정. 라디오1의 Group 설정을 TRUE로 하면 1,2,3이 하나의 그룹이 된다. 

만약 라디오 버튼 4,5,6을 추가하고 이 셋을 하나의 그룹으로 만들고자 한다면, 라디오 4의 Group 설정만 TRUE로 하면 된다. 

 

버튼2 수정
1을 선택한 후 '라디오' 버튼을 누르면 저절로 2번이 선택된다. 3번도 마찬가지


③ 체크 버튼

: 다수의 컨트롤 선택, 다수의 선택 사항 파악

 

nCheckID 배열 추가
WM_COMMAND의 switch문에 이를 추가한다.

화면에 On, Off를 표시하기 위해서 InvalidateRect 를 사용한다.

 

WM_PAINT 문을 추가한다.

 

체크된 것만 On으로 바뀜

 

'Project > WIN32 API' 카테고리의 다른 글

컨트롤(3)  (0) 2021.07.22
컨트롤(2)  (0) 2021.07.22
다이얼로그  (0) 2021.07.21
키보드, 마우스  (0) 2021.07.13
그래픽 오브젝트  (0) 2021.07.12
1. 모달형 다이얼로그
2. 모델리스형 다이얼로그
3. 폼 기반 윈도우 프로그램
4. 파일열기 모달형 다이얼로그

 

다이얼로그

① 모달(Modal)형 다이얼로그 : 대화상자를 닫기 전 다른 윈도우로 전환할 수 없는 대화상자

② 모델리스(Modeless)형 다이얼로그 : 대화상자를 열어 놓은 채로 다른 윈도우로 전환할 수 있는 대화상자

 

1. 모달형 다이얼로그

- 다이얼로그가 최우선 순위

- 대표적인 다이얼로그 형태 MessageBox()

 

- MessageBox()를 이용한 모달형 다이얼로그 확인 예제

리소스 - Menu - 모달형 메뉴 추가 후 ID 편집으로 ID 확인

 

WM_command 에서 MessageBox() 띄우는 간단한 예시 코드 작성

 

모달형 메시지 박스

 

※ 모달 대화 상자 생성 함수

DialogBox() 

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

 

 

※ 폼 생성과 lpTemplate 설정

폼 아이디 → 고유 값

폼 아이디를 변환하는 매크로 함수 MAKEINTRESOURCE() → lpTemplate에 지정

 

폼 생성

 

※ 다이얼로그 프로시저

:: 일반 윈도우 메시지와 동일하다. 콜백 함수의 형식을 가진다(메세지에 따라 반응).

DLGPROC Dlgproc;

INT_PTR CALLBACK Dlgproc(
  HWND hwndDlg,
  UINT uMsg,
  WPARAM wParam,
  LPARAM lParam
);

DialogProc과 WndProc 차이

- WndProc 메세지 처리 → DefWindowProc

- Dialog 메세지 처리 → TRUE, FALSE

 

 

※ EndDialog() 함수 호출

- 다이얼로그 해제되면서 부모 윈도우로 리턴해줄 수 있음. 이때 사용

BOOL EndDialog(
  HWND    hDlg,
  INT_PTR nResult
);

 

nResult :: 부모 윈도우에게 전달되는 값(전역변수). 기본 값 = 0 

 

→ 기본적인 해제 적용 방법? WM_CLOSE에서 EndDialog() 호출

 

DlgProc 선언
위 메시지 박스 코드 바로 아래에 DialogBox 함수 작성
About 함수를 복사해온 후 일부 코드 추가

Close 버튼과 OK 버튼, 취소 버튼 등을 누르면 EndDialog() 함수가 실행이 되면서 다이얼로그 창이 해제된다.

 

DialogBox 메세지


2. 모델리스형 다이얼로그

※ 생성 함수 CreateDialog 

- 모달 다이얼로그를 생성하는 DialogBox 함수와 이름만 다를 뿐 매개변수는 똑같다.

HWND CreateDialog(
   HINSTANCE hInstance,
   LPCTSTR lpTemplate,
   HWND hWndParent,
   DLGPROC lpDialogFunc
);

 

※ 모달형과 폼 생성 방법은 동일하다.

주의할 사항은 폼 속성 중에 Visible을 True로 설정해야 한다는 것! 

 

 

※ 해제 함수 DestroyWindow

BOOL DestroyWindow(HWND hWnd);

→ WM_CLOSE에서 호출한다.

 

리소스 메뉴 추가
visible = TRUE
WM_command case 문 추가
WndProc에 추가
메뉴
결과


3. 폼 기반의 윈도우 프로그램

:: WinMain()과 DialogBox() 호출 필요함 

DialogBox(인스턴스, 다이얼로그 아이디, 0, 콜백함수) → 부모 윈도우 자리에 0

 

※ DialogProc 연결

전체 코드(비교적 짧다)
빈 창

 

※ 타이틀 바꾸기

1. 리소스 속성창에서 변경

타이틀 변경

2. SetWindowText()로 변경

 

다이얼로그 시작시 제목을 "이미지"로 변경하고자 함
변경 확인


4. 파일열기 모달형 다이얼로그

:: 파일경로, 파일명을 알아오는 기능, #include <commdlg.h>

 

※ 생성 함수 GetOpenFileName

BOOL GetOpenFileNameA(
  LPOPENFILENAMEA lpofn
);

GetOpenFileName의 매개변수로는 OPENFILENAME이라는 구조체가 들어가며, 이 구조체 안에는 수많은 정보가 있다.

typedef struct tagOFN_NT4A {
  DWORD         lStructSize; //구조체 크기(몇 바이트 차지하는지)
  HWND          hwndOwner; //부모 윈도우 핸들
  HINSTANCE     hInstance;
  LPCSTR        lpstrFilter; //파일형식 설명 → \*.확장자\0 방식
  LPSTR         lpstrCustomFilter;
  DWORD         nMaxCustFilter;
  DWORD         nFilterIndex; //선택한 파일 인덱스, 1부터 시작
  LPSTR         lpstrFile; //전체 경로를 저장할 배열 설정. static 변수 또는 전역변수로 설정
  DWORD         nMaxFile; //파일경로 최대 길이 설정
  LPSTR         lpstrFileTitle; //파일명을 저장할 배열 설정. static 변수 또는 전역변수로 설정
  DWORD         nMaxFileTitle; //파일명 길이 지정
  LPCSTR        lpstrInitialDir;
  LPCSTR        lpstrTitle; //다이얼로그 타이틀 문자열 출력
  DWORD         Flags;
  WORD          nFileOffset;
  WORD          nFileExtension;
  LPCSTR        lpstrDefExt;
  LPARAM        lCustData;
  LPOFNHOOKPROC lpfnHook;
  LPCSTR        lpTemplateName;
} OPENFILENAME_NT4A, *LPOPENFILENAME_NT4A;

 

리소스 메뉴 추가
WndProc에 변수 선언
WM_COMMAND에 열기(32771)가 선택됐을 때의 동작을 선언

 

화면에 표시

※ 파일 저장 모달형 다이얼로그

:: 파일 열기 모달형과 대부분 같다. #include <commdlg.h>

 

※ 생성 함수 GetSaveFileName

BOOL GetSaveFileName(
  LPOPENFILENAMEA lpofn
);

 

리소스 메뉴 추가
열기와 거의 유사하나 표시한 부분만 조금 달라진다. 확장자의 유무에 따라 확장자 추가를 결정한다.

 

'Project > WIN32 API' 카테고리의 다른 글

컨트롤(2)  (0) 2021.07.22
컨트롤  (0) 2021.07.21
키보드, 마우스  (0) 2021.07.13
그래픽 오브젝트  (0) 2021.07.12
그래픽  (0) 2021.07.08

컴퓨터 주변기기들은 연결시킬 때 주변기기 인식은 OS가 한다.

주변기기들에 대한 정보들이 OS로 보내지고 OS는 정보를 메시지(값)로 가공한다.

이를 해당 응용 프로그램에 보내주거나 가져오는 것을 반복하는 것이다.

 

 

1) 키보드

※ 문자 키에 발생하는 메시지 --> 키보드가 눌렸구나

WM_CHAR

 

※ 대소문자 구분 방법 --> 어떤 문자일까?

wParam(WndProc 매개변수) : 아스키 코드 값

 

WndProc에 다음과 같은 코드 추가
키보드 입력 시 화면

 

※ 모든 키에 대해 발생하는 메시지

WM_KEYDOWN

 

※ 키 구분 방법

wParam : 가상 키 코드. 문자는 대문자로만 인식함!

 

- 가상 키코드?

   VK_LEFT, VK_HOME 등 '1','A' 문자 상수 사용

 

WM_KEYDOWN 메세지 코드
왼쪽 키 누를 시
F1 키 누를 시

 

+ GetAsyncKeyState() 

:: 실시간으로 키 입력을 체크

메세지 큐에 저장되는 키 메세지의 단점을 보완

키 눌림이 있으면 음수값 리턴

- 게임이나 시뮬레이션 등에 사용됨

SHORT GetAsyncKeyState(int vKey);

- 사용 예시

if (GetAsyncKeyState(VK_F1) < 0 ) { 코드 입력 };

 


2) 마우스

:: 키보드와 같은 방식

 

※ 마우스 메세지 WM_MOUSEMOVE

:: 마우스 이동 시 발생

 

- 마우스 위치 정보 

LOWORD(lParam) → x 좌표

HIWORD(lParam) → y 좌표

 

※ 그 외 마우스 메세지

WM_LBUTTONDOWN : 마우스 왼쪽 버튼 눌림

WM_LBUTTONUP : 마우스 왼쪽 버튼 놓임

WM_RBUTTONDOWN : 마우스 오른쪽 버튼 눌림

WM_RBUTTONUP : 마우스 오른쪽 버튼 놓임

 

 

WndProc에 마우스 관련 변수와 메세지 case
WM_PAINT에 추가

※ sprintf_s를 사용하기 위해서 C++ 코드 초반에 #include stdio.h 추가 해줘야함

 

 

결과

 

※ wParam으로 전달되는 코드

MK_LBUTTON, MK_MBUTTON, MK_RBUTTON, MK_CONTROL, MK_SHIFT

 

※ 드래그

WM_MOUSEMOVE + MK_LBUTTON

 

 

WM_MOUSEMOVE case 추가

먼저 WndProc 초반에 char string_drag[100]; 선언 후 case 문을 추가한다. 

그리고 WM_PAINT에 TextOut 문장을 추가한다.

 

결과

 

 

'Project > WIN32 API' 카테고리의 다른 글

컨트롤  (0) 2021.07.21
다이얼로그  (0) 2021.07.21
그래픽 오브젝트  (0) 2021.07.12
그래픽  (0) 2021.07.08
리소스  (0) 2021.07.07

<C언어와의 비교>

int main() --> WinMain(){     }  & WinProc(){      }

printf() --> TextOut(), DC

 

GDI 오브젝트

Pen, Brush, Enhanced metafile, Font, Memory DC, Bipmap, Palette, Region...

 

- 사용 방법 2가지!

1. 운영체제에서 제공

2. 사용자가 직접 설정

 

스톡오브젝트

:: OS에서 제공하는 GDI 오브젝트를 사용하기 위한 함수

HGDIOBJ GetStockObject(int fnObject)

- fnObject

브러쉬 : BLACK_BRUSH, WHITE_BRUSH, DKGRAY_BRUSH, HOLLOW_BRUSH ..

펜 : BLACK_PEN, DC_PEN, WHITE_PEN ..

폰트 : ANSI_FIXED_FONT, ANSI_VAR_FONT

팔레트 : DEFAULT_PALLETTE

 

 

GDI 오브젝트 설정과 해제
HGDIOOBJ SelectObject(HDC hdc, HGDIOBJ hgdiobj);
BOOL DeleteObject(HGDIOBJ hObject);

 


1) 펜

:: 선 색상, 선 굵기, 스타일 지정(점선, 실선 등)

핸들 명 : HPEN

HPEN CreatePen(int fnPenStyle, int nWidth, COLORREF crColor);

- fnPenStyle : PS_SOLID(실선), PS_DASH(점선)...

 

 

<펜을 이용한 선 그리기 예제>

BOOL MoveToEx(HDC hdc, int X, int Y, LPPOINT lpPoint);
LineTo(HDC hdc, int nXEnd, intnYEnd);

 

 

 

① 펜 생성 : CreatePen() , GetStockObject() 

② 펜 선택 : SelectObject()

③ 선 그리기 : MoveToEx(), LineTo()

④ 펜 제거 및 이전 펜 설정 : DeleteObject() - 스톡 오브젝트는 이를 사용할 필요 없음 , SelectObject() 

 

WM_PAINT에 해당 부분 추가

사용자가 직접 지정(Create)하거나

운영체제에서 제공하는 것을 사용하는 방법(스톡 오브젝트)의 차이는

DeleteObject의 유무이다.

 

결과

 

선의 스타일만 바뀐 코드 추가

 

결과

 

<펜을 이용한 도형 출력>

사각형, 원

BOOL Rectangle(HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
BOOL Ellipse(HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);

 

도형 그리기 코드 추가

 

결과


2) 브러쉬

:: 도형의 내부를 색상과 패턴으로 채우는 역할

 

- 사용 방법

1. 스톡 오브젝트 이용(GetStockObject())

2. 사용자 생성

 

- 브러쉬 생성 함수

CreateSolidBrush(), CreateHatchBrush(), CreatePatternBrush(), CreateBrushIndirect(),...

HBRUSH CreateSolidBrush(COLORREF crColor);

 

① 브러쉬 생성 : CreateSolidBrush() --> 핸들 얻기

② 브러쉬 설정 : SelectObject() --> 핸들 리턴

③ 도형 출력

④ 이전 브러쉬 복구 : SelectObject() 

⑤ 생성한 브러쉬 제거 : DeleteObject()

 

WM_PAINT 새로 작성
결과


3) 비트맵

- 이미지 종류

bmp, jpg, gif, tga 등

 

※ 비트맵을 다루는 두 가지 방법

1. 비트맵을 리소스에 등록하여 사용 (리소스에 추가하여 ID를 사용)

2. LoadImage() 함수를 이용하여 파일로부터 읽어내는 방법

 

① 비트맵의 핸들을 얻는다 : LoadBitmap(), LoadImage() 둘 중 하나

HBITMAP LoadBitmapA(
  HINSTANCE hInstance,
  LPCSTR    lpBitmapName
);
HANDLE LoadImageA(
  HINSTANCE hInst,
  LPCSTR    name,
  UINT      type,
  int       cx,
  int       cy,
  UINT      fuLoad
);

>>핸들 형변환(HANDLE --> HBITMAP) 후 사용

 

 

② 메모리 DC 생성 : CreateCompatibleDC()

메모리 DC ?

:: 화면과 동일한 특성을 가진 메모리의 일부.

- 컴퓨터는 이미지를 읽어서 바로 불러들이는 것이 아니라 이미지를 메모리에 올린 후 이것을 출력하는 방법을 택하고 있기 때문에 필요

HDC CreateCompatibleDC(HDC hdc);

 

 

③ 메모리 DC에 비트맵 적용 : SelectObject()

HGDIOOBJ SelectObject(HDC hdc, HGDIOBJ hgdiobj);

메모리 DC(핸들)를 명시한 후 비트맵(OBJ)을 지정하면 비트맵의 내용이 메모리 DC에 쏙 들어가게 된다.

 

 

④ 비트맵 출력 : BitBlt()

BOOL BitBlt(
  HDC   hdc,
  int   nXDest,
  int   nYDest,
  int   nWidth,
  int   nHeight,
  HDC   hdcSrc, //메모리 DC
  int   nXSrc, //메모리 DC의 어디서부터 시작할지
  int   nYSrc,
  DWORD dwRop
);

이미지의 헤더 정보를 알면 이 사이즈들을 다 알 필요가 없다!

 

typedef struct tagBITMAP {
  LONG   bmType;
  LONG   bmWidth;
  LONG   bmHeight;
  LONG   bmWidthBytes;
  WORD   bmPlanes;
  WORD   bmBitsPixel;
  LPVOID bmBits;
} BITMAP, *PBITMAP, *NPBITMAP, *LPBITMAP;
int GetObject(
  HGDIOBJ hgdiobj,
  int    cbBuffer, //메모리 버퍼 사이즈
  LPVOID pv
);

--> 비트맵의 가로 세로 크기를 알 수 있다.

 

⑤ 메모리 DC와 비트맵 제거 : DeleteDC(), DeleteObject()

BOOL DeleteDC(HDC hdc);
BOOL DeleteObject(HGDIOBJ hObject);

 

 

'Project > WIN32 API' 카테고리의 다른 글

다이얼로그  (0) 2021.07.21
키보드, 마우스  (0) 2021.07.13
그래픽  (0) 2021.07.08
리소스  (0) 2021.07.07
WndProc  (0) 2021.06.26

GDI(Graphics Device Interface)

:: 운영체제의 한 부분으로 출력을 담당한다(Gdi.dll)

:: GDI는 하드웨어에 관련된 사항들을 통제한다.

DC(Device Context)

:: 출력하기 위한 장치(화면, 프린터)의 특성을 저장하는 구조체

:: 명령 흐름 : Application → DC → GDI → 출력 

 

- DC를 사용하는 그래픽 오브젝트

비트맵, 브러쉬, 펜, 팔레트, 폰트, Region, Path 등

 

- DC의 데이터 형 = 핸들

HDC

typedef HANDLE HDC;
typedef PVOID HANDLE;
typedef void * PVOID;

결국 HDC는 주소값을 저장하긴 하지만 데이터 타입이 지정되지 않은 것이다.

따라서 이를 양의 정수(아이디)로서 이해하는 것이 좋다.

 

 

※ 화면 DC에 관련된 함수

BeginPaint(), EndPaint(), GetDC(), ReleaseDC() 등

 

 

<BeginPaint, EndPaint>

이 함수들은 WM_PAINT 메시지 처리에서만 사용가능하다

(WM_PAINT : 클라이언트 영역이 지워지거나 새로이 출력하고자 할 때, 윈도우끼리 겹쳐있다가 활성화되었을 때, 최소화되었다가 다시 최대화될 때 등 클라이언트 영역을 다시 실행할 때 나타나는 메세지)

HDC BeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint);
BOOL EndPaint(HWND hWnd, const PAINTSTRUCT *lpPaint);

프로젝트명.cpp에서 확인

실제로 프로젝트 생성시 자동생성되는 코드에서 기본적으로 잘 세팅되어 있다.

 

 

<GetDC, ReleaseDC>

GetDC()로 DC를 얻은 후에는 ReleaseDC()를 통해 반드시 운영체제에 반납해줘야 한다.

getdc, releasedc 같은 경우 지정된 장소가 없어서 아무곳에서나 선언 가능하다.

HDC GetDC(HWND hWnd);
int ReleaseDC(HWND hWnd, HDC hDC);

일반적인 WM_PAINT 메세지 외에선 GetDC, ReleaseDC를 통해 출력할 수 있다.


※ 문자 출력

문자 출력을 위한 함수 = Textout

BOOL TextOut(HDC hdc, int nXStart, int nYStart, LPCTSTR lpString, int cbString);

<필요한 것>

- hcd

- x와 y 좌표(0,0)~(299,299)

- 출력하고자 하는 문자열을 담고 있는 문자열 포인터

  sprintf (메모리의 char형 배열에 출력) 사용

- 출력하고자 하는 문자열의 길이

  strlen (문자열의 길이 측정) 사용

 

기존 WM_PAINT 처리 부분에 이를 추가해준다
(0,0) 부분에 해당 문자열이 출력됨을 확인할 수 있다.

 

GetDC(), ReleaseDC()를 이용해서도 화면에 출력할 수 있다.

기존에 만들어 뒀던 Test() 함수에 출력 내용에 대한 코드를 작성해준다.

Test() 함수
WndProc 에 LBUTTONDOWN 추가

WM_LBUTTONDOWN은 마우스 버튼을 클릭했을 때 실행되는 메시지이다. 

이 메세지 발생시 Test 함수가 작동되도록 코드를 작성한다.

 

(100,100) 에 해당 문자열이 출력됨을 확인할 수 있다.


※ 출력 색상 설정

:: 삼원색(R,G,B) 이용

:: 색상 범위 : 0 ~ 255, DWORD(unsigned long)로 다룬다.

 

→ 색상 관련 매크로 함수 = RGB(), GetRValue(DWORD rgb), GetGValue(DWORD rgb), GetBValue(DWORD rgb)

COLORREF RGB(
	BYTE byRed,
	BYTE byGreen,
	BYTE byBlue
);

COLORREF는 DWORD를 재정의한 데이터 형이다(똑같다)

 

 

기존 WM_PAINT 코드에 추가

 

(200,200)에 색상값이 잘 출력되었다.

 

 

+ 점(dot) 출력 함수 = SetPixel()

SetPixel(
	HDC hdc,
	int X, int Y,
	COLORREF crColor
);

 

 

+ 점(dot) 출력 함수 = GetPixel()

화면 DC에 출력된 색상값을 구하는 함수

GetPixel(
	HDC hdc,
	int nXPos, int nYPos
);

 

WM_PAINT 코드에 추가

 

아주 자세히 보면 (300,300)에 빨간색 점이 찍혀있다. (400,400)에선 GetPixel을 통한 색상값이 표현되어 있다.


※ 문자열 출력 관련 함수

SetTextColor() :: 출력할 문자열의 색상 설정

COLORREF SetTextColor (
	HDC hdc.
	COLORREF crColor
);

SetBkColor() :: 글자의 배경 색상 설정

COLORREF SetBkColor (
	HDC hdc,
	COLORREF crColor
);

 

WM_PAINT 코드에 추가
결과

 

InvalidateRect()

:: 화면의 일부(RECT 구조체 사용) 또는 전체(NULL, 0)를 다시 출력할 때 사용

-> WM_PAINT 메시지 발생(BeginPaint ~ EndPaint 영역 실행)

-> 무효화 영역 또는 업그레이드 영역 (다시 그리고자 하는 영역) 지정 가능

 

BOOL InvalidateRect(HWND hWnd, const RECT *lPRect, BOOL bErase);

typedef struct_RECT{ //구조체 원형
	LONG left;
	LONG top;
	LONG right;
	LONG bottom;
} RECT *PRECT;

+ BOOL bErase

: true면 그리고자 하는 일부 영역(클라이언트 영역)을 싹 다 지우고 다시 그림

false면 그 영역에 지우지 않고 계속해서 그림

 

WndProc 앞 부분에 선언

 

WM_LBUTTONDOWN case에 InvalidateRect 추가

&rect 자리에 0이 들어간다면 전체영역을 다시 그릴 것이고

FALSE 자리에 TRUE가 온다면 선택된 영역에 겹쳐서 그려지는게 아니라 새롭게(안 겹치게) 그려질 것이다.

 

WM_PAINT 메세지 발생하므로 이 곳에 원하는 행위 작성

 

제일 처음에 WM_PAINT 메시지 발생

 

false : 마우스 왼쪽 버튼을 누를수록 정했던 영역(0,0,150,150)에만 점이 겹치면서 찍힌다.

'Project > WIN32 API' 카테고리의 다른 글

키보드, 마우스  (0) 2021.07.13
그래픽 오브젝트  (0) 2021.07.12
리소스  (0) 2021.07.07
WndProc  (0) 2021.06.26
윈도우 창 생성 과정  (0) 2021.06.25

리소스

:: 메뉴, 다이얼로그, 비트맵, 커서, 아이콘, 엑셀레이터(단축키), 문자열

→ 리소스는 아이디(중복되지 않는 양의 정수 값)로 다룬다.

 

① 아이콘 리소스

:: 왼쪽 상단과 최소화 때 하단 제목 표시줄에 나타나는 작은 이미지

→ *.ico 파일 확장자

 

크기 : 16x16, 32x32, 48 x48, 색상비트 : 4/8/32비트

※ 기본 : 4비트(16색), 32x32

 

→ 아이콘 아이디

접두사 IDI_

 

파일 - 새로 만들기 - 파일 - 아이콘 파일 - 기본이 32x32, 4비트 아이콘이므로 이곳을 편집

- 청록색 부분은 '투명색'으로 출력에서 제외되는 색상

- 저장하면 아이콘에 대한 아이디가 다르게 설정되어 별도로 사용할 수 있다.

실제 Resource.h 파일에 가보면 다른 아이디가 설정되어 있음을 알 수 있다

 

해당 프로젝트 리소스 폴더의 아이콘 폴더에 추가

 

- 아이콘 로딩 함수는 아래와 같다.

HICON LoadIcon(
	__in HINSTANCE hInstance,
	__in LPCTSTR IpIconName
);

이때 IpIconName에는 아이콘 아이디 정수값을 문자열 형식으로 지정할 수 있는데, 여기에 사용되는 함수가 MAKEINTRESOURCE() 이다.

LPTSTR MAKEINTRESOURCE(
	WORD wInteger
);

자동 생성 코드의 아이콘 부분 수정

 

디버깅 시

- 좌측 상단에 ICON2의 내용이, 화면 표시 줄에는 ICON1의 내용이 출력됨을 확인할 수 있다.

 

 


② 커서 리소스

:: 커서 설정은 아이콘과 유사하며, 접두사는 IDC_ 이고 *.cur로 표현된다.

 

1) 커서 로딩 방법

 

※ 운영체제에서 제공하는 기본 커서 아이디

IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_IBEAM, IDC_ICON, IDC_NO, IDC_SIZE, IDC_WAIT 등,,

 

- 기본 커서 로딩 함수는 아래와 같다.

HCURSOR LoadCursor(
	__in HINSTANCE hInstance,
	__in LPCTSTR IpCursorName
);

운영체제의 기본 커서 사용시 hInstance는 NULL 값, IpCursorName은 커서 기본 아이디를 넣으면 된다.

 

아이콘 설정 시 봤던 cpp 코드에서 커서 부분 코드를 수정

임의의 커서를 적용하기 위해선 인스턴스를 지정해줘야 한다.

 

편집한 후 핫 스폿 설정까지 한다.

(핫 스폿 설정이란 이 커서가 화면 내에 위치한다는 기준이 되는 지점)

 

 

이 커서는 클라이언트 영역에서만 적용된다.

 

 

2) 메세지를 통한 커서 설정 방법 --> 전체 윈도우 영역 안에서의 커서를 통제

 

- 마우스 이동 시 발생하는 메세지

WM_MOUSEMOVE, WM_SETCURSOR

HCURSOR SetCursor(
	__in HCURSOR hCursor
);

메세지를 로딩한 후 핸들을 통해 커서를 지정해준다

 

WndProc 에 위와 같은 새로운 case문을 추가

.

전체 윈도우 창에서 커서가 동작


③ 메뉴 리소스

:: 응용 프로그램의 부가적인 기능을 선택하여 실행

:: 사용자에 의해 선택되는 사용자 인터페이스

 

메뉴를 만들고 아이디도 확인할 수 있다.
Resource.h 에서 아이디 추가된 것 확인

 

프로젝트명.cpp 수정

 

생성한 메뉴 확인

그러나 이를 클릭하면 아무 일도 일어나지 않는다. 

따라서 메뉴를 실행시키려면 WndProc에 이와 관련된 메세지와 정보를 전달하는 코드가 필요하다.

 

※ 메뉴 관련 메시지

WM_COMMAND : 메뉴 선택

 

 

※ 메뉴 항목 구분

- 메뉴 아이디 

메뉴 아이디는 wParam에 LOWORD를 통해 그 값이 들어오게 된다.

LOWORD(wParam)

wParam은 unsigned int로 4 Byte 크기를 가진다.

 

 

※ 메뉴 항목 호출 확인

메뉴 항목 호출을 확인하기 위해 '메시지 박스'를 이용할 것이다.

int WINAPI MessageBox(
	_In_opt_ HWND hWnd, //핸들
	_In_opt_ LPCTSTR IpText, //출력하고자 하는 문자열
	_In_opt_ LPCTSTR IpCaption, //타이틀 바
	_In_ UINT uType //메세지 형태
);

→ uType에 해당하는 아이디

MB_OK, MB_OKCANCEL, MB_RETRYCANCEL, MB_YESNO, MB_YESNOCANCEL 등

 

WndProc에 위와 같은 case 문을 추가

 

각각의 메뉴를 누르면 메시지 박스가 뜬다.

 


④ 문자열 리소스

 

리소스 뷰의 스트링 테이블을 가서 원하는 문자열을 추가

캡션이 실제로 출력되는 내용이다.

 

※ 타이틀 명 수정해보기

코드 초반에 제목에 대한 배열이 선언되어 있음을 확인할 수 있다.

 

코드 수정

 

제목이 수정되었다

하지만 이런 과정이 굉장히 번거롭다. 

따라서 문자열은 이런 방법보단 코드에 바로 넣어주는 경우가 많다.

 


☆ 정리

- 리소스 편집기를 활용하여 리소스를 설정할 수 있다.

- 리소스 아이디는 중복되지 않게 알아서 저장된다.

- 기본 설정일 때 인스턴스는 NULL로 설정된다.

'Project > WIN32 API' 카테고리의 다른 글

그래픽 오브젝트  (0) 2021.07.12
그래픽  (0) 2021.07.08
WndProc  (0) 2021.06.26
윈도우 창 생성 과정  (0) 2021.06.25
윈도우즈 프로그래밍  (0) 2021.06.25

 메시지 처리 함수란 메시지가 발생할 때 프로그램의 반응을 처리하는 일을 하며 WinMain 함수와는 별도로 WndProc(Window Procedure)이라는 이름으로 존재한다. 

 

WinMain 내의 메시지 루프는 메시지를 메시지 처리 함수로 보내주기만 할 뿐이며 WndProc은 메시지가 입력되면 윈도우즈에 의해 호출되어 메시지를 처리한다. 이렇게 운영체제에 의해 호출되는 응용 프로그램 내의 함수콜백(Callback)함수라고 한다.

 

WndProc의 인수는 모두 4개이며 이전 글에서 봤던 MSG 구조체의 멤버 4개와 동일하다.

typedef struct tagMSG
{
    HWND        hwnd;
    UINT        message;
    WPARAM      wParam;
    LPARAM      lParam;
    DWORD       time;
    POINT       pt;
} MSG;

<WndProc>

hWnd : 메시지를 받을 윈도우의 핸들

iMessage : 어떤 변화가 발생했는가에 관한 정보. (메시지 변수)

wParam, IParam : iMessage에 따른 부가적인 정보 (어디쯤에서 마우스 버튼이 눌렸는가, 어떤 문자열이 입력됐는가,,)

 

WndProc의 구조는 아래와 같다. 메시지의 종류에 따라 다중 분기하여 메시지별로 처리를 진행한다.

switch(iMessage)
{
	case Msg1:
		처리1;
		return 0;
	case Msg2:
		처리2;
		return 0;
	case Msg3:
		처리3;
		return 0;
	default:
		return DefWindowProc(...);
}

마지막에 DefWindowProc 함수는 WndProc에서 처리하지 않은 나머지 메시지에 관한 처리를 해 준다.

 

이전 글의 코드에서의 메시지 처리 함수는 아래와 같이 되어 있다.

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

여기서는 WM_DESTROY 메시지만을 처리하고 있으며 나머지 메시지에 대해서는 DefWindowProc에게 맡긴다.

WM_DESTROY 외의 메시지는 모든 DefWindowProc 함수로 전달되며 이 함수에서 디폴트 처리를 수행해 준다. 

WndProc은 메시지를 처리했을 경우 반드시 0을 리턴해 주어야 한다.

또한 DefWindowProc 함수가 메시지를 처리했을 경우 이 함수가 리턴한 값을 WndProc 함수가 다시 리턴해 주어야 한다. 

 

 

'Project > WIN32 API' 카테고리의 다른 글

그래픽 오브젝트  (0) 2021.07.12
그래픽  (0) 2021.07.08
리소스  (0) 2021.07.07
윈도우 창 생성 과정  (0) 2021.06.25
윈도우즈 프로그래밍  (0) 2021.06.25

 

#include <windows.h>

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass="First";

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance
		  ,LPSTR lpszCmdParam,int nCmdShow)
{
	HWND hWnd;
	MSG Message;
	WNDCLASS WndClass;
	g_hInst=hInstance;
	
	WndClass.cbClsExtra=0;
	WndClass.cbWndExtra=0;
	WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
	WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
	WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
	WndClass.hInstance=hInstance;
	WndClass.lpfnWndProc=(WNDPROC)WndProc;
	WndClass.lpszClassName=lpszClass;
	WndClass.lpszMenuName=NULL;
	WndClass.style=CS_HREDRAW | CS_VREDRAW;
	RegisterClass(&WndClass);

	hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,
		  CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
		  NULL,(HMENU)NULL,hInstance,NULL);
	ShowWindow(hWnd,nCmdShow);
	
	while(GetMessage(&Message,0,0,0)) {
		TranslateMessage(&Message);
		DispatchMessage(&Message);
	}
	return Message.wParam;
}

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

실행 결과, 간단한 윈도우 창이 보인다.

 

1. 헤더 파일

첫 행을 보면 windows.h 하나만 include 되어있다.

여기에 모든 API 함수들의 원형과 사용하는 상수들이 죄다 정의되어 있다.

 

2. 시작점

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance ,LPSTR lpszCmdParam,int nCmdShow)

프로그램의 시작점인 엔트리 포인트(Entry Point)가 WinMain이다.

DOS의 main에서는 인수 사용여부에 따라 여러가지 원형이 있지만 WinMain의 원형은 고정되어 있다.

4개의 인수를 취하는데, 각 인수의 의미는 다음과 같다.

 

인수 의미
hInstance 프로그램의 인스턴스 핸들. 프로그램 자체를 일컫는 정수값
hPrevInstance 바로 앞에 실행된 현재 프로그램의 인스턴스 핸들
없을 경우에는 NULL이며 Win32에서는 항상 NULL
호환성을 위해 존재하는 인수이므로 신경쓰지 않아도 됨
IpCmdLine 명령행으로 입력된 프로그램 인수. DOS의 argv인수에 해당
nCmdShow 프로그램이 실행될 형태이며 최소화, 보통모양 등이 전달됨

 

인스턴스(instance)

:: 클래스가 메모리에 실제로 구현된 실체

HINSTANCE g_hInst;

윈도우즈 프로그램은 여러 개의 프로그램이 동시에 실행되는 멀티태스킹 시스템이며 하나의 프로그램이 여러 번 실행될 수 도 있다. 이때 실행되고 있는 각각의 프로그램을 프로그램 인스턴스라고 하며 간단히 줄여서 인스턴스 라고 한다.

 

예를 들어, 메모장 프로그램 창이 2개 띄어진 상황이라고 생각해보자.

두 프로그램 모두 메모장(Notepad.exe)이지만 운영체제는 각각 다른 메모리를 사용하는 다른 프로그램으로 인식한다.

이때 각 메모장은 서로 다른 인스턴스 핸들을 가지며, 운영체제는 이 인스턴스 핸들값으로 두 개의 메모장을 서로 구별한다.

 

 

3. 메시지 처리 함수

코드를 자세히 보면 두 개의 함수만 있다.

하나는 프로그램의 시작점인 WinMain이고 나머지 하나는 WndProc이다.

DOS에서는 main 함수만으로도 프로그램을 작성할 수 있지만 윈도우즈에서는 아주 특별한 경우를 제외하고는 이 두개의 함수가 모두 있어야 한다.

 

: WinMain에서는 윈도우를 만들고 화면에 출력하는 일만 한다. 그저 프로그램을 시작시키기만

: WndProc이 실질적인 처리를 한다.  이는 프로그램에 따라 천차만별로 달라지며 보통 WinMain 바로 윗부분에 WndProc 함수의 원형이 선언되어 있다.

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

 

 

4. 윈도우 클래스

WinMain 함수에서 하는 가장 중요한 일은 윈도우를 만드는 일이다.

이를 위해선 윈도우 클래스를 먼저 등록한 후 CreateWindow 함수를 호출해야 한다.

윈도우 클래스는 windows.h에 아래와 같이 정의되어 있다.

typedef struct tagWNDCLASS
{
    UINT        style;
    WNDPROC     lpfnWndProc;
    int         cbClsExtra;
    int         cbWndExtra;
    HINSTANCE   hInstance;
    HICON       hIcon;
    HCURSOR     hCursor;
    HBRUSH      hbrBackground;
    LPCSTR      lpszMenuName;
    LPCSTR      lpszClassName;
} WNDCLASS;

10개의 멤버를 가지고 있는데 각각 간단하게 설명하면 아래와 같다.

- style : 윈도우의 스타일

- lpfnWndProc : 윈도우 메시지 처리 함수 지정

- cbClsExtra, cbCWndExtra : 예약 영역, 여분의 공간

- hInstance : 이 윈도우 클래스를 사용하는 프로그램의 번호

- hIcon, hCursor : 이 윈도우가 사용할 마우스 커서와 최소화되었을 경우 출력될 아이콘 지정

- hbrBackground : 윈도우의 배경 색상

- lpszMenuName : 이 프로그램이 사용할 메뉴를 지정

- lpszClassName : 윈도우 클래스의 이름을 정의

 

 

ATOM RegisterClass( CONST WNDCLASS *lpWndClass);

RegisterClass 함수의 인수로 WndClass 구조체의 주소를 넘겨주면 된다.

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

이런 특성을 가진 윈도우를 앞으로 사용하겠다는 등록 과정인 셈이다.

 

윈도우 클래스를 등록한 후에는 등록한 윈도우 클래스를 기본으로 윈도우를 실제로 생성해야 한다. 

윈도우를 생성할 땐 CreateWindow 함수를 사용한다.

 

HWND CreateWindow(lpszClassName, lpszWindowName, dwStyle, x, y, nWidth, nHeight, hwndParent, hmenu, hinst, lpvParam)

11개의 인수를 가지고 있는데 각각 간단하게 설명하면 아래와 같다.

- lpszWindowName : 윈도우의 타이틀 바에 나타날 문자열

- dwStyle : 만들고자 하는 윈도우의 형태를 지정(시스템 메뉴, 최대 최소 버튼, 타이틀 바, 경계선 등)

- X, Y, nWidth, nHeight : 윈도우의 크기와 위치 지정. 픽셀 단위

- hWndParent : 부모 윈도우 있을 경우, 부모 윈도우의 핸들을 지정

- hmenu : 윈도우에서 사용할 메뉴의 핸들을 지정

- hinst : 윈도우를 만드는 주체, 즉 프로그램의 핸들을 지정

- lpvParam : CREATESTRUCTURE라는 구조체의 주소이며 특수한 목적에 사용. 보통 NULL 값

 

CreateWindow 함수는 윈도우에 관한 모든 정보를 메모리에 만든 후 윈도우 핸들을 리턴값으로 넘겨준다.

넘겨지는 윈도우 핸들은 hWnd라는 지역 변수에 저장되어 다른 곳에서 참조된다.

 

 

BOOL ShowWindow(hWnd, nCmdShow);

메모리에 저장되어진 윈도우를 화면으로 보이게 할 때 쓰이는 함수이다.

hWnd 인수는 화면으로 출력하고자 하는 윈도우의 핸들이며 CreateWindow 함수가 리턴한 핸들을 그대로 넘겨주면 된다.

 

 

각각의 자세한 의미를 파악하려면 아래의 링크를 참고하자

http://soen.kr/lecture/win32api/ApiLec.htm

 

API 강좌

 

soen.kr

 

 

윈도우를 만드는 과정을 간단하게 정리하면 아래와 같다.

 

WndClass 정의  : 윈도우의 기반이 되는 클래스 정의

CreateWindow : 메모리 상에 윈도우 생성

ShowWindow : 윈도우를 화면에 표시

메시지 루프 : 사용자로부터의 메시지를 처리

 

 

5. 메시지 루프

 여기서 메시지란 사용자나 시스템 내부적인 동작에 의해 발생된 일체의 변화에 대한 정보를 말한다.

쉽게 말해서 사용자가 마우스의 버튼을 눌렀다거나 키보드를 눌렀다거나 윈도우가 최소화되었거나 하는 정보들이 메시지이다.

 

메시지가 발생하면 프로그램에서는 메시지가 어떤 정보를 담고 있는가를 분석하여 어떤 루틴을 호출할 것인가를 결정한다. 

즉, 순서를 따르지 않고 주어진 메시지에 대한 반응을 정의하는 방식으로 프로그램이 진행된다는 뜻

 

보통 WinMain 함수의 끝에 아래와 같은 형식으로 존재한다.

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

메시지 루프는 세 개의 함수 호출로 이루어져 있으며 전체 루프는 while 문으로 싸여져 있다.

 

(1) BOOL GetMessage( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);

 이 함수는 시스템이 유지하는 메시지 큐에서 메시지를 읽어들인다.

읽어들인 메시지는 첫 번째 인수가 지정하는 MSG 구조체에 저장된다. 이 함수는 읽어들인 메시지가 프로그램을 종료하라는 WM_QUIT일 경우 False를 리턴하며 그 외에는 모두 True를 리턴한다. 

따라서 프로그램이 종료할 때까지 while 루프가 계속 실행된다. 나머지 인수는 일단 무시하도록 하자

 

(2) BOOL TranslateMessage( CONST MSG *lpMsg);

 이 함수는 키보드 입력 메시지를 가공하여 프로그램에서 쉽게 쓸 수 있도록 해준다.

즉, 키보드의 눌림(WM_KEYDOWN)과 떨어짐(WM_KEYUP)이 연속적으로 발생할 때 문자가 입력되었다는 메시지(WM_CHAR)를 만드는 역할을 한다. 

 

(3) LONG DispatchMessage( CONST MSG *lpmsg);

 이 함수는 시스템 메시지 큐에서 꺼낸 메시지를 프로그램의 메시지 처리 함수(WndProc)로 전달한다. 

이 함수에 의해 메시지가 프로그램으로 전달되며 프로그램에서는 전달된 메시지를 점검하여 다음 동작을 결정하게 된다.

 

결국, 메시지 루프가 하는 일이란 메시지 큐에서 메시지를 꺼내 메시지 처리 함수(WndProc)으로 보내주는 것 뿐이다.

 

--> 실제 메시지 처리는 별도의 메시지 처리 함수(WndProc)에서 수행한다. 메시지는 시스템의 변화에 대한 정보이며 MSG라는 구조체에 보관된다. 

typedef struct tagMSG
{
    HWND        hwnd;
    UINT        message;
    WPARAM      wParam;
    LPARAM      lParam;
    DWORD       time;
    POINT       pt;
} MSG;
멤버 의미
hwnd 메시지를 받을 윈도우 핸들
message 어떤 종류의 메시지인지. 제일 중요
wParam 전달된 메시지에 대한 부가적 정보. 32비트 값
IParam 전달된 메시지에 대한 부가적 정보. 32비트 값
time 메시지가 발생한 시간
pt 메시지가 발생했을 때의 마우스 위치

 message 멤버를 읽음으로써 메시지의 종류를 파악하며 message 값에 따라 프로그램의 반응이 달라진다.

GetMessage 함수는 읽은 메시지를 MSG 형 구조체에 대입해주며 이 구조체는 DispatchMessage 함수에 의해 응용 프로그램의 메시지 처리 함수(WndProc)으로 전달된다.

 

※ 가장 자주 사용되는 메시지 종류

메시지 의미
WM_QUIT 프로그램 종료 시 발생하는 메시지
WM_LBUTTONDOWN 마우스의 좌측 버튼을 누를 경우 발생
WM_CHAR 키보드로부터 문자가 입력될 때 발생
WM_PAINT 화면을 다시 그려야 할 필요가 있을 때 발생
WM_DESTROY 윈도우가 메모리에서 파괴될 때 발생
WM_CREATE 윈도우가 처음 만들어질 때 발생

 

'Project > WIN32 API' 카테고리의 다른 글

그래픽 오브젝트  (0) 2021.07.12
그래픽  (0) 2021.07.08
리소스  (0) 2021.07.07
WndProc  (0) 2021.06.26
윈도우즈 프로그래밍  (0) 2021.06.25

+ Recent posts