Win32_API - 실습 - 색상표 만들기

컴퓨터/Win32-API

728x90
반응형

서론

지금까지 배운 포스트들을 정리하면서 간단하게 RGB 개념을 적용해 3가지 인풋 박스에 R G B 순으로 숫자를 입력하고 색상을 표여주는 프로그램을 만들어 보도록 합시다.

 

전체 프로젝트 파일을 확인하고 싶으시면 Github을 참조해 주세요.

 

 

0. WinMain 

프로그램을 시작하는 WinMain 부분을 작성합니다.

프로그램의 메인이 윈도에 대한 설정과 메시지 루프 부분을 만들어 줍니다.

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    //윈도우 창 구조체 정의 및 적용
    WNDCLASSEXW wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = NULL;
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
    wcex.lpszClassName = L"ColorTable";
    wcex.hIconSm = NULL;
    RegisterClassExW(&wcex);
    //적용한 윈도우 생성 및 업데이트
    hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.
    HWND hWnd = CreateWindowW(L"ColorTable", L"ColorTable", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
    if (!hWnd)
    {
        return FALSE;
    }
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    MSG msg;
    // 기본 메시지 루프입니다:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
    }
    return (int) msg.wParam;
}

 

1.Edit 컨트롤 생성

R G B 각각의 값을 쓰고 읽기 위한 Edit 컨트롤을 만들어 줍니다. 

HWND heditR, heditG, heditB; // 전역변수

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    switch (message)
    {
    case WM_CREATE:
        heditR = CreateWindowW(L"edit", NULL, WS_BORDER | WS_CHILD | WS_VISIBLE | ES_RIGHT | ES_NUMBER, 30, 40, 50, 25, hWnd, (HMENU)1, hInst, NULL);
        heditG = CreateWindowW(L"edit", NULL, WS_BORDER | WS_CHILD | WS_VISIBLE | ES_RIGHT | ES_NUMBER, 30, 70, 50, 25, hWnd, (HMENU)2, hInst, NULL);
        heditB = CreateWindowW(L"edit", NULL, WS_BORDER | WS_CHILD | WS_VISIBLE | ES_RIGHT | ES_NUMBER, 30, 100, 50, 25, hWnd, (HMENU)3, hInst, NULL);
        break;
  • CreateWindow 함수를 이용하여 3개의 Edit 컨트롤을 만들었습니다.
  • RGB 값만을 넣기 위해 스타일은 오른쪽 정렬 및 숫자 ( ES_RIGHT | ES_NUMBER)
  • 각각의 컨트롤의 값을 구분하기 위해 식별 값을 넣어 주었습니다 (HMENU 부분)

Edit컨트롤 박스의 효과적인 구분을 위해 WM_PAINT: 메시지에서 TextOut 함수를 통해 표시를 해 주었습니다.

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            hdc = BeginPaint(hWnd, &ps);

            //RGB 표시를 하기 위한 텍스트
            TextOutW(hdc, 10, 45, L"R", 1);
            TextOutW(hdc, 10, 75, L"G", 1);
            TextOutW(hdc, 10, 105, L"B", 1);
            
            EndPaint(hWnd, &ps);
        }
        break;

 

현재까지의 프로그램을 실행하면 다음과 같은 결과가 나오게 됩니다.

2.Edit 컨트롤의 값 처리

이제 Edit컨트롤으로 부터 사용자에게 입력받은 값을 처리해주는 작업을 해야 됩니다.

생각해 보면 요구사항은 다음과 같을 것입니다.

  • 입력 값이 들어올 때마다 처리를 해주어야 한다.
  • 숫자만 입력돼야 되며 0~255까지 입력이 되어야 한다.
  • Edit 컨트롤로부터 받은 값들을 적용하는지 확인 및 사용자에게 보여주어야 한다.

우선 입력되는 숫자를 제한하고 값을 받아오는 처리를 해보도록 합시다.

 

우선 Edit에서의 이벤트 동작은 WM_COMMAND 메시지에 wParam을 통해 들어옵니다. 메시지 처리를 위한 틀을 만들어 봅시다.

case WM_COMMAND:

        switch (HIWORD(wParam))
        {
        //이벤트 메세지에 대한 구분
        case EN_CHANGE:
        
        	//각각의 식별자에 대한 처리
        	if (LOWORD(wParam) == 1)
        	if (LOWORD(wParam) == 2)
        	if (LOWORD(wParam) == 3)
            break;
        }
        break;

 

이후 0~255까지의 값만을 필요하기 때문에 이에 대한 처리를 해주도록 합시다.

우선 0~255 값을 받기 위한 전역 변수 int형 R, G, B와 임시적으로 Edit에서 문자열을 담기 위한 지역변수를 선언하도록 합시다.

