본문 바로가기
코스웨어/15년 스마트컨트롤러

2015-11-16 Win32 API 개인업무일지 - 천정호

by 알 수 없는 사용자 2015. 11. 16.
728x90
반응형

Translate Message

Windows의 메세지 전송과정은 원형큐의 형식으로 동작이 되며 Windows가 보낸 메세지가 너무 많아서 Application이 모두 처리하지 못하는 경우에는 가장 최근의 메시지를 우선적으로 받는다.

원형 큐의 메세지를 받는 부분은 아래의 while()문 코드이며 while()문의 조건에 있는 GetMessage 함수로 메세지를 받아 처리 한다.

GetMessage 함수는 메세지 큐에서 메세지를 꺼내온 후 TranslateMessage 함수로 전달되는데 TranslateMessage 함수는 전달된 메세지가 WM_KEYDOWN과 같은 메세지인지 먼저 검사를 하고 WM_KEYDOWN과 같은 메세지면 눌려진 키가 문자키인지를 wParam을 통하여 검사를 한다. TranslateMessage의 조건과 맞다면 WM_CHAR 메세지를 만들어 메세지 큐에 붙이고 만약 조건에 맞지 않으면 DispatchMessage 함수를 통하여 WndProc으로 보내진다.

while (GetMessage(&Message, 0, 0, 0)) {

// 메세지를 가장 먼저 처리하는 함수

TranslateMessage(&Message);

// TranslateMessage가 처리 못한 메세지를 DispatchMessage 함수가 처리한다.

DispatchMessage(&Message);

}



Mouse

마우스 움직임의 좌표값은 lParam에 저저장이 되며 그 구성은 상위비트로 Y좌표, 하위비트로 X좌표가 구성되어진다. 좌표값을 검출해 내기 위해서는 HOWORD, LOWORD 매크로 함수를 사용하여 검출해낸다.



마우스의 움직임에 발생되는 메세지는 WM_MOUSEMOVE 이며 아래의 표는 마우스의 클릭에 대한 이벤트 메세지 표이다.

버튼누름놓음더블클릭
좌측WM_LBUTTONDOWNWM_LBUTTONUPWM_LBUTTONDBLCLK
우측WM_RBUTTONDOWNWM_RBUTTONUPWM_RBUTTONDBLCLK
중앙WM_MBUTTONDOWNWM_MBUTTONUPWM_MBUTTONDBLCLK


마우스 왼쪽 버튼을 클릭하면 WM_LBUTTONDOWN 이벤트 메세지가 생성되어 X, Y축의 좌표값이 해당 변수에 대입 후 bnowDraw 변수에 TRUE를 넣어 왼쪽 버튼이 눌려졌음을 알린다. 그 상태에서 마우스가 움직이면 WM_MOUSEMOVE 메세지로 인하여 움직이는 좌표로 선을 끗는다. 마지막으로 왼쪽 버튼의 클릭이 해제 되었을 경우 bnowDraw 변수에 FALSE를 넣어서 마우스가 움직여도 선이 그어지지 않게한다.


HDC hdc;

static int x;

static int y;

static BOOL bnowDraw = FALSE;


switch (iMessage) {

case WM_LBUTTONDOWN:

// lParam에서 X, Y의 값을 추출

x = LOWORD(lParam);

y = HIWORD(lParam);

bnowDraw = TRUE;

return 0;

// 마우스의 움직임을 감지하여 해당 기능을 수행

case WM_MOUSEMOVE:

// 마우스 왼쪽 버튼을 누른 상태인지를 확인하여 기능을 수행한다.

if (bnowDraw == TRUE) {

hdc = GetDC(hWnd);

MoveToEx(hdc, x, y, NULL);

x = LOWORD(lParam);

y = HIWORD(lParam);

LineTo(hdc, x, y);

ReleaseDC(hWnd, hdc);

}

return 0;

case WM_LBUTTONUP:

bnowDraw = FALSE;

return 0;


// WM_DESTROY 메세지가 오면 종료

case WM_DESTROY:

// 종료 전에 마지막으로 처리할 일이 존재하면 그 일을 마치고 

// PostQuitMessage 함수로 GetMessage에 0을 보내어 종료

PostQuitMessage(0);

return 0;

}




