본문 바로가기
공부

Win32 API 프로시저 / 메시지 / 메시지 루프 / 메시지 큐

by MY블로그 2022. 12. 3.

C++ 기초가 끝나고 DX 수업 진행하기전 Window API 에 관하여 조사를 진행한다.

  • 윈도우 프로시저
  • 윈도우 메시지
  • 메시지 루프
  • 메시지 큐

●윈도우 프로시저 ( Window Procedure, Wnd Proc 함수 )

WinMain 함수에서 전달한 메시지를 처리하는 윈도우 프로시저(WinProc 함수) 이다.

WinMain 함수에서 메시지 루프를 통해서 발생한 메시지를 큐에 저장한다.

해당 메시지를 처리하기 위해서 메시지 처리 전용 함수로 전달이 되어야 하는데

이때 메시지 처리 전용 함수가 윈도우 프로시저 이다.

윈도우 프로시저는 WinMain 함수와는 별도로 WndProc 함수의 형태로 존재한다.

윈도우 프로시저의 특징
  1. WinMain에서 호출하는 것이 아닌, 윈도우에 의해서 호출된다.
  2. WinMain내에 존재하는 메시지 루프는 메시지 처리 전용 함수로 전달하는 역할만 한다.
  3. 윈도우 프로시저는 메시지가 들어오면 호출되고 메시지에 맞게 내용을 처리한다.
  4. 콜백 함수(CallBack Function)이다.

WinMain 함수는 발생한 메시지를 전달하는 역할만 하는 것을 알 수 있다.

메시지의 처리에 관해서는 일절 관여하지않으며 전달하는 역할만을 하기 때문에

윈도우 프로시저를 WinMain 이 호출 하는 것이라고 오해할 수 있다.(윈도우가 호출 해야한다)

이것을 *콜백함수라한다.

 

*콜백함수 : 사용자가 호출하는 것이 아닌 운영체제에 의해 호출되는 함수

 

Win32 API에서 윈도우 프로시저는 WndProc() 함수를 통하여 제공 되고 있다.(아래 이미지 참조)

위처럼 반환형은 LRESULT 인 콜백함수로 4개의 인수를 가지고 있다.

WndProc 함수의 인수
  • HWND hWnd : 메시지를 받을 윈도우의 핸들
  • UINT iMessage : 어떤 메시지를 받았는지
  • WPARAM wParam : 메시지에 따른 부가 정보
  • LPARAM lParam : 메시지에 따른 부가 정보

어떤 윈도우에서 처리해야 할 메시지인지, 어떤 메시지를 처리해야 하는지, 메시지에 따른 부가 정보에 대한 내용을 담고 있다. 부가정보란 마우스가 이동하는 이벤트가 발생해서 메시지가 넘어오면 마우스의 X,Y 좌표 정보와 같은 것들이다.

 

함수의 본문을 보면 switch 문을 통하여 각 메시지별로 처리하는 내용을 정의해 놓은 것을 볼 수 있다.

 

WM_CREATE, WM_DESTROY는 각 메시지 이다.

  • WM_CREATE : 윈도우 생성시 발생하는 메시지
  • WM_DESTROY : 응용프로그램의 오른쪽 위의 X 표시(종료) 를 누르게되면 WM_DESTROY 메시지 발생후 종료됨

윈도우 핸들에 대한 정보는 있으나 프로그램에 대한 정보가 인수로 없는 이유는

전역 변수(g_hlnst)를 선언하여 프로그램 정보를 어느 함수에서도 접근할 수 있기 때문이다.

 

switch 문으로 메시지를 처리하기 때문에 각 케이스(메시지)별로 어떻게 처리해야할지 한번에 보기 쉽다.

 

메시지 처리 순서도

위의 이미지는 메시지 처리 순서도 (Flow Chart) 이다.

메시지가 발생하면 메시지 큐에 넣고 윈도우 프로시저로 전달하여 처리한다.

TranslateMessage는 꼭 필요한 과정은 아니다. 해당 함수는 문자 정보가 입력 되었을 때 어떤 문자인지 해석하여 전달하는 과정으로, 문자가 입력되는 상황이 아니라면 꼭 필요하지 않다.

 


 

