Search
Duplicate
💄

뽕빨! Windows Messaging

Category
S/W 엔지니어
Tags
Jeffrey Richter
Programming Applications for Microsoft Windows
Windows Messaging
Message Queue
Created time
2004/11/05

쓰래드와 메시지 : 기본 규칙

프로세스 하나가 10,000개에 이르는 제각기 다른 종류의 사용자 객체(아이콘, 커서, 윈도우 클래스, 메뉴, 단축키 테이블 등)를 생성할 수 있다. 한 쓰래드에서 이들 객체 중 하나를 생성하는 함수를 호출했을 때, 생성된 객체는 그 쓰래드의 프로세스가 소유하게 된다(이 말은 프로세스가 죽으면 그 객체도 함께 죽는다는 말이다).
반면 윈도우(window)와 훅(hook) 객체는 그 윈도우를 생성하거나 훅을 설치한 쓰래드가 소유한다(이 말은 그 쓰래드가 죽으면 그 객체도 함께 죽는다는 뜻이다). -> 윈도우를 소유한 쓰래드가 그 윈도우의 모든 메시지를 관장한다. -> 하나 이상의 윈도우를 생성한 모든 쓰래드에는 메시지 큐가 할당되고, 그 때문에 그 쓰래드만의 메시지 루프를 갖게 된다.
또한 각각의 쓰래드는 키보드 포커스라던가, 윈도우 활성화, 마우스 캡쳐 등을 다루는 그 자신만의 환경(simulated environment)이 있다고 생각한다. 그러므로 각 쓰래드에는 이들 환경 변수를 저장할 THREADINFO 구조체가 있고, 이 구조체로써 그 쓰래드만의 붙여진(posted) 메시지 큐, 보내진(send) 메시지 큐, 응답(reply) 메시지 큐, 가상화된(virtualized) 입력 큐, wake 플래그와 그 외의 여러 환경 변수등을 식별해낼 수 있다. (메시지 큐가 하나로만 구성된 것이 아니더라..)
결국 THREADINFO 구조체는 윈도우즈 메시지 시스템의 주춧돌이 되더라. 하지만, UI관련 함수가 호출되기 전까지는 이 THREADINFO를 포함한 관련 리소스가 할당되지 않는다. (근데 이 구조체는 'internal', 'undocumented' 구조체라 직접 뜯어볼 수가 없다. 됀장 MS.)

POST 계열 메시지 함수

붙여진 메시지 큐에 메시지만 넣은 다음, 그 메시지의 처리 여부에 관계없이, 바로 호출자 함수로 되돌아온다.
POST 계열 메시지 함수 : PostMessage, PostThreadMessage(다른 쓰래드에 메시지를 붙일 때), PostQuitMessage

SendMessage 함수

메시지를 윈도우 프로시저에 직접 보낸다. 오직 메시지가 처리되었을 때만 호출자 함수로 되돌아온다. 요러한 동기적 처리방식 때문에 주로 사용하게 된다.
다 른 쓰래드에 메시지를 넘길 경우 : 메시지를 보낸 쓰래드는 멈춰서서, 메시지를 받은 쓰래드가 그 메시지를 처리 완료하여 보낸 쓰래드의 응답 메시지 큐에 처리된 결과(SendMessage의 반환값)가 담긴 메시지를 붙일 때에야, 비로소 깨어나 그 결과 처리를 시작으로 다음 실행을 재개한다.
호출된 쓰래드에 문제가 있어 메시지를 처리 못할 경우 : 호출자 쓰래드와, 호출된 쓰래드 모두 deadlock에 걸린다! -> 이를 위해 SendMessageTimeout, SendMessageCallback, SendNotifyMessage, ReplyMessage 함수가 있는 것이다.
SendMessageTimeOut 함수 : 보낸 메시지에 대한 응답을 받기까지의 최대 시간을 지정.
SendMessageCallback 함 수 : 메시지를 보내고 잽싸게 되돌아온다. 호출된 쓰래드에서 메시지 처리를 마치고 응답을 보내면, 지정한 CallBack 함수가 호출된다. 호출되는 시점은 호출자 함수의 다음번에 응답 메시지 큐에서 메시질 가져올 때이다. 메시지 브로드케스팅에도 이용.
SendNotifyMessage 함수 : 이 역시 메시지를 보내고 잽싸게 되돌아온다. 때문에 PostMessage와 비스무리할 수도 있지만, 윈도우에 직접 메시지를 보내기에 붙여진(Posted) 메시지보다 처리 우선순위가 높다.
ReplyMessage 함 수 : (deadlock을 피하기 위해) 위의 함수들은 메시지를 보내는 측에서 호출하지만, 이 함수는 받는 쪽에서 호출한다. 이 함수를 호출한다는 것은 시스템에게 메시지 결과를 알 만큼 충분히 처리했고, 처음 메시지를 보낸 측에 결과 메시지를 싸서 보내라고 알리는 것이다. 그 결과, 처음 보낸 측은 깨어나 응답 메시지를 처리하게 될 것이야~

쓰래드 메시지 큐에서의 메시지 추출 메카니즘

쓰래드가 GetMessage나 PeekMessage를 호출했을 때, 시스템은 그 쓰래드 큐의 상태 플래그를 검사하여 어떤 메시지를 처리할지를 결정한다. 다음은 처리 순서다.
QS_SENDMESSAGE 플래그가 켜있으면, 시스템은 메시지를 해당 윈도우 프로시저로 보낸다. 이 때에 GetMessage와 PeekMessage역시 이 작업에 내부적으로 관여하여, 윈도우 프로시저가 그 메시지를 다 처리한 후에도 return하지 않는다. 대신에, 처리할 다음 메시지를 기다린다.
붙여진(posted) 메시지 큐에 메시지가 있으면, GetMessage와 PeekMessage는 이들 함수로 넘겨진 MSG 구조체를 채운다. 이 쓰래드의 메시지 루프는 보통 이 시점에서 DispatchMessage를 호출하여, 적당한 윈도우 프로시저가 그 메시지를 처리하도록 한다.
QS_QUIT 플래그가 켜져 있으면, GetMessage와 PeekMessage는 WM_QUIT를 반환하고 QS_QUIT 플래그를 재설정한다.
가상화된 입력 큐에 메시지가 있으면, GetMessage와 PeekMessage는 하드웨어 입력 메시지를 반환한다.
QS_PAINT 플래그가 켜 있으면, 이들 함수는 적당한 윈도우를 위한 WM_PAINT 메시지를 반환한다.
QS_TIMER 플래그가 켜 있으면, 이들 함수는 WM_TIMER 메시지를 반환한다.
보다시피, GetMessage와 PeekMessage는 보내진(send) 메시지를 처리하고 나서는 바로 반환하지 않고, 그 다음 과정으로 넘어가서야 반환한다(특히 붙여진(posted) 메시지를 처리하고나서야 반환한다). 그리고 QS_QUIT에서만 TRUE를 반환한다. 큐를 하나만 사용하는 것도 아니고, 플래그까지 사용하다니, 아주 메카니즘이 지랄같다. 아마 위 설명만 갖고는 이해가 다 안될 것이다. 더 공부해야…
큐 상태 플래그나 커널 객체로 쓰래드 깨우기, 메시지로 데이터 보내기 등의 주제가 더 있는데, 이 부분은 나중 필요할 때 더~