마우스의 움직임에 대한 좌표 표시

HDC hdc;

PAINTSTRUCT ps;


static int x;

static int y;

static WCHAR wcBuf[100];


switch (iMessage) {

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

// 유니코드(2Byte)를 지원 함수는 함수 앞에 l을 붙이고 사용

TextOut(hdc, 80, 120, wcBuf, lstrlen(wcBuf));

EndPaint(hWnd, &ps);

return 0;


// 마우스의 움직임을 감지하여 해당 기능을 수행

case WM_MOUSEMOVE:

x = LOWORD(lParam);

y = HIWORD(lParam);


// 유니코드(2Byte)를 메모리에 저장할 수 있는 함수

wsprintf(wcBuf, L"X : %d   Y : %d", x, y);


InvalidateRect(hWnd, NULL, FALSE);

return 0;



// WM_DESTROY 메세지가 오면 종료

case WM_DESTROY:

// 종료 전에 마지막으로 처리할 일이 존재하면 그 일을 마치고 

// PostQuitMessage 함수로 GetMessage에 0을 보내어 종료

PostQuitMessage(0);

return 0;

}




Double Click

Win32 API에서 Double Click을 사용하기 위해서는 WindowClass 구조체의 Style 멤버에 CS_DBLCLKS를 추가하여야한다.

// 마지막 CS_DBLCLKS는 Dluble Click을 활성화 시키기 위해서 추가

WndClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;


WinProc 함수의 내용은 Mouse의 내용에 Double Click에 해당하는 메세지인 WM_LBUTTONDBLCLK에 해당하는 내용만 추가하였다.

Double Click시에 그렸던 내용 모두를 지워주기 때문에 Double Click 후에는 페이지가 처음으로 돌아가게된다.

switch (iMessage) {

case WM_LBUTTONDOWN:

x = LOWORD(lParam);

y = HIWORD(lParam);

bnowDraw = TRUE;

return 0;


case WM_MOUSEMOVE:

if (bnowDraw == TRUE) {

hdc = GetDC(hWnd);

MoveToEx(hdc, x, y, NULL);

x = LOWORD(lParam);

y = HIWORD(lParam);

LineTo(hdc, x, y);

ReleaseDC(hWnd, hdc);

}

return 0;


case WM_LBUTTONUP:

bnowDraw = FALSE;

return 0;


case WM_LBUTTONDBLCLK:

InvalidateRect(hWnd, NULL, TRUE);

return 0;


// WM_DESTROY 메세지가 오면 종료

case WM_DESTROY:

// 종료 전에 마지막으로 처리할 일이 존재하면 그 일을 마치고 

// PostQuitMessage 함수로 GetMessage에 0을 보내어 종료

PostQuitMessage(0);

return 0;

}

왼쪽 버튼을 눌린 상태로 그린 그림

Double Click으로 페이지를 비운 상태


Timer

WM_CREAT 메세지는 창이 실행될때 한 번만 수행되는 메세지이며 창에대한 설정을 도와준다.

SetTimer 함수를 통하여 Timer의 번호와 시간의 변화 타이밍등을 설정한다.

WM_TIMER 메세지를 통하여 한 번 지정후에 지속적으로 시간에 관련된 기능을 수행한다.

현재 코드에서는 WM_TIMER를 통하여 시스템 시간을 받아와 문자열로 변환 후 배열에 저장한다.

배열에 저장된 시간은 지속적으로 화면에 나타나면서 변화되는 시간은 표시한다.

마지막으로 WM_CREATE에서 설정하였던 Timer번호를 KillTimer를 이용하여 해제시킨다.

HDC hdc;

PAINTSTRUCT ps;

SYSTEMTIME Sys_Time;

static TCHAR sTime[128] = { 0, };



