본문 바로가기

API

[API] 대화상자(Dialog)에서 ESC 누를 때 종료되지 않게 하기

제가 제작하고 있는 프로그램은 순수 API 로만 이루어진 프로그램입니다.

대화상자가 ESC 키에 반응 안하게 하려고 구글링을 해봤지만 대부분 MFC Dialog 에 관한 내용만 뜨더군요.

MFC 에서는  PreTranslateMessage  라는 걸로 쉽게 해결이 가능하지만 API 는 아닙니다... 흑.

하지만 역시 구글링은 절 버리지 않는군요...  

http://www.williamwilling.com/blog/?p=28
 


여기 위 주소에 API Dialog 에 관한 해결 방법이 있었네요. 


보통 API 로 Dialog 프로시저를 작성할 때


BOOL CALLBACK MainDlgProc(HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
	switch (iMessage)
	{
	case WM_COMMAND:
		switch (LOWORD(wParam)){
		case IDCANCEL:
			EndDialog(hDlg, IDCANCEL);
			return TRUE;
	}
	return FALSE;
}

기본 틀은 위의 코딩과 유사할 것입니다.

하지만 위와 같이 코딩 했을 시 ESC 를 눌렀을 때 대화상자가 종료되어 버립니다.

그렇다고 WM_COMMAND 메세지 처리 부분을 없애면 대화상자의 우상단에 있는 X 버튼도 작동하지 않게 됩니다.

그렇다면 어떻게 해야하나...? 

링크에 따르면 WM_COMMAND 대신 WM_CLOSE 메세지를 처리하면 된다고 합니다.


BOOL CALLBACK MainDlgProc(HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
	switch (iMessage)
	{
	case WM_CLOSE:
		EndDialog(hDlg, IDCANCEL);
		return TRUE;
	}
	return FALSE;
}

이렇게 코딩을 하면 ESC 에 반응하지 않고 종료되지 않습니다.

그런데 우상단 X버튼, 시스템 메뉴의 닫기, ALT + F4, 작업 관리자의 작업 끝내기 등은 여전히 작동이 됩니다. ~_~


매우 간단해 보여도 저에게는 정말 필요한 기능이였습니다~


그런데!

Multiline 속성을 가진 Edit 에서 ESC 를 누르면 WM_CLOSE 메세지가 처리되어 대화상자가 종료되어 버립니다. -_-;

물론 Multiline 속성을 가진 Edit 를 사용하시지 않는 분들은 전혀 상관 없습니다만...

아무튼 이런 문제에 대한 해결법도 적혀 있습니다.

WM_CLOSE 대신 WM_SYSCOMMAND 메세지로 처리 가능하다고 하네요.


BOOL CALLBACK MainDlgProc(HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
	switch (iMessage)
	{
	case WM_SYSCOMMAND:
		if (wParam == SC_CLOSE)
		{
			EndDialog(hDlg, 0);
			return TRUE;
		}
		break;
	}
	return FALSE;
}

이와 같이 처리를 하게 되면 Edit 에서 ESC를 눌러도 대화상자가 종료되지 않습니다. 

그런데 또 한가지 문제가 생깁니다. -_-; 바로 작업 관리자에서 작업 끝내기를 할 때 반응을 하지 않습니다.

직접 해보시면 바로 종료되지 않다가 조금 텀을 두고 강제 종료되거나 응답 없음이라는 표시가 뜹니다.

사소한 것 같지만 저는 이런게 매우 거슬려요. ~_~ 친절하게도 링크에서는 이에 대한 대안 솔루션이 적혀 있습니다.

약간 번거로운 방법이지만 서브클래싱을 이용하는 방법인데요. 

WM_CLOSE 메세지를 여전히 사용은 하되 Edit control의 서브클래싱을 통해 ESC 키에 대한 처리를 따로 하는겁니다.

WNDPROC OldControlProc;

LRESULT CALLBACK ControlProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_KEYDOWN:
		if (LOWORD(wParam) == VK_ESCAPE) 
		{
			/* 주석 부분은 링크에 포함되어 있었던 부분입니다.
			   단지 꼭 필요한 것 같지는 않아서 주석 처리 했습니다.
			   만약 넣어야 된다고 생각하시는 분들은 주석 처리를 빼고 포함해주시면 
됩니다.
HWND hwnd_parent = GetAncestor(hWnd, GA_PARENT); SendMessage(hwnd_parent, WM_COMMAND, IDCANCEL, (LPARAM) hWnd);*/ return 0; } break; } return CallWindowProc(OldControlProc, hWnd, message,wParam, lParam); } BOOL CALLBACK MainDlgProc(HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam) { switch (iMessage) { case WM_INITDIALOG: { HWND hwnd_edit = GetDlgItem(hDlg, IDC_EDIT1); OldControlProc = (WNDPROC) SetWindowLongPtr(hwnd_edit, GWL_WNDPROC,
(LONG_PTR) ControlProc); return TRUE; } case WM_CLOSE: { EndDialog(hDlg, 0); return TRUE; } } return FALSE; }

이렇게 하면 EDIT 에서도 ESC가 작동하지 않고 나머지도 문제 없이 잘 되는 것 같네요.


저는 Multiline Edit 를 사용해야 되는 터라 서브클래싱을 이용한 해결 방법을 이용할 생각입니다.

적당히 자기에게 맞는 코딩 방법을 선택해서 사용하시면 되겠습니다~