Win32 API 환경에서 메시지 박스(MessageBox)를 띄울 때, 다른 창 뒤에 가려지거나 포커스를 받지 못하는 경우가 있다. 사용자에게 반드시 눈에 띄어야 하는 알림·경고·확인 대화상자를 다룰 때는 최상단(topmost) 표시와 포커스 확보가 중요하다. 이 포스트에서는 그 배경과, CBT 후크·AllowSetForegroundWindow API를 활용한 두 가지 실전 해결 방법을 정리한다.
개요
- 대상: Win32 네이티브 앱(C/C++)에서
MessageBox를 사용하며, 항상 최상단·포커스를 보장하고 싶은 개발자. - 전제:
MB_SETFOREGROUND만으로는 Windows의 포그라운드 정책 때문에 의도대로 동작하지 않는 경우가 많다. 이에 대한 대안으로 CBT 후크와AllowSetForegroundWindow를 소개한다.
문제의 배경
Windows에서는 포그라운드 창을 바꾸는 동작에 보안·정책상 제약이 있다. 다른 프로세스가 활성 창인 상태에서 우리 프로세스가 메시지 박스만으로 포커스를 가져오려 하면, 시스템이 이를 제한할 수 있다.
MessageBox(..., MB_SETFOREGROUND)는 호출 프로세스가 이미 포그라운드이거나, 시스템이 허용하는 조건일 때만 포커스를 준다.- 따라서 백그라운드에서 호출되거나, 다른 앱이 활성인 경우 메시지 박스가 뒤에 가려지거나 포커스를 받지 못할 수 있다.
이 제한을 우회하거나 완화하기 위해 아래 두 가지 방법을 사용할 수 있다.
해결 방법 요약(흐름도)
아래 Mermaid 흐름도는 “메시지 박스를 최상단에 띄워야 할 때” 어떤 접근을 고려할 수 있는지 요약한다.
flowchart LR
subgraph needTopMost["요구사항"]
reqTopMost["메시지 박스를최상단에 표시"]
end
subgraph methodOne["방법 1"]
cbtHook["CBT 후크 등록"]
msgBoxCall["MessageBox 호출"]
setPosTopmost["HCBT_ACTIVATE에서SetWindowPos TOPMOST"]
end
subgraph methodTwo["방법 2"]
allowForeground["AllowSetForegroundWindow호출"]
msgBoxCall2["MessageBox 호출"]
end
reqTopMost --> cbtHook
reqTopMost --> allowForeground
cbtHook --> msgBoxCall
msgBoxCall --> setPosTopmost
allowForeground --> msgBoxCall2
- 방법 1: 창이 활성화되는 순간(HCBT_ACTIVATE)을 후크로 잡아
HWND_TOPMOST로 올린다. - 방법 2: 메시지 박스 호출 전에
AllowSetForegroundWindow로 포그라운드 설정 권한을 부여한 뒤MB_SETFOREGROUND와 함께 호출한다.
해결 방법 1: CBT 후크 이용
CBT(Computer-Based Training) 후크를 사용하면 메시지 박스 창이 생성·활성화되는 순간을 가로채서 스타일을 바꿀 수 있다. WH_CBT 후크에서 HCBT_ACTIVATE가 발생할 때 해당 창에 SetWindowPos(..., HWND_TOPMOST, ...)를 적용하면, 메시지 박스가 항상 최상단에 붙어 있게 된다.
동작 순서
SetWindowsHookEx(WH_CBT, CBTProc, ...)로 같은 스레드에 CBT 후크 등록.MessageBox(..., MB_SETFOREGROUND)호출.- 시스템이 메시지 박스 창을 활성화하면서
CBTProc에HCBT_ACTIVATE전달. - 콜백에서 해당 HWND에
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE)적용 후UnhookWindowsHookEx로 후크 해제.
예제 코드
| |
- 후크는 동일 스레드에서만 동작하므로
GetCurrentThreadId()로 등록하는 것이 일반적이다. 다른 스레드에서 메시지 박스를 띄우면 해당 스레드에 후크를 걸어야 한다. - 이 방법도 시스템 보안 정책·UAC·포그라운드 잠금 등에 따라 일부 환경에서는 제한될 수 있으므로, “가능한 한 최상단으로 올린다”는 수준으로 이해하는 것이 좋다.
해결 방법 2: AllowSetForegroundWindow API 활용
AllowSetForegroundWindow는 현재 프로세스(또는 지정한 프로세스)가 포그라운드 창을 설정할 수 있도록 일시적으로 권한을 주는 API다. 메시지 박스를 띄우기 직전에 이 API를 호출한 뒤 MB_SETFOREGROUND와 함께 MessageBox를 호출하면, 포커스 확보 가능성이 높아진다.
예제 코드
| |
ASFW_ANY는 “어떤 프로세스든 포그라운드를 설정할 수 있도록” 허용하는 인자다. 특정 프로세스 ID를 넘기면 해당 프로세스만 권한을 받는다.- 호출 시점에 이미 다른 창이 강하게 포커스를 갖고 있거나, 정책에 의해 제한되면 동작이 보장되지 않을 수 있다. “대부분의 상황에서 포커스를 얻기 쉽게 만든다”는 보조 수단으로 쓰는 것이 적절하다.
두 방법 비교 및 선택
| 구분 | CBT 후크 | AllowSetForegroundWindow |
|---|---|---|
| 목적 | 창을 항상 최상단(HWND_TOPMOST) 으로 유지 | 포그라운드 설정 권한 부여 후 포커스 확보 |
| 구현 | 후크 등록·해제, 스레드 일치 필요 | API 한 번 호출 후 MessageBox 호출 |
| 적용 시점 | 창 활성화 시점(HCBT_ACTIVATE) | MessageBox 호출 전 |
| 제한 | 스레드별 후크, 정책에 따라 제한 가능 | 호출 시점·환경에 따라 효과 제한 |
- 최상단 고정이 더 중요하면 CBT 후크를 사용하고, 포커스만 확보하면 될 때는
AllowSetForegroundWindow만으로도 시도해 볼 수 있다. 필요하면 두 방법을 함께 사용할 수 있다(CBT로 TOPMOST 적용 + 호출 전 AllowSetForegroundWindow).
결론
Win32 API에서 메시지 박스가 최상단에 보이고 포커스를 갖도록 하려면 MB_SETFOREGROUND만으로는 부족한 경우가 많다.
- CBT 후크를 쓰면 메시지 박스 창이 활성화될 때
SetWindowPos(..., HWND_TOPMOST, ...)로 최상단에 붙일 수 있다. - AllowSetForegroundWindow를 쓰면 메시지 박스 호출 전에 포그라운드 설정 권한을 부여해, 포커스 확보 가능성을 높일 수 있다.
환경(백그라운드 호출, 다른 앱 활성 여부, 보안 정책 등)에 따라 동작이 제한될 수 있으므로, 실제 타깃 환경에서 동작을 확인한 뒤 상황에 맞는 방법을 선택하는 것이 좋다.
![Featured image of post [Win32] Win32 API에서 메시지 박스를 최상단에 표시하는 방법](/post/2025-02-20-win32-api-messagebox-topmost/messagebox_02_hu_a22bcf7163ff6c32.webp)
![[Rust] Comprehensive Rust 무료 강의 정리 및 코스 구조](/post/2022-12-30-comprehensive-rust/wordcloud_hu_d1420ff38434cdb6.webp)
![[Hardware] LattePanda Alpha에 Ubuntu 16.04 LTS 설치 가이드](/post/2018-12-06-install-ubuntu-16.04-on-lattepanda/wordcloud_hu_fc536f8de2cbd4bf.webp)
![[.NET] 런타임별 Finalizer 호출 차이와 IDisposable 권장](/post/2021-04-27-distructor-called-by-runtime/wordcloud_hu_7b7dd809a5d4bdc3.webp)
![[Tutorial] Learn Prompting - 프롬프트 엔지니어링 무료 가이드 정리](/post/2022-12-30-learn-prompting/wordcloud_hu_6a9d105de4834753.webp)
![[RPM] Spec 파일에서 주석과 매크로 동시 사용 시 주의사항](/post/2021-11-24-rpm-spec-comments/wordcloud_hu_6d09ac09623081c7.webp)