switch (iMessage) {

case WM_CREATE:

// 첫 번째 인자 - Window Handler

// 두 번째 인자 - Timer의 번호

// 세 번째 인자 - 1 / 1000초 단위로 설정값

SetTimer(hWnd, 1, 1000, NULL);

return 0;



case WM_TIMER:

GetLocalTime(&Sys_Time);

wsprintf(sTime

, TEXT("지금 시간은 %d:%d:%d입니다")

, Sys_Time.wHour

, Sys_Time.wMinute

, Sys_Time.wSecond);

InvalidateRect(hWnd, NULL, TRUE);

return 0;


case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

TextOut(hdc, 40, 120, sTime, lstrlen(sTime));

EndPaint(hWnd, &ps);

return 0;


// WM_DESTROY 메세지가 오면 종료

case WM_DESTROY:

KillTimer(hWnd, 1);

// 종료 전에 마지막으로 처리할 일이 존재하면 그 일을 마치고 

// PostQuitMessage 함수로 GetMessage에 0을 보내어 종료

PostQuitMessage(0);

return 0;

}


SendMessage - Two Timer

두 개의 타이머를 설정하고 하나의 타이며는 시간을 하나의 타이며는 일정시간이 지날때마다 비프음을 출력하여 동시에 두 개의 타이머를 사용하는것이다.

 SetTimer를 통하여 각각 다른번호의 Timer를 설정하고 사용자의 설정에 맞게 시간 타이밍을 지정한다. 기본 설정은 1/1000초로 설정이 되어있다.

SemdMessage를 통하여 타이머 메세지인 WM_TIMER와 wParam, lParam을 통하여 설정이 변경된 타이머를 원형큐에 등록시킬수 있다.

HDC hdc;

PAINTSTRUCT ps;

SYSTEMTIME Sys_Time;

RECT rt = { 40, 120, 100, 50 };

static TCHAR sTime[128] = { 0, };



switch (iMessage) {

case WM_CREATE:

// 첫 번째 인자 - Window Handler

// 두 번째 인자 - Timer의 번호

// 세 번째 인자 - 1 / 1000초 단위로 설정값

SetTimer(hWnd, 1, 1000, NULL);

SetTimer(hWnd, 2, 5000, NULL);

// 첫 번째 인자 - Window Handler

// 두 번째 인자 - 메세지의 타입, 종류

// 세 번째 인자 - wParam

// 네 번째 인자 - lParam

SendMessage(hWnd, WM_TIMER, 1, 0);

return 0;



case WM_TIMER:

switch (wParam) {

case 1:

GetLocalTime(&Sys_Time);

wsprintf(sTime

, TEXT("지금 시간은 %d:%d:%d입니다")

, Sys_Time.wHour

, Sys_Time.wMinute

, Sys_Time.wSecond);

InvalidateRect(hWnd, &rt, TRUE);

break;


case 2:

MessageBeep(MB_OK);

break;

}

return 0;


case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

TextOut(hdc, 40, 120, sTime, lstrlen(sTime));

EndPaint(hWnd, &ps);

return 0;


// WM_DESTROY 메세지가 오면 종료

case WM_DESTROY:

KillTimer(hWnd, 1);

KillTimer(hWnd, 2);

// 종료 전에 마지막으로 처리할 일이 존재하면 그 일을 마치고 

// PostQuitMessage 함수로 GetMessage에 0을 보내어 종료

PostQuitMessage(0);

return 0;

}


CALLBACK

WM_TIMER 메세지의 경우에는 이전에 다른 메세지가 있을경우에는 순서에 밀려서 늦게 실행이 되지만 CALLBACK 함수의 경우에는 정확한시간에 빠르게 호출이 가능하다.

정확도를 요하는 프로그램의 경우에는 WM_TIMER 보다 CALLBACK 함수를 사용하는것이 더 낫다.

void CALLBACK TimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime) {

HDC hdc;

int i;

hdc = GetDC(hWnd);


for (i = 0; i < 100; i++) {

SetPixel(hdc, rand() % 500, rand() % 400,

RGB(rand() % 256, rand() % 256, rand() % 256));

}

ReleaseDC(hWnd, hdc);

}


LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)

