성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
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 - Code Modal과 UI Modal</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;' > Win32 - modeless 대화창을 modal처럼 동작하게 만드는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13295'>https://www.sysnet.pe.kr/2/0/13295</a> </pre> <br /> 인용한 <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20050218-00/?p=36413'>Modality, part 1: UI-modality vs code-modality</a> 글은 modality라는 동작에 대해 UI-modal과 code-modal로 나누고 있습니다.<br /> <br /> 우선 code-modal은 우리가 그동안 <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-dialogboxw'>DialogBox</a> API를 이용해 호출한 modal 대화창의 동작 방식일 텐데요, 좀 더 엄밀히 말하면 메시지 루프를 (중첩시켜) 만들어 블록킹을 시키면 그것을 code-modal이라고 지칭하고 있습니다.<br /> <br /> 반면 UI-modal은 "<a target='tab' href='https://www.sysnet.pe.kr/2/0/13295'>Win32 - modeless 대화창을 modal처럼 동작하게 만드는 방법</a>" 글에서 든 예제와 같이 어떤 식으로든 UI가 modal로 동작하는 것을 의미합니다.<br /> <br /> 그러니까, 지난 예제는 (중첩 메시지 루프의) code-modal을 사용하지 않은 UI-modal을 만든 것인데요, Modality 2번째 글에서는,<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 2: Code-modality vs UI-modality ; <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20050221-00/?p=36403'>https://devblogs.microsoft.com/oldnewthing/20050221-00/?p=36403</a> </pre> <br /> 그 반대의 경우로, UI-modal 없이 (중첩 메시지 루프의) code-modal을 사용하는 것도 가능하다고 합니다. 실제로 후자의 경우가 더 흔한 사례라고 하는데요, 가령 스크롤바의 thumb 버튼을 드래그하는 중에, 윈도우의 캡션 바를 잡고 드래그하는 중에, 팝업 메뉴를 표시하는 중에, OLE drag/drop 작업을 개시할 때와 같은 상황에서 (중첩 메시지 루프의) code-modal을 활용합니다.<br /> <br /> 실제로 MsgWaitForMultipleObjects를 이용한 code-modal을 구현하는 예제를 링크하고 있는데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > One example is given in MSDN, ; (broken) http://msdn.microsoft.com/library/en-us/dllproc/base/waiting_in_a_message_loop.asp </pre> <br /> 현재는 없는 자료입니다. ^^ 단지 인터넷에서 "<a target='tab' href='https://blog.naver.com/tingkr/140014453503'>waiting in a message loop</a>"로 소스코드를 구할 수 있는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int MessageLoop ( HANDLE* lphObjects, // handles that need to be waited on int cObjects // number of handles to wait on ) { // The message loop lasts until we get a WM_QUIT message, // upon which we shall return from the function. while (TRUE) { // block-local variable DWORD result ; MSG msg ; // Read all of the messages in this next loop, // removing each message as we read it. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // If it is a quit message, exit. if (msg.message == WM_QUIT) return 1; // Otherwise, dispatch the message. DispatchMessage(&msg); } // End of PeekMessage while loop. // Wait for any message sent or posted to this queue // or for one of the passed handles be set to signaled. result = MsgWaitForMultipleObjects(cObjects, lphObjects, FALSE, INFINITE, QS_ALLINPUT); // The result tells us the type of event we have. if (result == (WAIT_OBJECT_0 + cObjects)) { // New messages have arrived. // Continue to the top of the always while loop to // dispatch them and resume waiting. continue; } else { // One of the handles became signaled. DoStuff (result - WAIT_OBJECT_0) ; } // End of else clause. } // End of the always while loop. } // End of function. </pre> <br /> 위의 코드를 보면 PeekMessage를 이용해 메시지 큐에 있는 모든 메시지를 소진한 다음 <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-msgwaitformultipleobjects'>MsgWaitForMultipleObjects</a>를 호출하는 구조로, 이에 대한 이유를 다음의 글에서 자세하게 설명하고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > MsgWaitForMultipleObjects and the queue state ; <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20050217-00/?p=36423'>https://devblogs.microsoft.com/oldnewthing/20050217-00/?p=36423</a> </pre> <br /> 윈도우 메시지가 큐에 있는 경우, MsgWaitForMultipleObjects를 호출하면 신규로 들어오는 메시지에 대해서만 함수가 반환을 한다고 합니다. 따라서 메시지 큐에 처리되지 않은 메시지가 있어도 MsgWaitForMultipleObjects 호출 시 곧바로 반환하지는 않습니다.<br /> <br /> MsgWaitForMultipleObjects로 기존 메시지를 처리하는 것도 가능한데, 다음과 같은 코딩이라면 메시지 처리에 문제가 되지는 않습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > bool hasMsg = NativeMethods.PeekMessage(out MSG msg2, IntPtr.Zero, 0, 0, PM_NOREMOVE); if (hasMsg == false) { // ...[이 사이에 메시지가 post된 경우]... // ... 아래의 MsgWaitForMultipleObjects는 곧바로 반환 NativeMethods.MsgWaitForMultipleObjects(cObjects, null, true, -1, <span style='color: blue; font-weight: bold'>QS_ALLPOSTMESSAGE</span>); } </pre> <br /> 주의해야 할 점은, 위의 PeekMessage가 true를 반환했다면 그것을 별도로 처리해야 합니다. 왜냐하면 PeekMessage로 확인했다는 것은 그 시점까지 메시지 큐의 내용을 체크했다는 플래그가 설정돼 이후의 MsgWaitForMultipleObjects는 마찬가지로 신규 메시지만을 처리하기 때문입니다. 좀 뭔가 애매하죠?<br /> <br /> 이후 <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-msgwaitformultipleobjectsex'>MsgWaitForMultipleObjectsEx</a>는 MWMO_INPUTAVAILABLE 플래그를 둬서 이런 문제를 해결합니다. 그래서 예전에 만들어 둔 메시지 루프를,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - Console 응용 프로그램에서 UI 스레드 구현 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12139'>https://www.sysnet.pe.kr/2/0/12139</a> </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;' > while (true) { try { uint cObjects = (uint)_pHandles.Length; uint result = <span style='color: blue; font-weight: bold'>NativeMethods.MsgWaitForMultipleObjectsEx</span>(cObjects, _pHandles, 0xFFFFFFFF, QS_ALLPOSTMESSAGE, <span style='color: blue; font-weight: bold'>MWMO_INPUTAVAILABLE</span>); if (result == cObjects) { <span style='color: blue; font-weight: bold'>NativeMethods.GetMessage</span>(out MSG msg, IntPtr.Zero, 0, 0); WindowProc(msg); MessageArrived?.Invoke(this, new MessageEventArgs(msg)); switch (msg.message) { case (uint)Win32Message.WM_QUIT: case (uint)Win32Message.WM_CLOSE: OnClose(); return; } <span style='color: blue; font-weight: bold'>NativeMethods.DispatchMessage</span>(ref msg); } else { OnClose(); return; } } finally { _ewh_Sync.Set(); } } </pre> <br /> _pHandles에 대기해야 할 커널 개체가 없다면, 그냥 바로 GetMessage를 호출하도록 바꾸면 됩니다.<br /> <br /> 참고로, 위의 코드에서 제가 PeekMessage(out MSG msg, IntPtr.Zero, 0, 0, PM_REMOVE); 대신 GetMessage를 사용했는데요, 이게 약간 묘하게 다른 점이 있습니다. 위의 메시지 루프를 동일하게 사용한다고 했을 때, 다음과 같이 사용 코드를 만들면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using CustomMessageLoop; class Program { // Install-Package CustomMessageLoop static void Main(string[] args) { using (MessageLoop mml = new MessageLoop()) { ThreadPool.QueueUserWorkItem((arg) => { Thread.Sleep(1000); mml.PostMessage(Win32Message.WM_TIMER); mml.PostMessage(Win32Message.WM_TIMER); mml.PostMessage(Win32Message.WM_TIMER); mml.PostMessage(Win32Message.WM_TIMER); mml.PostMessage(Win32Message.WM_TIMER); Thread.Sleep(500); mml.PostMessage(Win32Message.WM_CLOSE); }); mml.MessageArrived += Mml_MessageArrived; mml.Run(); mml.WaitForExit(); } Console.WriteLine("Press any key to exit..."); Console.ReadLine(); } private static void Mml_MessageArrived(object? sender, MessageEventArgs e) { Console.WriteLine(e.Message.message); } } </pre> <br /> 분명히 5번의 WM_TIMER를 보냈지만 어쩌다 한 번씩 6번의 Console.WriteLine 출력이 나오게 됩니다. 반면 GetMessage로 처리한 경우에는 그런 현상이 없고.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
7734
(왼쪽의 숫자를 입력해야 합니다.)