●윈도우 메시지 ( Window Message )

시스템은 메시지 형식의 창 프로시저에 입력을 전달하며, 메시지는 시스템과 애플리케이션 모두에서 생성이 된다.

시스템은 사용자가 입력하거나 마우스를 이동하거나 스크롤 막대와 같은 컨트롤을 클릭할 때와 같이

각 입력 이벤트에서 메시지를 생성한다.

 

또한 시스템은 애플리케이션이 시스템 글꼴 리소스 풀을 변경하거나 창 중 하나의 크기를 조정하는 경우와 같이

애플리케이션에서 가져온 시스템의 변경에 대한 응답으로 메시지를 생성한다.

 

애플리 케이션은 작업을 수행하거나 다른 애플리 케이션의 창과 통신하도록 자체 창을 지시하는 메시지를 생성 할 수 있다.

시스템은 창 핸들, 메시지 식별자 및 메시지 매개 변수라는 두개의 값의 네 가지 매개 변수 집합이 있는 창 프로시저에 메시지를 보낸다.

창 핸들은 메시지가 의도된 창을 식별하며, 시스템에서 이를 사용하여 메시지를 수신해야 하는 창 프로시저를 결정한다.

 

메시지 식별자는 메시지의 용도를 식별하는 명명된 상수 이다.

창 프로시저가 메시지를 받으면 메시지 식별자를 사용하여 메시지를 처리하는 방법을 결정한다.

예를 들어 메시지 식별자 WM_PAINT 창의 클라이언트 영역이 변경 되었으며

다시 그려야 한다는 것을 창 프로시저에 알린다.

 

메시지 매개 변수는 메시지를 처리할 때 창 프로시저에서 사용되는 데이터 또는 데이터의 위치를 지정한다.

메시지 매개 변수의 의미와 값은 메시지에 따라 달라진다.

메시지 매개 변수에는 정수, 압축된 비트  플래그, 추가 데이터가 포함된 구조체에 대한 포인터 등이 포함 될 수 있다.

메시지가 메시지 매개 변수를 사용하지 않는 경우 일반적으로 NULL로 설정된다.

창 프로시저는 메시지 식별자를 확인하여 메시지 매개 변수를 해석하는 방법을 결정 해야 한다.

 


 

●메시지 루프 ( Message Loop )

윈도우즈는 프로그램의 실행 순서가 명확하게 정해져 있지 않으며 상황에 따라 실행 순서가 달라진다.

이때 실행 순서 란 어떤 메시지가 발생했는가 를 의미한다.

 

메시지는 사용자나 시스템의 내부적인 동작에 의해 발생된 일체의 변화에 대한 정보이다.

예를 들어 사용 자가 마우스 버튼을 클릭 혹은 키보드를 누르거나 윈도우의 크기가 변화하는 정보들이 메시지 이다.

 

메시지를 받은 프로그램은 메시지가 어떤 정보를 담고 있는지를 분석하여 무슨 동작을 할 것인가를 결정한다.

즉, 정해진 순서가아닌 주어진 메시지에 대한 반응을 정의하는 방식으로 프로그램이 실행된다.

 

윈도우즈 프로그램에서 메시지를 처리하는 부분을 바로 메시지 루프 ( Message Loop ) 라고 한다.

보통 WinMain 함수의 끝에 전재하며 메인 윈도를 만든 직후 WinMain은 메시지 루프를 실행 한다. (아래코드참조)

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

메시지 루프는 세개의 함수 호출로 이루어져 있고 반복문(while)로 싸여져 있어 무한히 반복되는 구조이다.

메시지 루프는 윈도우즈의 멀티태스킹을 지원하는 굉장히 중요한 역할을 한다.

 

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

메시지 큐에서 메시지를 읽어들인다.

메시지 큐()는 시스템이나 사용자로부터 발생된 메시지가 잠시 대기하는 일종의 임시 저장 영역이다.

읽어들인 메시지는 첫 번째 인수가 지정하는 MSG 구조체에 저장된다.

이 함수는 읽어들인 메시지가 프로그램을 종료하라는 WM_QUIT 일 경우 FALSE 를 리턴하며

그외의 메시지라면 TRUE 를 리턴한다.