int R, G, B; // 전역변수

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    WCHAR editword[10]; // 임시로 Edit에서 문자열값을 받기 위한 변수
   
//.....이하 소스코드
    case WM_COMMAND:

        switch (HIWORD(wParam))
        {
        case EN_CHANGE:

            //R 처리
            if (LOWORD(wParam) == 1) {

                int num = 0;
                if (GetWindowTextW(heditR, editword, GetWindowTextLengthW(heditR) + 1) != NULL) {
                    num = _wtoi(editword);
                }

                if (0 <= num && num <= 255) {
                    R = num;
                }
                else {
                    MessageBox(hWnd, L"0 ~ 255의 숫자만 입력할 수 있습니다.", L"경고", NULL);
                }
            }
//.....이하 소스코드

위의 소스코드를 해석하면 아래와 같습니다.

  • Edit으로부터 문자열의 형태로 값을 받는다.
  • 값이 0~255 안에 들면 전역 변수에 대입시킨다.
  • 만약 값에 들지 않는다면 메시지 박스로 사용자에게 알려준다.

_wtoi는 유니코드를 정수로 변환하기 위해 지원되는 함수입니다. c표준의 atoi와 동일한 개념

 

이후 정상적으로 적용된 값을 사용자에게 표시해주기 위해서 WM_PAINT를 호출하고 WM_PAINT 부분에 그리기를 처리해 줍시다.

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            hdc = BeginPaint(hWnd, &ps);

            //RGB 표시를 하기 위한 텍스트
            TextOutW(hdc, 10, 45, L"R", 1);
            TextOutW(hdc, 10, 75, L"G", 1);
            TextOutW(hdc, 10, 105, L"B", 1);

            //적용된 값 표시를 하기 위한 텍스트
            wsprintf(editword, L"R : %d", R);
            TextOutW(hdc, 100, 45, editword, lstrlenW(editword));
            wsprintf(editword, L"G : %d", G);
            TextOutW(hdc, 100, 75, editword, lstrlenW(editword));
            wsprintf(editword, L"B : %d", B);
            TextOutW(hdc, 100, 105, editword, lstrlenW(editword));

            EndPaint(hWnd, &ps);
        }
        break;

 

이후 각각의 식별자에 대한 처리도 마무리해주도록 합시다.

switch (HIWORD(wParam))
        {
        case EN_CHANGE:

            //R 처리
            if (LOWORD(wParam) == 1) {

                int num = 0;
                if (GetWindowTextW(heditR, editword, GetWindowTextLengthW(heditR) + 1) != NULL) {
                    num = _wtoi(editword);
                }

                if (0 <= num && num <= 255) {
                    R = num;
                }
                else {
                    MessageBox(hWnd, L"0 ~ 255의 숫자만 입력할 수 있습니다.", L"경고", NULL);
                }
                InvalidateRect(hWnd, NULL, TRUE);
            }

            //G 처리
            if (LOWORD(wParam) == 2) {
                GetWindowTextW(heditG, editword, GetWindowTextLengthW(heditG) + 1);
                int num = _wtoi(editword);

                if (0 <= num && num <= 255) {
                    G = num;
                }
                else {
                    MessageBox(hWnd, L"0 ~ 255의 숫자만 입력할 수 있습니다.", L"경고", NULL);
                }
                InvalidateRect(hWnd, NULL, TRUE);
            }

            //B 처리
            if (LOWORD(wParam) == 3) {
                GetWindowTextW(heditB, editword, GetWindowTextLengthW(heditB) + 1);
                int num = _wtoi(editword);

                if (0 <= num && num <= 255) {
                    B = num;
                }
                else {
                    MessageBox(hWnd, L"0 ~ 255의 숫자만 입력할 수 있습니다.", L"경고", NULL);
                }
                InvalidateRect(hWnd, NULL, TRUE);
            }

            break;
        }

이제 프로그램을 실행해 보면 다음과 같은 결과를 동작이 가능해집니다.

3. 값을 이용해서 색칠하기

Edit 컨트롤을 이용해서 다양한 값을 받고 이동하여 저장하는 부분까지는 완료했습니다.

이제 이 값을 가지고 색을 칠하기만 하면 됩니다. 

WM_PAINT에 하나의 사각형을 그리고 색을 칠해주기만 하면 됩니다.

            //색 칠하기
            HBRUSH newBrush = CreateSolidBrush(RGB(R, G, B));
            HBRUSH OldBrush = (HBRUSH)SelectObject(hdc, newBrush);
            Rectangle(hdc, 200, 50, 500, 400);
            SelectObject(hdc,OldBrush);
            DeleteObject(newBrush);
            EndPaint(hWnd, &ps);

 

4. 결과 물 및 전체 소스코드

이제 프로그램을 구동해보면 RGB의 값을 입력받아 사각형의 색상을 변경할 수 있습니다.

