오래된 Visual Studio 2008 MFC 환경에서 CHtmlView를 품은 다이얼로그를 개발하며 겪었던 며칠간의 험난한 여정을 기록으로 남겨봅니다. 간단할 줄 알았던 작업은 예상치 못한 문제들을 연이어 터뜨렸고, 그 과정에서 MFC와 Windows 메시지 시스템의 깊은 곳까지 탐험해야 했습니다. 이 글이 저와 비슷한 문제로 고통받는 누군가에게 작은 등불이 되기를 바랍니다.
1. 미스터리한 종료: 다이얼로그를 닫았을 뿐인데 프로그램이 꺼진다?
가장 처음 마주한 황당한 문제였습니다. CHtmlView를 상속받아 만든 CScrHtml 뷰를 CDialog 위에서 생성하고, 이 다이얼로그를 닫으면 메인 프로그램까지 함께 종료되는 현상이었습니다.
- 원인: CView에서 파생된 클래스는 윈도우가 파괴될 때 PostNcDestroy 가상 함수에서 delete this;를 호출하여 스스로를 메모리에서 해제합니다. 이 동작이 다이얼로그의 생명주기와 충돌하면서, 프레임워크가 응용 프로그램 전체에 WM_QUIT 메시지를 보내는 오작동을 일으킨 것이었습니다.
- 해결: CScrHtml 클래스에서 PostNcDestroy 함수를 재정의(override)하고, 그 안을 아무것도 하지 않도록 비워두었습니다. 부모 클래스의 함수를 호출하지 않음으로써 delete this;가 실행되는 것을 막았고, 뷰의 생명주기를 온전히 다이얼로그가 관리하도록 만들었습니다.
2. 유령 모달: DoModal이라 쓰고 ‘모달리스’라 읽는다
분명 DoModal()로 다이얼로그를 띄웠는데도, 그 뒤에 있는 부모 윈도우가 아무렇지 않게 클릭되고 조작되는 문제가 발생했습니다. CHtmlView가 품고 있는 ActiveX 컨트롤(웹 브라우저 엔진)이 MFC의 모달 상태를 멋대로 풀어버리는 것이 원인이었습니다.
- 해결 (1차 – 타이머): 가장 간단하고 확실한 방법은 타이머였습니다. OnInitDialog에서 SetTimer로 0.1초 간격의 타이머를 설정하고, OnTimer 핸들러에서 현재 활성화된 창이 다이얼로그 자신이 아닐 경우 SetForegroundWindow()를 호출하여 강제로 포커스를 되찾아왔습니다.
- 해결 (2차 – RunModalLoop 시도): 더 근본적인 해결을 위해 DoModal을 직접 구현하는 RunModalLoop 재정의까지 시도했습니다. 하지만 CHtmlView의 비표준적인 동작 방식 때문에 이 방법은 더 복잡하기만 할 뿐, 완벽하게 동작하지 않았습니다. 결국 단순하지만 강력한 타이머 방식이 이 문제의 정답이었습니다.
3. 투명한 창의 딜레마: 클릭이냐, 투명이냐
사용자 경험을 위해 다이얼로그의 배경을 투명하게 만들고 싶었습니다. 하지만 여기서 또 다른 선택의 기로에 놓였습니다.
- 진짜 투명 (WS_EX_LAYERED): SetLayeredWindowAttributes API를 사용해 특정 색상을 투명하게 만드는 방법입니다. 시각적으로 완벽한 투명 효과를 주지만, 투명한 영역은 마우스 클릭이 그대로 통과(pass-through)되어 버리는 치명적인 단점이 있었습니다.
- 가짜 투명 (배경 캡처): 클릭을 막기 위해 차선책을 선택했습니다. 다이얼로그가 뜨기 직전, 자신의 위치에 있는 부모 윈도우 화면을 캡처하여 비트맵으로 저장한 뒤, OnEraseBkgnd에서 이 비트맵을 배경으로 그리는 방식입니다. 시각적으로는 투명해 보이지만 실제로는 모든 영역이 존재하는 창이므로 클릭 문제를 해결할 수 있었습니다. 다만, 배경이 고정된다는 한계는 명확했습니다.
4. 새로운 대안: 마우스 가두기 (ClipCursor)
투명화의 한계를 느끼고 방향을 틀었습니다. 사용자의 상호작용을 다이얼로그에 집중시키는 더 확실한 방법으로, 마우스 커서를 다이얼로그 안에 가두기로 했습니다.
- 해결: OnInitDialog에서 GetWindowRect로 다이얼로그의 화면 좌표를 얻어와 ClipCursor API를 호출하여 마우스의 움직임을 제한했습니다. 가장 중요한 것은, OnOK, OnCancel, OnDestroy 등 다이얼로그가 닫히는 모든 경로에서 ClipCursor(NULL)을 호출하여 반드시 제한을 풀어주는 것이었습니다.
5. 깨져버린 언어: 태국어는 왜 물음표가 될까?
다국어 지원을 위해 버튼에 태국어 텍스트를 넣자 어김없이 물음표(???)가 나타났습니다. 전형적인 인코딩 문제였습니다.
- 원인: Visual Studio 2008 프로젝트의 기본 문자 집합은 ‘멀티바이트(MBCS)’였습니다. 이는 시스템 로캘(한국어 Windows)에 맞는 코드 페이지만 지원하므로, 다른 언어는 표현할 수 없었습니다.
- 해결: 프로젝트 속성에서 문자 집합을 ‘유니코드(Unicode)’로 변경하는 것이 정답이었습니다. 이와 함께 코드의 모든 문자열을 _T(“…”) 매크로로 감싸고, 태국어를 지원하는 Tahoma 같은 폰트를 명시적으로 지정해주자 비로소 아름다운 태국어가 화면에 표시되었습니다.
6. 마지막 관문: ESC 키 무력화
사용자가 실수로 ESC 키를 눌러 다이얼로그를 닫는 것을 방지해야 했습니다.
- 해결: CDialog에서 ESC 키는 OnCancel 가상 함수를 호출합니다. 이 OnCancel 함수를 재정의하고, 내부를 비워 부모 클래스의 함수를 호출하지 않는 것만으로 간단하게 해결할 수 있었습니다.
글을 마치며
오래된 기술을 다루는 것은 마치 낡은 지도를 들고 보물을 찾는 것과 같습니다. 곳곳에 함정이 있고 길은 명확하지 않지만, 끈기를 가지고 하나씩 문제를 해결해 나갈 때마다 느끼는 성취감은 무엇과도 바꿀 수 없습니다. 이번 경험을 통해 MFC의 내부 동작을 더 깊이 이해하게 되었고, 무엇보다 ‘포기하지 않으면 답은 반드시 있다’는 평범한 진리를 다시 한번 깨닫게 되었습니다.
참고
#MFC, #CHtmlView, #이슈해결, #VisualStudio2008