따라서 WM_QUIT 메시지가 읽혀질 때까지 프로그램이 종료될 때까지 전체 while 루프가 반복된다.

나머지 3개의 인수는 읽어들일 메시지의 범위를 지정하는데 사용된다.

BOOL TranslateMessage(CONST MSG *lpMsg);

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

윈도우즈는 키보드의 어떤 키가 눌러지거나 떨어졌을 때 키보드 메시지를 발생시키는데

TranslateMessage 함수는 키보드의 눌림 (WM_KEYDOWN) 메시지가 발생할 때

문자가 입력되었다는 메시지(WM_CHAR)를 만드는 역할을 한다.

LONG DispatchMessage(CONST MSG *lpmsg);

메시지 큐에서 꺼낸 메시지를 윈도우의 메시지 처리 함수(WndProc)로 전달한다.

이 함수에 의해 메시지가 윈도우로 전달되며 프로그램(윈도우 프로시저)에서는 전달된 메시지를 점검하고

다음으로 동작을 결정한다.

이 함수가 메시지를 전달하면 다시 루프의 선두로 돌아가 다음 메시지를 기다린다.

    typedef struct tagMSG
    {
        HWND	hwnd; // 메시지를 받을 윈도우
        UINT	message; // 어떤 종류의 메시지인가를 나타낸다 (중요*)
        WPARAM	wParam; // 전달된 메시지에 대한 부가적 정보를 갖는다. (의미는 메시지 별로 다름)
        LPARAM	IParam; // 전달된 메시지에 대한 부가적 정보를 갖는다. (의미는 메시지 별로 다름)
        DWORD	time; // 메시지가 발생한 시간
        POINT	pt; // 메시지가 발생한 순간 마우스의 위치(좌표)
    }MSG;

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

wParam, lParam 은 메시지에 대한 부가적인 정보를 가지게되며 메시지 별로 다른 의미를 갖는다.

GetMessage 함수는 읽은 메시지를 MSG 형의 구조체에 대입하며

이 구조체는 DispatchMessage 함수에 의해 응용프로그램의 메시지 처리 함수(WndProc) 으로 전달 된다.

 

메시지는 실제로 하나의 정수값으로 표현되는 데 종류가 무척 많아 메시지 번호를 암기하여 사용 할 수 없으므로

windows.h 에 메시지별 매크로 상수를 정의해 두었으며 접두어 WM_ 으로 시작된다.

 

  • WM_QUIT - 프로그램을 종료할 때 발생하는 메시지
  • WM_LBUTTONDOWN - 마우스 좌클릭시 발생
  • WM_KEYDOWN - 키보드의 키가 눌렸을시 발생
  • WM_CAHR - 키보드를통하여 문자 입력시 발생
  • WM_PAINT - 화면을 다시 그려야 할 필요가 있을 때 발생
  • WM_CREATE - 윈도우가 처음 만들어 질 때 발생
  • WM_DESTROY - 윈도우가 메모리에서 파괴 될 때 발생

메시지 루프가 종료되면 프로그램은 마지막으로 Message.wParam 을 리턴하고 종료한다.

이 값은 WM_QUIT 메시지로부터 전달된 탈출 코드 (exit code) 이며 이 프로그램을 실행시킨 운영체제로 리턴 된다.

도스에서 사용하는 탈출 코드와 동일한 의미를 가지며 사용되는 경우가 거의 없다.

 


 

●메시지 큐 ( Message Queue )

메시지 큐는 프로세서 또는 프로그램 간에 데이터를 교환할 때 사용하는 통신 방법 중 하나이다.

메시지 지향 미들 웨어(Message Oriented Middleware : MOM)를 구현한 시스템을 의미한다.

메시지 지향미들웨어 란 비동기 메시지를 사용하는 으용 프로그램들 사이에서 데이터를 송신&수신 하는 것을 의미 한다.

여기서 메시지란 요청, 응답, 오류 메시지 혹은 단순한 정보 등의 작은 데이터가 될 수 있다.

 

-참고-

이전 데이터 저장 공간중 스택 영역은 LIFO 방식 으로 공부하였다 ( Last In First Out ) 후입선출!

큐는 이와 반대인 FIFO ( First In First Out ) 선입선출!

