API

SendMessage vs PostMessage, 기타 프로시저 재진입

디버그정 2008. 7. 31. 02:09
SendMessage vs PostMessage 개발 ~*

2007/07/09 10:42

복사 http://blog.naver.com/yjjyg/80040027453

## SendMessage & PostMessage ##
1. 기본적인 동작 방식


 - 기본적으로 시스템에는 하나의 메시지큐가 있고, 또한 각 스레드 마다 메시지큐가 하나식 생긴다.

 - 스레드가 처음 생성될 시에는 메시지큐가 생성되지 않고, 메시지큐가 필요한 시점에서 생성된다.
  (메시지큐가 필요한 시점이란 메시지를 필요로 하는 관련 함수가 한번이라도 호출 되는 시점을 말한다..gdi함수 등..)


 - SendMessage의 경우 메시지큐를 거치지 않고 직접 윈도우 프로시저를 호출한다.

 - PostMessage의 경우 메시지큐에서 메시지를 받아와 순서대로 처리한다.
   (모든 메시지가 메시지큐에 차례로 들어가는 것은 아니다. 예를 들어 WM_PAINT의 경우 이미 메지시큐에
    WM_PAINT가 있다면 뒤에 들어온 WM_PAINT는 무시된다. 단 무효화 영역은 결합된다.)


  - PostMessage의 경우 메시지큐가 풀이 되면 메시지가 들어가지 않는다. 메시지가 올바로 삽입되었는지는 
    PostMessage의 반환값으로 검사할 수 있다.




2. SendMessage의 동작

 (PostMessage 경우 동작이 명확하기 때문에 설명은 필요없을 듯 함)


- SendMessage의 경우 윈도우 프로시저를 직접 콜하기 때문에 동작이 간단해 보이지만,
  다른 스레드와 연관될 경우 내용이 복잡해 진다.


   일단 기본적으로 컨텍스트 스위칭이 발생해야 하고 또 어느 시점에서 호출이 이루어져야 하는지 등의 내용을 알아야 한다.

  

   결론만 말하자만 thread a 에서 thread b에서 생성된 x라는 윈도우로 sendmessag 보낸다면
   그 호출은 thread b에서 GetMessage, PeekMessage, WateMessage 함수를 호출한 시점에서 동작한다.
   

   이는 thread b에서 메시지 처리중에 thread a에서 호출한 sendmessage가 호출됨으써 발생하는 문제를
   제거하기 위함이다. 즉 하나의 메시지를 완전히 처리하고 다음 메시지를 처리할 준비가 되었을 때 sendMessage에 대한 처리를 하는 것이다.

   그렇다고 SendMessage에 의 해 GetMessage, PeekMessage에서 메시지를 가지고 올 수 있는 것은 아니다.
   호출 시점만 그렇다는 것이다.(sendMessag가 메시지큐에 메지지를 넣는 것이 아니기 때문에 당연하다.)


   또한 thread a에서 sendmessage를 호출 하였을 때 thread b에서 처리 완료하기 전 까지 thread a는 블럭상태가 된다.
   (이 블럭 상태에서도 thread a로 들어오는 sendmessage가 처리가 되었다..
    sendmessage reentrancy에 대해서는 아래에서 알아보기로 하자...)


   그럼 sendmessage동작중에 조심해야 할 것들은??
   일단 데드락을 조심하라고 하는데 아직 이쪽은 확실한 이해가 안된다.
   (아마 보내는 스레드랑 받는 스레드랑 서로 기다리게 되는 경우에 발생할 듯 한데... 어째보면 당연하다..-_-)

   이 쪽은 InSendMessage, ReplyMessag쪽 함수 참고

   일단 조심해야할 것들~
    sendmessag로 스레드간 메시지를 보내고, 이것이 만약 GetMessage가 호출 된 시점에서
    처리가 될 때 그 안에서 다시 GetMeessage가 호출 될 경우 문제가 발생한다.(블락되어 버리는 듯)
   (처리 함수에서 MessageBox등을 띄울 때도 그렇다. 아마 MessageBox내에서 GetMessage를 호출하는 듯)
   아직 다른 경우는 모르겠음...





3. SendMessage reentrant


SendMessage 중에 reentrant가 가능하다.


예를 들어 간단히 하나의 경우만 살펴보자.

thread a 에서 OnButton1을 클릭 했을 때 thread b로 SendMessage 를 보내고 블럭되어 있을 때
thread c에서 thread a로 SendMessage를 보내서 OnMessage1이 실행된다고 하면


그 때의 콜 스택은 아래와 같다.



OnMessage1 <=======================
CWnd::OnWndMsg
CWnd::WindowProc
AfxCallWndProc
AfxWndProc
AfxWndProcBase
USER32! 77cf8734()
USER32! 77cf8816()
USER32! 77cfb4c0()
USER32! 77cfb50c()
NTDLL! 7c93eae3()
USER32! 77d0f3e3()
OnButton1() <=======================



즉, Onbutton1의 SendMessage 내부에서 다시 윈도우 프로시저 내로 직접 콜이 되는 형태이다.


재진입으로 인해 원치 않는 순서로 전역번수가 변할 수 있으니 주의가 필요하다!!