본문 바로가기

Library/Windows Programming

ATL/WTL 멀티미디어 타이머 사용하기

WTL을 사용할 때 WM_TIMER 메세지가 때때로 오지 않을 경우가 있다. 현재 실행하고 있는 프로세스 안에서 무엇인가가 바쁘게 돌아가고 있다면 WM_TIMER 메세지가 제대로 오지 않을 수도 있다.

어떤 일을 타이머에 맞춰서 간단하게 처리하고 싶을 때, 딱 이런 목적으로 만들어진 WM_TIMER가 제대로 동작하지 않는다면 곤란할 것이다. 그러나, 다른 스레드에서 그 정도로 복잡한 일을 하고 있어서 메세지가 제대로 오지 않는다면, WM_TIMER를 쓰는 것은 맞지 않다. WM_TIMER는 메세지 처리 과정에서 우선 순위가 낮으며, 경우에 따라서 윈도우가 무시해버릴 수도 있기 때문이다. 이런 경우, 멀티미디어 타이머를 사용하는 것이 좋다.

멀티미디어 타이머는 WM_TIMER와 달리 다른 스레드에서 생성되어 계속 메세지를 보내준다. 이 메세지를 처리할 콜백 함수를 등록하고, 여기서 원하는 일을 하면 된다. 그러나, 이 콜백 함수 안에서 사용할 수 있는 함수는 극히 제한되어 있는데, 시스템 함수를 직접 호출하는 방식은 대부분 제대로 동작하지 않는다. 타이머의 해상도가 높고 주기가 짧다면 더욱 그럴 것이다. 메세지를 처리하기 위해서 콜백 함수 안에 PostMessage를 사용하여 다시 메세지를 보내고, 바로 리턴하는 방식의, 최소한 처리가 간결하도록 콜백 함수를 구성하는 것이 정신 건강에 대단히 좋다. SendMessage가 아니라, PostMessage인 것을 주의하라. SendMessage는 해당 메세지를 보낸 뒤 제어권이 다시 돌아올 때까지 대기하며, 타이머의 주기가 짧다면 메세지 슬롯이 가득차버려 의도한대로 동작하지 않는다.

또, 콜백 함수에서 PostMessage를 사용하여 메세지 핸들러에서 해당 메세지를 처리한다고 하더라도, 메세지 큐에 쌓이는 방식이 아닌 메세지를 동기식으로 처리한다면 타이머 주기가 충분히 길지 않은 경우 문제가 생긴다. 이것은 윈도우가 메세지 큐에 쌓이는 메세지와, 바로 처리되는 메세지를 다루는 방식 때문이다. 타이머 콜백 함수에서 PostMessage()를 사용하여 메세지를 날려주고, 해당 메세지 핸들러에서 메세지 큐에 등록 가능한 메세지라면 메세지 큐에 등록하고 비동기식으로 리턴해야 원하는대로 동작할 것이다.

예를 들어, 타이머의 콜백 함수에서 PostMessage를 사용하여 어떤 윈도우에 메세지를 보냈고, 그 메세지 핸들러가 Invalidate()를 호출한다고 했을 때, 그 윈도우는 제대로 화면이 갱신되지 않을 것이다. 대신, 그 윈도우에 PostMessage(WM_PAINT)를 사용하여 메세지큐에 메세지를 비동기식으로 등록해야 한다.