향후 글에서는 이 프로그램을 가지고 다양한 기능을 추가해 보도록 하겠습니다.


#include <windows.h>

#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include "resource.h"


HINSTANCE hInst;
HWND heditR, heditG, heditB;
int R, G, B;

// 이 코드 모듈에 포함된 함수의 선언을 전달합니다:
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    //윈도우 창 구조체 정의 및 적용
    WNDCLASSEXW wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = NULL;
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
    wcex.lpszClassName = L"ColorTable";
    wcex.hIconSm = NULL;
    RegisterClassExW(&wcex);
    //적용한 윈도우 생성 및 업데이트
    hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.
    HWND hWnd = CreateWindowW(L"ColorTable", L"ColorTable", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
    if (!hWnd)
    {
        return FALSE;
    }
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    MSG msg;
    // 기본 메시지 루프입니다:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
    }
    return (int) msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    WCHAR editword[10];
    switch (message)
    {
    case WM_CREATE:
        heditR = CreateWindowW(L"edit", NULL, WS_BORDER | WS_CHILD | WS_VISIBLE | ES_RIGHT | ES_NUMBER, 30, 40, 50, 25, hWnd, (HMENU)1, hInst, NULL);
        heditG = CreateWindowW(L"edit", NULL, WS_BORDER | WS_CHILD | WS_VISIBLE | ES_RIGHT | ES_NUMBER, 30, 70, 50, 25, hWnd, (HMENU)2, hInst, NULL);
        heditB = CreateWindowW(L"edit", NULL, WS_BORDER | WS_CHILD | WS_VISIBLE | ES_RIGHT | ES_NUMBER, 30, 100, 50, 25, hWnd, (HMENU)3, hInst, NULL);
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            hdc = BeginPaint(hWnd, &ps);

            //RGB 표시를 하기 위한 텍스트
            TextOutW(hdc, 10, 45, L"R", 1);
            TextOutW(hdc, 10, 75, L"G", 1);
            TextOutW(hdc, 10, 105, L"B", 1);

            //적용된 값 표시를 하기 위한 텍스트
            wsprintf(editword, L"R : %d", R);
            TextOutW(hdc, 100, 45, editword, lstrlenW(editword));
            wsprintf(editword, L"G : %d", G);
            TextOutW(hdc, 100, 75, editword, lstrlenW(editword));
            wsprintf(editword, L"B : %d", B);
            TextOutW(hdc, 100, 105, editword, lstrlenW(editword));

            //색 칠하기
            HBRUSH newBrush = CreateSolidBrush(RGB(R, G, B));
            HBRUSH OldBrush = (HBRUSH)SelectObject(hdc, newBrush);
            Rectangle(hdc, 200, 50, 500, 400);
            SelectObject(hdc,OldBrush);
            DeleteObject(newBrush);
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_COMMAND:

        switch (HIWORD(wParam))
        {
        case EN_CHANGE:

            //R 처리
            if (LOWORD(wParam) == 1) {

                int num = 0;
                if (GetWindowTextW(heditR, editword, GetWindowTextLengthW(heditR) + 1) != NULL) {
                    num = _wtoi(editword);
                }

                if (0 <= num && num <= 255) {
                    R = num;
                }
                else {
                    MessageBox(hWnd, L"0 ~ 255의 숫자만 입력할 수 있습니다.", L"경고", NULL);
                }
                InvalidateRect(hWnd, NULL, TRUE);
            }

            //G 처리
            if (LOWORD(wParam) == 2) {
                GetWindowTextW(heditG, editword, GetWindowTextLengthW(heditG) + 1);
                int num = _wtoi(editword);

                if (0 <= num && num <= 255) {
                    G = num;
                }
                else {
                    MessageBox(hWnd, L"0 ~ 255의 숫자만 입력할 수 있습니다.", L"경고", NULL);
                }
                InvalidateRect(hWnd, NULL, TRUE);
            }

            //B 처리
            if (LOWORD(wParam) == 3) {
                GetWindowTextW(heditB, editword, GetWindowTextLengthW(heditB) + 1);
                int num = _wtoi(editword);

                if (0 <= num && num <= 255) {
                    B = num;
                }
                else {
                    MessageBox(hWnd, L"0 ~ 255의 숫자만 입력할 수 있습니다.", L"경고", NULL);
                }
                InvalidateRect(hWnd, NULL, TRUE);
            }

            break;
        }
        break;
    case WM_SETTEXT:

    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

 

후기

결과물을 보니 중복된 함수도 많고 주석처리도 안되어 있어, 다음 글에서는 간단한 주석처리화 함수화를 해보겠습니다.

728x90
반응형

Commnet

G91개발일지

Gon91(지구일)

91년생 공학엔지니어의 개발일지

TODAY :

YESTER DAY :

TOTAL :