쓰래드와 메시지 : 기본 규칙
•
프로세스 하나가 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를 반환한다. 큐를 하나만 사용하는 것도 아니고, 플래그까지 사용하다니, 아주 메카니즘이 지랄같다. 아마 위 설명만 갖고는 이해가 다 안될 것이다. 더 공부해야…
큐 상태 플래그나 커널 객체로 쓰래드 깨우기, 메시지로 데이터 보내기 등의 주제가 더 있는데, 이 부분은 나중 필요할 때 더~