성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 정적 분석과 함께, 이제는 실행 시 성능 분석까지 (비록 Azu...
[정성태] .NET Source Browser를 이용해 Roslyn 소스 ...
[정성태] Experimental C# Interceptors: AOT &...
[정성태] .NET Conf 2023 (Day 2) - Tiny, fast...
[정성태] The end of the Tye Experiment #1622...
[정성태] This is a simple app that converts ...
[정성태] Wrathmark: An Interesting Compute W...
[정성태] FFmpeg Filters Every Youtuber Needs...
[정성태] 일단, PInvokeStackImbalance 오류가 발생했다는...
[Heegyoo Lee] 코드 자체는 동작하는 것처럼 보이는데요. (어떤 때는 시그니쳐가...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>IsDialogMessage와 협업하는 WM_GETDLGCODE Win32 메시지</h1> <p> 이전 글에서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C/C++ - 일반 창에도 사용 가능한 IsDialogMessage ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13292'>https://www.sysnet.pe.kr/2/0/13292</a> </pre> <br /> <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-isdialogmessagew'>IsDialogMessage</a> API에 대한 설명을 했는데요, 사실 그 API가 메시지 루프에서 불리는 것만으로는 대화창의 부가 기능을 매끄럽게 구현할 수 없습니다. 왜냐하면, 대화창 내의 컨트롤들이 어떤 부가 기능을 가지고 있는지 알아야만 하기 때문입니다.<br /> <br /> "<a target='tab' href='https://www.sysnet.pe.kr/2/0/13292'>C/C++ - 일반 창에도 사용 가능한 IsDialogMessage</a>" 글에서도 설명했지만, 해당 API는 대화창에 TAB, ENTER/ESC, 화살표 키 등의 동작을 컨트롤과 연동합니다. 그뿐만이 아닙니다. 대화창은 Edit Control의 경우 입력 포커스를 받을 때마다 입력된 텍스트를 모두 선택하는 부가 기능이 있습니다. 예를 들어, 아래의 화면은 Edit 컨트롤에 "test"를 입력 후 TAB 키를 눌러 버튼으로 포커스를 옮겼다가, 다시 TAB 키를 눌러 Edit 컨트롤에 포커스가 간 경우를 보여줍니다.<br /> <br /> <img alt='dlgcode_1.png' src='/SysWebRes/bbs/dlgcode_1.png' /><br /> <br /> 보다시피, "test" 내용이 모두 선택돼 있습니다. 문제는, 그렇게 추가된 부가 기능을 때로는 사용자가 원하지 않을 수도 있다는 점입니다. 일례로, 위의 대화창에서처럼 Edit 컨트롤이 포커스를 받았을 때 텍스트가 선택되지 않게 만들고 싶다면 어떻게 해야 할까요?<br /> <br /> 바로 그런 기능을 위해 컨트롤이 구현하게 되는 코드가 <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/dlgbox/wm-getdlgcode'>WM_GETDLGCODE</a> 메시지에 대한 반환값입니다. (WM_GETDLGCODE와 IsDialogMessage를 동일하게 Windows 2000 운영체제부터 지원하게 된 것은 우연이 아닙니다.) 기본적으로 Windows 2000 이후의 기본 컨트롤들은 <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/dlgbox/wm-getdlgcode#return-value'>WM_GETDLGCODE에 대한 반환값</a>이 그들만의 Window Procedure에 이미 구현돼 있습니다.<br /> <br /> 그러니까, (대화창에 있는) Edit 컨트롤의 경우 입력 포커스를 받았을 때 텍스트를 선택하도록 DLGC_HASSETSEL 값이 설정돼 있는데요, 정말 그런지 테스트를 해볼까요? ^^ <a target='tab' href='https://www.sysnet.pe.kr/2/0/13292'>지난번처럼</a> 비주얼 스튜디오로 만든 C++ Windows Application 프로젝트의 기본 대화상자를 다음과 같이 변경한 후,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // resource.h // #define IDC_EDIT1 110 IDD_ABOUTBOX DIALOGEX 0, 0, 170, 62 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About Project1" FONT 8, "MS Shell Dlg" BEGIN ICON IDR_MAINFRAME,IDC_STATIC,14,14,21,20 LTEXT "Project1, Version 1.0",IDC_STATIC,42,14,114,8,SS_NOPREFIX <span style='color: blue; font-weight: bold'>EDITTEXT IDC_EDIT1, 38, 24, 114, 15</span> DEFPUSHBUTTON "OK",IDOK,113,41,50,14,WS_GROUP END </pre> <br /> 간단하게 SendMessage를 대상 컨트롤에 전달 후 반환값을 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: { <span style='color: blue; font-weight: bold'>int result = ::SendMessage(GetDlgItem(hDlg, IDC_EDIT1), WM_GETDLGCODE, 0, 0); if ((result & DLGC_HASSETSEL) == DLGC_HASSETSEL) { ::MessageBox(hDlg, L"DLGC_HASSETSEL", L"DLGC_HASSETSEL", MB_OK); }</span> } case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; } </pre> <br /> DLGC_HASSETSEL 값이 설정된 것을 볼 수 있습니다. 그렇다면 ^^ 이 기능을 제거하는 것도 가능할 텐데요, 이에 대해서는 다음의 글에서 자세하게 설명하고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Preventing edit control text from being autoselected in a dialog box ; <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20031114-00/?p=41823'>https://devblogs.microsoft.com/oldnewthing/20031114-00/?p=41823</a> </pre> <br /> 따라서 우리도 Edit 컨트롤의 Window Procdefure를 subclassing하는 코드를 다음과 같이 구현해 주면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > #include <CommCtrl.h> #pragma comment(lib, "Comctl32.lib") // ...[생략]... LRESULT CALLBACK RemoveHasSetSelSubclassProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { switch (uiMsg) { case WM_NCDESTROY: RemoveWindowSubclass(hwnd, RemoveHasSetSelSubclassProc, uIdSubclass); break; <span style='color: blue; font-weight: bold'>case WM_GETDLGCODE: return DefSubclassProc(hwnd, uiMsg, wParam, lParam) & ~DLGC_HASSETSEL;</span> } return DefSubclassProc(hwnd, uiMsg, wParam, lParam); } INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: // <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20230323-00/?p=107962'>How unique must the uIdSubclass parameter be when I call SetWindowSubclass?</a> <span style='color: blue; font-weight: bold'>SetWindowSubclass(GetDlgItem(hDlg, IDC_EDIT1), RemoveHasSetSelSubclassProc, 0, 0);</span> return (INT_PTR)TRUE; // ...[생략]... } return (INT_PTR)FALSE; } </pre> <br /> 이후부터는, Edit 컨트롤이 TAB 키에 의해 입력 포커스를 받아도 입력해 두었던 텍스트가 선택되는 현상은 발생하지 않습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 또 다른 글을 통해,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Other tricks with WM_GETDLGCODE ; <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20031126-00/?p=41703'>https://devblogs.microsoft.com/oldnewthing/20031126-00/?p=41703</a> Microsoft KB 83302 ; <a target='tab' href='https://jeffpar.github.io/kbarchive/kb/083/Q83302/'>https://jeffpar.github.io/kbarchive/kb/083/Q83302/</a> </pre> <br /> DLGC_WANTMESSAGE 값에 대한 사용법을 볼 수 있습니다. 예를 들어, 대화창에서는 ESC 키를 누르면 대화창이 닫히게 되는데요, 만약 Edit 컨트롤에 입력 포커스가 있는 경우 그 동작을 막고 싶다면 다음과 같이 구현할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > LRESULT CALLBACK PreventESCKeySubclassProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { switch (uiMsg) { case WM_NCDESTROY: RemoveWindowSubclass(hwnd, PreventESCKeySubclassProc, uIdSubclass); break; case WM_GETDLGCODE: { <span style='color: blue; font-weight: bold'>LRESULT lRet = DefSubclassProc(hwnd, uiMsg, wParam, lParam); if (lParam) { LPMSG lpmsg = (LPMSG)lParam; if (lpmsg->wParam == VK_ESCAPE) { lRet |= DLGC_WANTMESSAGE; } } return lRet;</span> } } return DefSubclassProc(hwnd, uiMsg, wParam, lParam); } INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: { <span style='color: blue; font-weight: bold'>SetWindowSubclass(GetDlgItem(hDlg, IDC_EDIT1), PreventESCKeySubclassProc, 0, 0);</span> return (INT_PTR)TRUE; } // ...[생략]... } return (INT_PTR)FALSE; } </pre> <br /> 즉, 대화창의 부가 기능으로 제공되는 어떤 키도 필터링할 수 있는데요, 자주 쓸만한 키 같은 경우에는 저렇게 어렵게 구현하는 대신 DLGC_WANTCHARS, DLGC_WANTTAB, DLGC_WANTARROWS 플래그를 제공하고 있으니 사용하면 됩니다. (따지고 보면, 이러한 3가지 플래그들은 DLGC_WANTMESSAGE의 특수한 사례에 속합니다.)<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=2054&boardid=331301885ㄴ'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그렇다면, 저러한 동작들이 C#으로 만든 Windows Forms에서는 어떨까요? ^^ 정답 먼저 말하면, 윈폼 환경에서는 WM_GETDLGCODE 메시지가 아무런 역할도 하지 않습니다. 그 이유가 뭘까요? ^^<br /> <br /> 왜냐하면, WM_GETDLGCODE 기능은 IsDialogMessage와의 협업으로 이뤄지는 것인데, <a target='tab' href='https://www.sysnet.pe.kr/2/0/13290#winform_isdlgmsg'>Windows Forms의 경우에는 메시지 루프에서 IsDialogMessage를 호출하지 않고 독자적으로 편의 기능을 제공</a>하고 있기 때문입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > DLGC_BUTTON 0x2000 Button. DLGC_DEFPUSHBUTTON 0x0010 Default push button. DLGC_HASSETSEL 0x0008 EM_SETSEL messages. DLGC_RADIOBUTTON 0x0040 Radio button. DLGC_STATIC 0x0100 Static control. DLGC_UNDEFPUSHBUTTON 0x0020 Non-default push button. DLGC_WANTALLKEYS 0x0004 All keyboard input. DLGC_WANTARROWS 0x0001 Direction keys. DLGC_WANTCHARS 0x0080 WM_CHAR messages. DLGC_WANTMESSAGE 0x0004 All keyboard input (the application passes this message in the MSG structure to the control). DLGC_WANTTAB 0x0002 TAB key. </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1368
(왼쪽의 숫자를 입력해야 합니다.)