{

switch (iMessage) {

case WM_CREATE:

SetTimer(hWnd, 1, 100, (TIMERPROC)TimerProc);

return 0;

case WM_DESTROY:

KillTimer(hWnd, 1);

PostQuitMessage(0);

return 0;

}

return(DefWindowProc(hWnd, iMessage, wParam, lParam));


생성 및 파괴

메시지 중 가장 간단한 메시지는 윈도우가 생성될 때 보내지는 WM_CREATE와 윈도우가 파괴될 때 보내지는 WM_DESTROY 두가지가 있다. WM_CREATE 메시지는 Windows가 생성될 때 보내지므로 각종 초기화를 하기에 적합한 장소이며 반대로 WM_DESTROY 메시지는 종료처리를 하기에 적합하다.  


작업영역

창에서 작업영역과 비작업영역이 존재하는데 여기서 작업영역이란 Windows가 자동으로 관리해주지 않는 영역 즉 프로그래머가 프로그램의 대상이 되는 영역을 이야기한다.

이런 작업영역을 이용하여 화면의 정가운데로 문자열을 출력하는 프로그램이다.

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)

{

HDC hdc;

PAINTSTRUCT ps;

static RECT rt;


switch (iMessage) {

case WM_CREATE :

GetClientRect(hWnd, &rt);

return 0;


case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

SetTextAlign(hdc, TA_CENTER);

TextOut(hdc, rt.right / 2, rt.bottom / 2, L"Center String", 13);

EndPaint(hWnd, &ps);

return 0;


case WM_DESTROY:

PostQuitMessage(0);

return 0;

}

return(DefWindowProc(hWnd, iMessage, wParam, lParam));

}






WM_SIZE

문자열을 계속 작업 영역 중앙에 두고 싶으면 윈도우의 크기가 변경될 때마다 다시 출력해 주어야 하는데 이때 사용되는 메시지가 WM_SIZE 메시지이다. 


WM_SIZE의 Flag

플레그
SIZE_MAXHIDE다른 윈도우가 최대화되어 이 윈도우가 가려졌다.
SIZE_MAXIMIZED최대화되었다.
SIZE_MAXSHOW다른 윈도우가 원래 크기로 복구되어 이 윈도우가 드러났다.
SIZE_MINIMIZED최소화되었다.
SIZE_RESTORED크기가 변경되었다.

 


창의 사이즈가 변경될때마다 다시 창의 크기를 측정하여 문자열을 정중앙으로 정렬한다.


LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)

{

HDC hdc;

PAINTSTRUCT ps;

static RECT rt;

switch (iMessage) {

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

SetTextAlign(hdc, TA_CENTER);

TextOut(hdc, rt.right / 2, rt.bottom / 2, "Center String", 13);

EndPaint(hWnd, &ps);

return 0;

case WM_SIZE:

GetClientRect(hWnd, &rt);

InvalidateRect(hWnd, NULL, TRUE);

return 0;

case WM_DESTROY:

PostQuitMessage(0);

return 0;

}

return(DefWindowProc(hWnd, iMessage, wParam, lParam));

}


리소스 작성

새로운 프로젝트에 기본코드 작성하고 저장 후 리소스 파일 생성




생성된 리소스 파일에 메뉴 입력





#include "resource.h"


// 메뉴 이름


WndClass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);


메뉴에서 들어오는 것은 모두 WM_COMMAND로 메세지가 들어오며 메뉴에 해당하는 고유번호는 wParam으로 값이 들어온다.

switch (iMessage) {


case WM_COMMAND:

switch (LOWORD(wParam)) {

case ID_FILE_NEW:

MessageBox(hWnd, L"New Menu.", L"New", MB_OK);

break;

case ID_FILE_LOAD:

MessageBox(hWnd, L"Load Menu", L"Load", MB_OK);

break;

case ID_FILE_SAVE:

MessageBox(hWnd, L"Save Menu", L"Save", MB_OK);

break;

case ID_FILE_EXIT:

PostQuitMessage(0);

break;

}

return 0;


// WM_DESTROY 메세지가 오면 종료

case WM_DESTROY:

// 종료 전에 마지막으로 처리할 일이 존재하면 그 일을 마치고 

// PostQuitMessage 함수로 GetMessage에 0을 보내어 종료

PostQuitMessage(0);

return 0;

}


728x90