출처 : https://velog.io/@mmy789/Java-%EC%BB%AC%EB%A0%89%EC%85%98-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-6
출처 : https://tecoble.techcourse.co.kr/post/2021-09-19-message-queue/

이미지를 보면 메시지 큐(QUEUE) 는 메시지를 임시로 저장하는 간단한 버퍼 라고 생각하면 된다.

메시지를 전송 및 수신하기 위해 중간에 메시지 큐를 두는 것이다.(우편을보내기위해 우체국에 우편이 모이는 느낌)

 

메시지 전송 시 생산자(Producer)로 취급되는 컴포넌트가 메시지를 메시지 큐에 추가한다.

해당 메시지는 소비자(Consumer)로 취급되는 또 다른 컴포넌트가 메시지를 검색하고 이를 사용해 어떤 작업을 수행할 때까지 메시지 큐에 저장 된다.

각 메시지는 하나의 소비자에 의해 한 번만 처리될 수 있는데,

이러한 이유로 메시지 큐를 이용하는 방식을 1:1 통신이라 부른다.

메시지 큐를 사용하는 경우

일반적인 (클라이언트-서버) 구조에서는 사용자가 요청을 하면 서버는 그에 대한 처리를 한 후 클라이언트에게 응답 한다.

간단한 서버 구조에서는 굳이 메시지 큐를 사용할 필요가 없다.

이메일 전송

어떤 웹 사이트의 비밀 번호를 잊어 버려서 이메을을 통해 임시 비밀 번호를 받거나,

새로운 회원가입을 위한 인증 코드를 받아본경험을 생각하면 이때 인증메일이 바로바로 오지 않는 경우가 있었다.

이러한경우 메시지 큐를 이용한다면 도움이 될 수 있다.

이메일 전송 서비스는 이메일이 어느 서비스로부터 생산되었는지와는 관계없이 메시지 큐의 메시지를 하나씩 소비하고 그저 메일이 전송 되어야 할 곳으로 전송된다.

만일 메시지 큐에 들어오는 메시지 수가 많아지는 경우 전용 서비스 인스턴스를 더 확장 할 수 있으므로 확장성이 뛰어나다.

이메일 전송 예시 출처 : https://tecoble.techcourse.co.kr/post/2021-09-19-message-queue/

블로그 포스팅

블로그 사용자가 용량이 큰 자료들을 업로드할 경우 블로그 서비스의 읍답 시간을 저해하지 않으면서 사용자들에게 유연성을 제공하는 방법으로, 사용자가 업로드한 모든 이미지를 게시 과정에서 즉각 처리하는 것이 아니라

사후처리하며 최적화 하는 방법이 있다.

사용자 경험에 약간 영향을 미칠 수는 있으나 최적화는 응용 프로그램에서 가장 중요한 것은 아니며 작업을 즉시 수행할 필요도 없다. 메시지 큐는 이러한 상황에서도 사용 될 수 있다.

고용량 자료 업로드 > 저장소에 전송 > 최적화 서비스 메시지 큐에 담는다 > 저장소에서 자료를 가져와 최적화후 자료대체

메시지 큐의 장점
  • 비동기 - 메시지의 저장,전송 에대해 동기화를 즉각진행하지않고 큐에 담아두었다가 차후 처리 할 수 있다.
  • 낮은 결합도 - 생상자 & 소비자 서비스가 독립적이기 때문에 결합도가 낮다.
  • 확장성 - 필요시 원하는 만큼 확장이 가능하다.
  • 탄력성 - 서비스가 다운되더라도 메시지가 큐에 담겨있으므로 서비스 재시작시 메시지 처리를 이어서 할 수 있다.
  • 보장성 - 메시지 큐는 큐에 보관되는 모든 메시지가 결국 소비자 서비스 에게 전달되는 일반적인 보장을 제공 한다.

'공부' 카테고리의 다른 글

비트연산자 & 쉬프트연산자  (0) 2022.12.12
수학 - 삼각 함수 / 삼각비  (0) 2022.12.06
C++ 메모리영역 복습  (1) 2022.12.01
C++ static & const 복습  (0) 2022.12.01
C++ 함수 포인터 / 멤버 함수 포인터  (0) 2022.11.30

댓글