본문

[API]SendMessage / PostMessage

SendMessage => LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
설명 : 메시지를 윈도우에게 보낸다. 해당 윈도우의 윈도우 프로시저를 호출하여 이 메시지가 완전히 처리되기 전에는 리턴하지 않는다. 같은 스레드에 속한 윈도우에게 메시지를 보낼 때는 마치 서브루틴을 호출하는 것과 동일하다. 예를 들어 메인 윈도우가 차일드 윈도우인 리스트 박스에게 LB_ADDSTRING이나 LB_GETCOUNT 등의 메시지를 보내면 리스트 박스는 해당 동작을 수행하는 서브루틴을 호출하고 이 동작이 완료될 때까지 SendMessage는 리턴하지 않는다. 다른 스레드에 속한 윈도우에게 메시지를 보낼 때는 스레드 스위칭이 발생하며 메시지를 받는 스레드가 메시지를 읽는 코드를 실행중이어야 한다. 만약 메시지를 받는 스레드가 메시지 처리에 오랜 시간을 소모한다면 SendMessage를 호출한 스레드는 이 함수가 리턴할 때까지 블록 상태로 남아있게 된다. WM_COPYDATA 등의 특정 메시지는 반드시 SendMessage 함수로만 보내야 하며 PostMessage를 쓸 수 없는 것도 있다.

PostMessage => LRESULT PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
설명 : 메시지를 해당 윈도우의 메시지 큐에 붙이며 즉시 리턴한다. 이렇게 붙여진 메시지는 메시지 루프의 GetMessage 함수에 의해 꺼내져서 DiepatchMessage 함수에 의해 윈도우 프로시저로 보내지며 최종적으로 윈도우 프로시저에 의해 처리된다. SendMessage와는 달리 메시지를 큐에 붙인 후 곧바로 리턴하므로 이 메시지는 곧바로 처리되지 않으며 메시지를 붙인 스레드는 곧바로 다른 작업을 할 수 있다. 메시지를 붙이는 시점과 메시지를 처리하는 시점이 비동기적이기 때문에 PostMessage의 wParam, lParam으로 포인터를 전달하는 것은 의미가 없다. 메시지를 붙일 때 존재하던 값이 메시지를 처리하는 시점에서는 사라질 수 있기 때문이다. 특히 WM_COPYDATA 메시지는 임시적인 데이터를 전역 공유 메모리에 생성한 후 전달하는데 이 메시지는 절대로 PostMessage로 붙일 수 없으며 SendMessage로만 보내야 한다. PostMessage 호출의 아주 특수한 경우로 첫번째 인수가 NULL일 수도 있는데 이 경우는 특정 윈도우에게 메시지를 붙이는 것이 아니라 응용 프로그램 전반에 걸친 작업 지시를 보내는 경우이다. 대상 윈도우가 없기 때문에 이렇게 붙여진 메시지는 윈도우 프로시저까지 전달되지 않으며 메시지 루프에서 직접 처리해야 한다.


인수 설명 :
hWnd : 메시지를 받을 윈도우 핸들. HWND_BROADCAST일 경우 모든 최상위 윈도우에게 보내진다. 숨겨진 윈도우나 사용금지된 윈도우도 포함되며 오버랩드, 팝업 윈도우도 포함된다. 단 차일드 윈도우에게는 보내지지 않는다. (PostMessage => NULL일 경우 PostThreadMessage 함수와 같아지며 특정한 대상 윈도우없이 스레드에게 메시지를 보낸다.)

Msg : 전달할(붙일) 메시지
wParam : 메시지 추가 정보. 메시지에 따라 의미가 달라진다.
lParam : 메시지 추가 정보.


추가 자료 :
두 함수의 인자는 완전히 동일하다. 여기서 Post라는 말은 우리말로 "붙인다"라고 번역되며 "Send"라는 말은 "보낸다"라고 번역된다.
PostMessage 함수는 Msg 인자로 지정된 메시지를 hWnd 윈도우의 메시지 큐에 집어넣어 윈도우 프로시저에서 이 메시지를 처리하도록한다. 메시지를 큐에 넣기만 하고 바로 리턴하므로 메시지를 붙인 후 즉시 다른 작업을 할 수 있지만 큐에 대기하고 있는 다른 메시지가 있으면 뒤에 붙인 메시지는 곧바로 처리되지 않는다. 큐에 붙여진 메시지는 GetMessage()에 의해 읽혀지고 DispatchMessage()에 의해 윈도우 프로시저로 보내져 처리될 것이다.
급하게 처리할 필요가 없거나 또는 지금 하고 있는 작업을 완전히 끝내야만 처리할 수 있는 메시지는 PostMessage 함수로 큐에 붙인다. 이 함수로 붙여진 메시지는 언제 처리될지 정확하게 예측하기 힘들다. 그래서 붙여지는 메시지는 wParam와 lparam에는 지역 포인터를 사용지 말아야 한다. 메시지를 붙일 시점에는 포인터가 존재했더라도 메시지가 처리될 시점에는 포인터가 무효해질 수 있기 때문이다. PostMessage()는 메시지를 큐에 붙인 후 성공하면 TRUE를 리턴하며 실패하면 FALSE를 리턴하는데 메시지 큐는 크기가 한정되어 있기 때문에 고속으로 전송되는 모든 메시지를 다 수용하지 못 할 수도 있다. 다행히 Win32 환경에서는 큐 크기가 대폭 늘어나서 웬만해서는 큐가 부족한 상황이 잘 발생하지 않는다. 
SendMessage()는 메시지를 큐에 넣는 것이 아니라 곧바로 윈도우 프로시저로 보내 즉각 처리하도록 하며 메시지가 완전히 처리되기 전에는 리턴하지 않는다. 즉, 블록시켜서 SendMessage()는 윈도우 간 특히 부모 윈도우와 차일드 컨트롤 간의 통신에 자주 사용된다.  예를 들어, 리스트 박스에 LB_ADDSTRING이라는 메시지를 보내면 이는 리스트 박스에게 문자열 항목을 추가하라는 명령이 되며 항목이 완전히 추가되고 난 후에 SendMessage()가 리턴된다.
윈도우 간에 메시지를 교환할 때 어떤 함수를 사용할 것인가는 신중하게 결정해야 하는데 대부분의 경우는 SendMessage()로 보내는 것이 정석이며 또 효율적이다. 또 WM_COPYDATA같은 메시지는 그 특성상 반드시 SendMessage()로만 보내야 하며 PostMessage()로 붙여서는 않된다.
즉, 정리하자면 SendMEssage()는 당장 어떤 일을 하라는 명령이며, PostMessage()는 한가해질 때 어떤 일을 하라는 신호이다.
부가하자면, PostMessage()는 큐에 넣고, SendMessage()는 WndProc의 case 하나를 호출하는 것과 같다.



출처 : http://www.winapi.co.kr/ / 윈도우즈 API 정복

댓글

Holic Spirit :: Tistory Edition

design by tokiidesu. powerd by kakao.