자료 저장소

# 메시지 통신

메시지는 주로 사용자에 의해 발생되지만 프로그램 내부에서 윈도우간의 통신을 위해 의도적으로 다른 윈도우
에게 메시지를 보낼 수도 있다. 이 때는 다음 두 함수를 사용한다.


BOOL PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam); 
LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);
PostMessage함수는 Msg 인수로 지정된 메시지를 hWnd 윈도우에 메시지 큐에 집어넣어 윈도우 프로시저에서
이 메시지를 처리하도록 한다. 메시지를 큐에 넣기만 하고 바로 리턴하므로 붙인 후 즉시 다른 작업을 할 수 있지
만 큐에 대기하고 있는 다른 메시지가 있으면 뒤에 붙인 메시지는 곧바로 처리되지 않는다.
큐에 붙여진 메시지는 GetMessage에 의해 읽혀지고 DispatchMessage에 의해 윈도우 프로시저로 보내져 처리
될 것이다.

급하게 처리할 필요가 없는 메시지는 PostMessage 함수로 큐에 붙인다. 이 함수로 붙여진 메시지는 처리 시점을
예측할 수 없기 때문에 지역 포인터를 사용해서는 안된다. 메시지를 큐에 붙인 후 성공하면 TRUE를 리턴하며
실패하면 FALSE를 리턴한다.

PostMessage의 첫 번째 인수인 hWnd는 이 메시지를 받을 윈도우의 핸들인데 이 값은 아주 특수하게 NULL일
수도 있다. 즉, 메시지를 받는 대상 윈도우가 없는 메시지를 큐에 붙일 수도 있는데 이런 메시지는 특정 윈도우에게
신호를 보내기 위한 것이 아니라 응용 프로그램 전반에 걸친 작업 지시를 보낼 때 사용된다. 대상 윈도우가 없기
때문에 이 메시지는 윈도우 프로시저가 처리할 수 없으며 반드시 메시지 루프에서 직접 처리해야 한다.

PostMessage 함수가 메시지를 붙여넣는 큐의 대상은 윈도우를 위한 큐가 아니라 스레드를 위한 큐이기 때문에
GetMessage로 메시지를 꺼낸후 메시지 ID(WM_SOME)를 비교해 메시지 검사를 해야 한다.

다른 스레드의 메시지 큐에 메시지를 붙일 때는 다음 함수가 사용된다.

BOOL PostThreadMessage(DWORD idThread,UINT Msg,WPARAM wParam,LPARAM lParam); 
이 메시지를 받는 스레드는 반드시 스레드 큐를 가지고 있어야 하며 큐를 가지지 않은 작업 스레드는 메시지를
받지 못한다.

SendMessage는 메시지를 큐에 넣는 것이 아니라 곧바로 윈도우 프로시저로 보내 즉각 처리 하도록 하며
메시지가 완전히 처리되기 전에는 리턴하지 않는다.

SendMessage는 당장 어떤 일을 하라는 명령이며 PostMessage는 한가해질 때 어떤일을 하라는 신호이다.


# 메시지 데드락

SendMessage 함수는 메시지를 받은 윈도우 프로시저가 리턴하기 전에는 리턴하지 않는다.
SendMessage 호출은 곧 서브루틴 호출과 대등하며 실제로 WndProc의 case 하나를 호출하는 것과 같다.
만약 SendMessage를 받은 대상이 어떠한 이유로 10초 후에 반환을 하게 되었다면 메시지를 보낸 대상은
10초간 데드락 상태에 빠지게 되어 어떠한 것도 할 수 없는 무능 상태가 되어버린다.

이 상황을 해결하는 방법으로는 메시지가 다른 스레드로 부터 전달되었을 때는 즉시 리턴한 후 다음 작업을
계속 하는 것이다. 메시지가 다른 스레드로부터 왔는지는 InSendMessage 함수로 조사하여 다른 스레드로부터
온 것이면 TRUE를 리턴한다.

case WM_USER+1: 
if(InSendMessage()) // 다른 스레드로부터 온것이라면
{
ReplyMessage(TRUE);
// BOOL ReplyMessage(LRESULT lResult) 인자는 SendMessage로 리턴될 값

...
}
간단한 예로 보내온 메시지를 바로 리턴하고 다음 코드를 실행 하도록 하는 것이다.

다른 방법으로는
BOOL SendNotifyMessage(DWORD idThread,UINT Msg,WPARAM wParam,LPARAM lParam);
LRESULT SendMessageTimeout(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam,
UINT fuFlags,UINT uTimeout,PDWORD_PTR lpdwResult);
SendNotifyMessage는 hWnd가 다른 스레드의 윈도우일 경우는 대기를 하지않고 즉시 리턴한다.
SendMessageTimeout 함수는 지정한 경과 시간 이상 지나면 처리여부에 상관없이 즉시 리턴한다.


# 메시지 콜백

SendMessage의 블록 특성은 문제가 없더라도 불편한면이 있는데. 이때는 SendMessageCallback 함수를 호출하여
윈도우로 메시지를 보낸 후 즉시 리턴하되 콜백함수를 등록해 놓고 메시지 처리가 끝나면 콜백함수를 호출하도록
시스템에게 부탁한다.

BOOL SendMessageCallback(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam,SENDASYNCPROC
lpCallBack,ULONG_PTR dwData);

VOID CALLBACK SendAsyncProc(HWND hWnd,UINT uMsg,ULONG_PTR dwData,LRESULT lResult);
즉시 리턴하므로 메시지를 보내는 스레드는 곧바로 다른 작업을 할 수 있으며 메시지 처리가 끝난 후 콜백함수가
호출되므로 처리 후의 시점도 정확하게 파악할 수 있다는 장점이 있다.


# 브로드 캐스팅

브로드캐스팅(BroadCasting)이란 복수 개의 수신자에게 한꺼번에 메시지를 보내는 동작을 말하는데 말 그대로
실행중인 모든 윈도우에게 메시지로 방송을 하는 것이다. 이때 수신 대상 윈도우의 종류는 응용프로그램은 물론,
시스템 디바이스 드라이버, 네트워크 드라이버, 기타 설치 가능한 디바이스 드라이버도 포함된다.
long BroadcastSystemMessage(DWORD dwFlags,LPDWORD lpdwRecipients,UINT uiMessage,WPARAM 
wParam,LPARAM lParam);
대상 윈도우 핸들이 없으며 대신 수신자의 종류를 지정할 수 있는 인수가 있고 옵션을 지정할 수 있는 플래그가
있다. 가능한 수신자의 종류는 다음과 같으며 복수 개를 지정할 수도 있다.

BSM_ALLCOMPONENTS : 모든 시스템의 요소
BSM_ALLDESKTOPS : 모든 데스크탑
BSM_APPLICATIONS : 응용 프로그램
BSM_INSTALLABLEDRIVERS :  설치 가능한 드라이버
BSM_NETDRIVER :  네트워크 드라이버
BSM_VXDS : 시스템 디바이스 드라이버

복수 개의 수신자가 지정되어 있을 경우
시스템 디바이스 드라이버 > 네트워크 드라이버 > 설치 가능한 드라이버 >응용 프로그램 순으로 메시지를 받는다.




댓글 로드 중…

최근에 게시된 글