TrackPopupMenu에 숨겨진 비밀?

@codemaru · June 09, 2010 · 3 min read

한 십만오천년만에 GUI 프로그래밍을 하다가 알게된 사실입니다. TrackPopupMenu의 6번째 인자에는 메뉴를 소유한 윈도우 핸들을 전달하도록 되어 있습니다. MSDN을 읽어보면 알겠지만 TPM_NONOTIFY를 지정한 경우에도 윈도우 핸들은 넣어야 한다고 되어 있죠. 여기다 NULL을 지정하면 메뉴가 표시되지 않습니다. 그런데 핸들을 지정한 경우에도 메뉴가 표시되지 않는 경우가 있습니다. GetLastError를 해보면 잘못된 파라미터 오류가 반환되죠.

왜 그럴까요? 이유는 간단합니다. TrackPopupMenu를 호출한 스레드와 메뉴를 소유한 윈도우가 속한 스레드가 일치하지 않아서 발생하는 겁니다. 말로 하면 어려운데 코드로 살펴보면 쉽습니다. 다음 조건이 반드시 만족될 때에만 메뉴가 자연스럽게 표시된다는 겁니다.

GetCurrentThreadId() == GetWindowsThreadProcessId(Window, NULL)

당연히 항상 그 조건을 만족하는게 아니냐, 라는 질문을 하시는 분들이 계신것 같습니다. 물론 일반적인 경우에는 그 조건이 항상 만족됩니다. 하지만 다른 프로세스에 메뉴를 띄우려고 하는 경우에는 문제가 되더군요. 제가 하려고 했던 작업은 익스플로러에서 특정 키를 눌렀을 때 메뉴를 띄우는 작업이었습니다. 그랬더니 코드가 호출된 스레드와 익스플로러 윈도우가 속한 스레드가 일치하지 않아서 메뉴가 표시되지 않더군요.

그럼 당연히 메뉴를 소유한 우리 윈도우 핸들을 넣어주면 되는게 아니냐, 라고 하실 텐데요. 해보시면 아시겠지만 그렇게 할 경우에 액티브 윈도우가 변경되면서 아주 우스는 결과가 발생합니다. 탐색기에서 자연스럽게 메뉴가 뜬 것 처럼 호출이 되지 않는 것이죠.

결국 다른 프로세스에 메뉴를 우아하게 띄우기 위해서는 TrackPopupMenu라는 함수가 해당 윈도우가 속한 스레드 컨텍스트에서 반드시 호출되어야 한다는 겁니다.

@codemaru
돌아보니 좋은 날도 있었고, 나쁜 날도 있었다. 그런 나의 모든 소소한 일상과 배움을 기록한다. 여기에 기록된 모든 내용은 한 개인의 관점이고 의견이다. 내가 속한 조직과는 1도 상관이 없다.
(C) 2001 YoungJin Shin, 0일째 운영 중