성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
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'>Win32 - modeless 대화창을 modal처럼 동작하게 만드는 방법</h1> <p> 지난 글에서 modal과 modeless 대화창을 만드는 방법을 설명했는데요,<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> Win32 C/C++ - Dialog에서 값을 반환하는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13284'>https://www.sysnet.pe.kr/2/0/13284</a> </pre> <br /> 그런데 어떤 경우에는 위와 같은 변환이 불가능한 경우도 있습니다. 일례로 Windows가 제공하는 <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/commdlg/nf-commdlg-findtextw'>FindText</a> API로 호출하는 대화창이 그렇습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - 윈도우에서 기본 제공하는 FindText 대화창 사용법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13290'>https://www.sysnet.pe.kr/2/0/13290</a> // FindText를 C/C++에서 사용하는 방법: <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20050218-00/?p=36413'>Modality, part 1: UI-modality vs code-modality</a> </pre> <br /> FindText API로 호출하는 대화창을, Modal로 만들기 위해 <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-dialogboxw'>DialogBox</a> API로 대체할 수는 없습니다. 대신 다른 방법이 있는데요, 바로 그것을 아래의 글에서 설명하고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Modality, part 1: UI-modality vs code-modality ; <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20050218-00/?p=36413'>https://devblogs.microsoft.com/oldnewthing/20050218-00/?p=36413</a> </pre> <br /> 즉 Find 대화창을 생성한 다음 곧바로 부모 창을 Disable 상태로 만들고, 대화창이 종료되면 다시 Enable 상태로 만들어주면 되는 것입니다. (물론, 그와 함께 <a target='tab' href='https://www.sysnet.pe.kr/2/0/13292'>IsDialogMessage</a> 호출도 추가해야 합니다.)<br /> <br /> 실제로 <a target='tab' href='https://www.sysnet.pe.kr/2/0/13290'>지난 글의 C# 코드</a>로도 위의 규칙대로 변경을 해보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private void Form1_Load(object sender, EventArgs e) { _dlg = new FindTextDialog(this.Handle); _dlg.CreateDialog(); <span style='color: blue; font-weight: bold'>this.Enabled = false;</span> } private void button1_Click(object sender, EventArgs e) { _dlg.CreateDialog(); <span style='color: blue; font-weight: bold'>this.Enabled = false;</span> } unsafe void OnFindReplace(IntPtr hwnd, ref FINDREPLACEW pfr) { if (_dlg.HasCloseFlag()) { <span style='color: blue; font-weight: bold'>this.Enabled = true;</span> _dlg.Close(); return; } if (_dlg.HasFindNextFlag()) { string text = Marshal.PtrToStringAuto(pfr.lpstrFindWhat) ?? ""; MessageBox.Show($"{_dlg.Text}, {text}"); } } </pre> <br /> Find 대화창이 (동일하지는 않지만) 마치 Modal처럼 동작하는 것을 확인할 수 있습니다. 단지, <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-dialogboxw'>DialogBox</a> API를 사용하는 것과는 다르게 위의 방법은 부모 윈도우와 그 자식 컨트롤 모두를 Disable 상태로 만들게 된다는 차이점이 있습니다. (이것은 Windows Forms의 동작과 Win32 API인 EnableWindow의 동작에 차이가 있기 때문입니다.)<br /> <br /> <hr style='width: 50%' /><br /> <a name='enable_order'></a> <br /> 재미있게도, 위와 같이 modeless를 modal로 띄우는 경우 코딩 순서가 중요하다고 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > The correct order for disabling and enabling windows ; <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20040227-00/?p=40463'>https://devblogs.microsoft.com/oldnewthing/20040227-00/?p=40463</a> </pre> <br /> Modal 처리를 할 때는,<br /> <br /> <ol> <li>Owner를 비활성화시키고,</li> <li>Modal 대화창을 활성화</li> </ol> <br /> 순으로 처리하고, Modal 대화창을 닫을 때는 다음과 같이 해야 합니다.<br /> <br /> <ol> <li>Owner를 활성화</li> <li>Modal 대화창을 닫고</li> </ol> <br /> 예를 들어, 위에서 닫는 처리를 2번 먼저 하고 1번을 하게 되면 데스크톱에서 마지막으로 포커스를 가졌던 다른 창이 활성화되는 것을 볼 수 있습니다. 실제로 이에 대한 테스트를 "<a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20050218-00/?p=36413'>Modality, part 1: UI-modality vs code-modality</a>" 글의 예제 코드로 할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > void OnFindReplace(HWND hwnd, FINDREPLACE *pfr) { if (pfr->Flags & FR_DIALOGTERM) { <span style='color: blue; font-weight: bold'>EnableWindow(hwnd, TRUE); DestroyWindow(g_hwndFR);</span> g_hwndFR = NULL; } } </pre> <br /> 만약 위의 코드를 다음과 같이 바꾸면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > DestroyWindow(g_hwndFR); EnableWindow(hwnd, TRUE); </pre> <br /> (다른 창을 의도적으로 한번 클릭한 후 Modal을 띄운 경우) Modal을 닫을 때 그것의 부모창이 포커스를 받는 것이 아니고, 마지막으로 클릭했었던 윈도우가 포커스를 받게 됩니다. 이런 현상이 나타나는 이유는, (대화)창이 종료하면서 그것의 소유자에게 포커스를 주려다가, 그 소유자가 disable 상태이기 때문에 Window Manager는 입력 포커스를 줄 다른 (Enable 상태의) 윈도우를 찾게 된다고 합니다.<br /> <br /> 위와 같은 현상은, <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-destroywindow'>DestoryWindow</a>를 이용해 창을 닫는 코드라면 C#에서도 동일하게 재현됩니다.<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=2053&boardid=331301885'>첨부 파일은 이 글의 코드를 포함</a>합니다.)<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;' > To enable and disable a window, use the EnableWindow function ; <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20100719-00/?p=13403'>https://devblogs.microsoft.com/oldnewthing/20100719-00/?p=13403</a> </pre> <br /> 윈도우 활성/비활성을 제어하기 위해 EnableWindow API 대신, <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlongw'>SetWindowLong</a> 함수로 <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/winmsg/window-styles'>WS_DISABLED</a> 스타일을 직접 변경하는 것은 내부 변수의 값을 바꾸는 것에 지나지 않으므로 WM_ENABLE 메시지가 발생하진 않는다고 합니다. 당연히 그 외의 절차인 accessibility 관련 알림도 없고, 입력 포커스 조정 과정도 거치지 않게 됩니다. 이와 마찬가지로 ShowWindow API 대신 WS_VISIBLE 플래그를 직접 변경하는 식으로 사용하는 것도 역시 부작용이 있다고 합니다. (게다가, <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/winmsg/window-styles?redirectedfrom=MSDN'>이런 내용은 문서에도 적혀 있다</a>고. ^^)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
9104
(왼쪽의 숫자를 입력해야 합니다.)