Windows

psycho 2012. 7. 29. 01:11

 특정 윈도우에 특정 메시지를 보내는 API 함수로는 SendMessage()와 PostMessage()의 두 가지가 있다. 물론 MSDN에도 잘 설명되어있다시피 두 함수의 차이는 명백하다. SendMessage()는 대상 윈도우의 메시지 처리 함수가 보낸 메시지를 처리하고 반환할 때까지 기다리고 그 반환값을 돌려주며, PostMessage()는 메시지 큐에 메시지만 추가하고 바로 반환한다. 그런데, 이 특성을 잘못 이해하고 프로그램을 작성하여 프로그램이 원하는 대로 동작하지 않는 경우가 있다. 여기서는 간단한 예를 살펴보도록 한다.

 

 

 

 당신은 이제부터 키보드 입력을 시뮬레이트하는 일종의 매크로 프로그램을 만들고 싶어한다. 그래서 대충 다음과 같이 코드를 작성했다.

 

 SendMessage(hWnd, WM_KEYDOWN, _VK_CODE_, 0);

 SendMessage(hWnd, WM_KEYUP, _VK_CODE_, 0);

 

 이 코드가 정말 원하는 대로 동작할까? 답은 '아니올시다'이다. 앞에서 SendMessage()의 특성이 뭐라고 했는가? 대상 윈도우에서 보낸 메시지를 처리할 때까지 기다렸다가 반환한다고 하였다. 그렇다면, 위에서 보낸 두 개의 메시지는 따로따로 처리가 될 것이다. 그런데, 위 두 개의 메시지는 기본적으로 윈도우의 메시지 루프에서 TranslateMessage() 함수를 통하여 조합된 다음 인식되게 된다. 당연히 두 메시지를 연관지어 한꺼번에 처리해야 제대로 동작하는데, 서로 상관없는 메시지처럼 취급되어 따로따로 처리를 해 버리니 제대로 동작하지 않을 것임은 자명하다. 그렇기 때문에 이 때는 반드시 PostMessage()를 사용하여야 한다.

 

 그렇다면 다음 코드는 어떨까?

 

 SendMessage(hWnd, WM_CHAR, _VK_CODE_, 0);

 

 이 코드는 과연 같은 문제가 있을까? 역시 답은 '아니올시다'이다. WM_CHAR 메시지는 WM_KEYUP, WM_KEYDOWN 메시지와는 다르게 그 자체만으로 독립적이다. 실질적으로는 위의 두 메시지가 TranslateMessage()를 거쳐서 합성된 결과가 WM_CHAR 메시지이다. 이 때는 PostMessage()를 사용하여도 문제는 없을 것이다. 어차피 메시지 큐라는 것은 메시지가 입력된 순서대로 처리되므로, 메시지 처리 함수가 반환하기를 굳이 기다리지 않아도 원하는 순서대로(메시지를 보낸 순서대로) 처리될 것이기 때문이다. 그렇지만, 이 방식으로는 제어 키(Alt, Ctrl, Shift 등)와의 키 조합을 적절하게 처리하는 데 무리가 있다. 해당 키를 눌렀다가 바로 떼는 것과 같은 효과가 나기 때문이다.

 

 

 

 이번에는 다른 예이다. 당신은 콤보 상자를 사용하는 예제 프로그램을 하나 작성하는데, 목록 중에 있는 어떤 항목을 선택하면 그것이 몇 번째 항목인지 구하는 아주 간단한 예제이다. 그래서 (역시 대충) 다음과 같이 코드를 작성했다.

 

case ID_COMBOBOX:
    switch(HIWORD(wParam))
    {
        case CBN_SELCHANGE:
            ComboIndex = PostMessage(hCombo, CB_GETCURSEL, 0, 0);
            break;
        }
    break;
}

 

 설마 여기에서 뭐가 잘못되었는지 모르는 사람은 없으리라 믿는다. 왜냐 하면 그건 MSDN 문서, 아니 한글로 쓰여 있는 이 글의 서두조차 제대로 읽지 않았다는 뜻이니까. PostMessage()는 메시지 큐에 메시지를 추가하기만 하고 바로 반환하며, 반환값은 성공하면 0이 아닌 값(참), 실패하면 0(거짓)이다. 콤보 상자에서 선택된 항목의 Index를 받아와야 하는데, PostMessage()는 속된 말로 '그딴 거 없다'. 메시지를 큐에 추가하기만 하면 임무 끝, 그 뒤는 내 알 바 아닌 셈이다. 반면 처음에 설명했다시피 SendMessage()는 보낸 메시지를 완전히 처리할 때까지 기다린 다음 그 반환값을 돌려준다. 따라서 여기서는 반드시 SendMessage()를 사용하여야 한다.

※ 사실 이 예제는 개념 설명을 위해 만든 것으로, 실제로는 저런 식으로 코드를 짜면 문제가 있음을 알아채기 쉽다. 왜냐 하면, SendMessage()는 반환값 형이 LRESULT이고 PostMessage()는 BOOL이기 때문이다. 결과값을 받는 변수만 제대로 선언을 했다면 고생할 일은 많이 줄어들 것이다.

 

 

 

 이상 결론은 다음과 같다. 여러 개의 메시지가 연관되어 한꺼번에 처리되어야 하는 경우에는 반드시 PostMessage()를 사용하여야 하고, 보낸 메시지에 대한 반환값을 사용해야 할 경우나 '완전히 처리되었다'는 사실이 중요할 때는 반드시 SendMessage()를 사용하여야 한다. 기타 다른 경우에는 어떤 것을 사용하여도 관계없다.