Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일

C# - (JetBrains Omea Reader 대상으로) 런타임 시에 메서드 가로채기

아직도 저는 ^^; .NET 1.1로 만들어진 "JetBrains Omea Reader"를 사용합니다. (독립 실행형 프로그램으로 제공하는 RSS Reader 중에 이만한 것을 찾지 못했습니다.)

그런데, 최근에 겪은 문제점이라면... 종료가 되지 않는 현상이 가끔씩 발생한다는 것입니다. 즉, Close 버튼을 누르면 대기 창이 하나 뜨더니 종료하지 못하고 초 시계만 마냥 돌고 있는 것입니다.

jb_omea_reader_hang_1.png

이 상태에서 WinDbg를 이용해 연결해 보면, 다음의 호출에서 멈춰 있는 것을 볼 수 있습니다.

// 윈도우 응용 프로그램의 경우, 높은 확률로 0번 스레드가 UI 스레드이므로!
0:024> ~0s
eax=00000000 ebx=000d167c ecx=00000000 edx=00000000 esi=00000000 edi=000c1b30
eip=74f511dc esp=0019d354 ebp=0019d398 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200202
win32u!NtUserWaitMessage+0xc:
74f511dc c3              ret

0:000> !clrstack
OS Thread Id: 0x3688 (0)
Child SP       IP Call Site
0019e0f0 77b15e3c [HelperMethodFrame_1OBJ: 0019e0f0] System.Threading.Thread.JoinInternal(Int32)
0019e174 04c12aff *** WARNING: Unable to verify checksum for OmniaMeaBase.dll
JetBrains.Omea.AsyncProcessing.AsyncProcessor.WaitUntilFinished()
0019e180 04c12a76 JetBrains.Omea.AsyncProcessing.AsyncProcessor.Dispose()
0019e190 04c1217c JetBrains.Omea.MainFrame.h()
0019e43c 73d32516 [DebuggerU2MCatchHandlerFrame: 0019e43c] 
0019e294 73d32516 [HelperMethodFrame_PROTECTOBJ: 0019e294] System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean)
0019e4e0 714ca58a System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(System.Object, System.Object[], System.Object[])
0019e504 7140d61d System.Delegate.DynamicInvokeImpl(System.Object[])
0019e518 0a024241 JetBrains.Omea.AsyncProcessing.DelegateJob.Execute()
0019e540 0a0241ac JetBrains.Omea.MainFrame.t(System.Object, System.EventArgs)
0019e54c 04c11ee0 JetBrains.Omea.ProgressWindow.b()
0019e558 5a890c30 *** WARNING: Unable to verify checksum for System.Windows.Forms.ni.dll
*** WARNING: Unable to verify checksum for System.ni.dll
System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry)
0019e574 5a890b78 System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(System.Object)
0019e59c 71408e34 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
0019e608 71408d67 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
0019e61c 71408d24 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
0019e634 5a890b0a System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry)
0019e64c 5a8908aa System.Windows.Forms.Control.InvokeMarshaledCallbacks()
0019e68c 5a87fd10 System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)
0019e690 5a889581 [InlinedCallFrame: 0019e690] 
0019e6d8 5a889581 System.Windows.Forms.ScrollableControl.WndProc(System.Windows.Forms.Message ByRef)
0019e6e4 5a889247 System.Windows.Forms.Form.WndProc(System.Windows.Forms.Message ByRef)
0019e6f8 5a87fbe3 System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)
0019e700 5a87fbc9 System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)
0019e714 5a87f500 System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)
0019e950 0218d08e [InlinedCallFrame: 0019e950] 
0019e94c 5a8e4f6d DomainBoundILStubClass.IL_STUB_PInvoke(MSG ByRef)
0019e950 5a89020b [InlinedCallFrame: 0019e950] System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG ByRef)
0019e984 5a89020b System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr, Int32, Int32)
0019e988 5a88fdf9 [InlinedCallFrame: 0019e988] 
0019ea10 5a88fdf9 System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)
0019ea60 5a88fc4f System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)
0019ea8c 5af64033 System.Windows.Forms.Application.RunDialog(System.Windows.Forms.Form)
0019eaa0 5af6d52d System.Windows.Forms.Form.ShowDialog(System.Windows.Forms.IWin32Window)
0019eaa4 04c1185b [InlinedCallFrame: 0019eaa4] 
0019eb48 04c1185b JetBrains.Omea.MainFrame.a(System.String, Boolean, System.Delegate, System.Object[])
0019eb6c 04c1178a JetBrains.Omea.MainFrame.n(System.Object, System.EventArgs)
0019eb90 5af6b253 System.Windows.Forms.Form.OnClosed(System.EventArgs)
0019eba4 5af6e522 System.Windows.Forms.Form.WmClose(System.Windows.Forms.Message ByRef)
0019ebe8 5b32a238 System.Windows.Forms.Form.WndProc(System.Windows.Forms.Message ByRef)
0019ebfc 0c456d5c JetBrains.Omea.MainFrame.WndProc(System.Windows.Forms.Message ByRef)
0019ec20 5a87fbe3 System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)
0019ec28 5a87fbc9 System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)
0019ec3c 5a87f500 System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)
0019eeb0 0218d08e [InlinedCallFrame: 0019eeb0] 
0019eeac 5a8e2dbd DomainBoundILStubClass.IL_STUB_PInvoke(System.Runtime.InteropServices.HandleRef, Int32, Int32, Int32)
0019eeb0 5a893c5b [InlinedCallFrame: 0019eeb0] System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef, Int32, Int32, Int32)
0019eeec 5a893c5b System.Windows.Forms.Control.SendMessage(Int32, Int32, Int32)
0019ef04 5af6a416 System.Windows.Forms.Form.Close()
0019ef14 04c1166e JetBrains.Omea.ExitAction.Execute(JetBrains.Omea.OpenAPI.IActionContext)
0019ef1c 04c115f3 JetBrains.Omea.MenuActionManager.a(System.Object, System.EventArgs)
0019ef4c 5b13586e System.Windows.Forms.MenuItem.OnClick(System.EventArgs)
0019ef60 5b20a0ac System.Windows.Forms.MenuItem+MenuItemData.Execute()
0019ef6c 5b0b8519 System.Windows.Forms.Command.Invoke()
0019ef78 5b0b8301 System.Windows.Forms.Command.DispatchID(Int32)
0019ef80 5b32d1db System.Windows.Forms.Control.WmCommand(System.Windows.Forms.Message ByRef)
0019ef90 5a87ff77 System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)
0019ef94 5a889581 [InlinedCallFrame: 0019ef94] 
0019efdc 5a889581 System.Windows.Forms.ScrollableControl.WndProc(System.Windows.Forms.Message ByRef)
0019efe8 5a889247 System.Windows.Forms.Form.WndProc(System.Windows.Forms.Message ByRef)
0019effc 0c456d5c JetBrains.Omea.MainFrame.WndProc(System.Windows.Forms.Message ByRef)
0019f020 5a87fbe3 System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)
0019f028 5a87fbc9 System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)
0019f03c 5a87f500 System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)
0019f278 0218d08e [InlinedCallFrame: 0019f278] 
0019f274 5a8e4f6d DomainBoundILStubClass.IL_STUB_PInvoke(MSG ByRef)
0019f278 5a89020b [InlinedCallFrame: 0019f278] System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG ByRef)
0019f2ac 5a89020b System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr, Int32, Int32)
0019f2b0 5a88fdf9 [InlinedCallFrame: 0019f2b0] 
0019f338 5a88fdf9 System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)
0019f388 5a88fc4f System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)
0019f3b4 0aedeb1c JetBrains.Omea.AsyncProcessing.AsyncProcessor.MsgWaitForMultipleObjects(IntPtr[], Int32, UInt32)
0019f3d4 0eec7f52 JetBrains.Omea.AsyncProcessing.AsyncProcessor.MsgWaitForMultipleObjects(Int32, Int32)
0019f3f0 0aedbd2a JetBrains.Omea.AsyncProcessing.AsyncProcessor.WaitEvents(Int32)
0019f410 0aedb213 JetBrains.Omea.AsyncProcessing.AsyncProcessor.DoJobs()
0019f444 0eec7ea3 JetBrains.Omea.MainFrame+b.h()
0019f464 0aeda90e JetBrains.Omea.AsyncProcessing.AsyncProcessor.ThreadFunction()
0019f498 0eec7e51 JetBrains.Omea.AsyncProcessing.AsyncProcessor.EmployCurrentThread()
0019f4a4 0eec7dbc JetBrains.Omea.MainFrame+b.e()
0019f4d4 02212300 JetBrains.Omea.MainFrame.ad()
0019f67c 73d32516 [GCFrame: 0019f67c] 

호출 스택을 보면, 결국 OmniaMeaBase.dll의 AsyncProcessor.WaitUntilFinished 메서드에서 멈춰 있다는 것인데요, 해당 메서드를 역어셈블하면 다음의 코드가 나옵니다.

// JetBrains.Omea.AsyncProcessing.AsyncProcessor
// Token: 0x060000E7 RID: 231 RVA: 0x00006C64 File Offset: 0x00005C64
public void WaitUntilFinished()
{
    if (this._isThreadStarted != 0)
    {
        if (!Application.MessageLoop)
        {
            this._processorThread.Join();
        }
        else
        {
            while (this._processorThread.IsAlive && !this._processorThread.Join(100))
            {
                Application.DoEvents();
            }
        }
    }
}

자발적인 종료가 안 돼 결국엔 작업 관리자를 이용해 강제 종료를 하게 되는데요, 그렇다면 저 코드는 사실상 (현실적으로 어차피 강제 종료해야 하므로) 필요 없는 단계에 속합니다.

그러니, 단순하게 다음의 정도만을 수행하게 바꿔도 될 것 같은데요,

public void WaitUntilFinished()
{
    Thread.Join(5000);
}

지난번처럼, 이를 위해 dnSpy를 사용하려고 했지만 아쉽게도 이번에는 OmniaMeaBase.dll의 전체적인 코드가 난독화 컴파일돼 있어 dnSpy의 IL 코드 어셈블로는 정상적으로 재컴파일이 안 됩니다.

따라서 이런 경우라면 .NET Profiler를 이용한 IL rewrite 기법을 사용해야 합니다.




한 가지 다행인 점은, Omea Reader의 경우 Plug-in 확장을 지원한다는 것입니다. 그래서 임의로 DLL을 만들어 "Tools" / "Options" 메뉴의 "Plugins"에 등록하면 런타임 시에 우리가 원하는 코드를 실행할 수 있습니다.

따라서 이전에 쓴 내용을 바탕으로,

C# - 실행 시에 메서드 가로채기 (.NET Framework 4.8)
; https://www.sysnet.pe.kr/2/0/13909

.NET Framework 2.0 DLL 프로젝트를 만들고 다음의 코드를 작성해 문제를 우회할 수 있습니다.

using JetBrains.Omea.OpenAPI;
using System;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Windows.Forms;

public class Class1 : IPlugin, IResourceDisplayer, IResourceTextProvider
{
    public IDisplayPane CreateDisplayPane(string resourceType)
    {
        return null;
    }

    public bool ProcessResourceText(IResource res, IResourceTextConsumer consumer)
    {
        return false;
    }

    public void Register()
    {
    }

    public void Shutdown()
    {
    }

    static int _replaced = 0;

    public void Startup()
    {
        if (Interlocked.Exchange(ref _replaced, 1) == 1)
        {
            return;
        }

        MethodInfo targetMethod = this.GetType().GetMethod("WaitUntilFinished");

        foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
        {
            try
            {
                if (Path.GetFileName(asm.Location).Equals("OmniaMeaBase.dll", StringComparison.OrdinalIgnoreCase) == true)
                {
                    Type type = asm.GetType("JetBrains.Omea.AsyncProcessing.AsyncProcessor");
                    if (type == null)
                    {
                        return;
                    }

                    MethodInfo mi = type.GetMethod("WaitUntilFinished", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
                    if (mi == null)
                    {
                        return;
                    }

                    // C# - 실행 시에 메서드 가로채기 (.NET Framework 4.8)
                    // https://www.sysnet.pe.kr/2/0/13909
                    ReplaceMethod.Replace(mi, targetMethod);
                }
            }
            catch { }
        }
    }

    public void WaitUntilFinished()
    {
        // https://stackoverflow.com/questions/14522540/how-to-close-a-messagebox-after-several-seconds
        var owner = new Form { TopMost = true };

        ThreadPool.QueueUserWorkItem((arg) =>
        {
            Thread.Sleep(2000);
            owner.Invoke(
                (WaitCallback)
                (   (cb) =>
                    {
                        if (!owner.IsDisposed)
                        {
                            owner.Close();
                        }
                    }
                )
            );
        });

        var dialogRes = MessageBox.Show(owner, "Waiting...", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }
}

이후로는, 몇 주간 사용해 봤는데 모두 잘 종료했습니다. ^^

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 5/8/2025]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13924정성태5/8/2025153닷넷: 2332. C# - (JetBrains Omea Reader 대상으로) 런타임 시에 메서드 가로채기파일 다운로드1
13923정성태5/5/2025397스크립트: 74. 파이썬 - C# - Python.NET의 RunSimpleScript, Exec, Eval 차이점파일 다운로드1
13922정성태5/3/2025589스크립트: 73. 파이썬 - Windows embeddable package 버전에서 tkinter 환경 구성
13921정성태5/3/2025750오류 유형: 952. 듀얼 채널 메모리 정렬을 지키지 않은 컴퓨터의 Windows 비정상 종료 현상(Blue Screen) [2]
13920정성태5/3/20251091오류 유형: 951. Typed DataSet 생성 중 "Failed to open a connection to the database" 오류
13919정성태5/2/2025869VS.NET IDE: 201. C# - Typed DataSet(XSD)를 위한 연결 문자열 암호화 [1]파일 다운로드1
13918정성태5/2/20251161VS.NET IDE: 200. C# - app.config 파일의 출력을 Configuration(Debug/Release)에 따라 제어하는 방법파일 다운로드1
13917정성태4/30/2025833VS.NET IDE: 199. Directory.Build.props에 정의한 속성에 대해 Condition 제약으로 값을 변경하는 방법
13916정성태4/23/2025917디버깅 기술: 221. WinDbg 분석 사례 - ASP.NET HttpCookieCollection을 다중 스레드에서 사용할 경우 무한 루프 현상 - 두 번째 이야기
13915정성태4/13/20251940닷넷: 2331. C# - 실행 시에 메서드 가로채기 (.NET 9)파일 다운로드1
13914정성태4/11/20252218디버깅 기술: 220. windbg 분석 사례 - x86 ASP.NET 웹 응용 프로그램의 CPU 100% 현상 (4)
13913정성태4/10/20251429오류 유형: 950. Process Explorer - 64비트 윈도우에서 32비트 프로세스의 덤프를 뜰 때 "Error writing dump file: Access is denied." 오류
13912정성태4/9/20251122닷넷: 2330. C# - 실행 시에 메서드 가로채기 (.NET 5 ~ .NET 8)파일 다운로드1
13911정성태4/8/20251330오류 유형: 949. WinDbg - .NET Core/5+ 응용 프로그램 디버깅 시 sos 확장을 자동으로 로드하지 못하는 문제
13910정성태4/8/20251514디버깅 기술: 219. WinDbg - 명령어 내에서 환경 변수 사용법
13909정성태4/7/20252038닷넷: 2329. C# - 실행 시에 메서드 가로채기 (.NET Framework 4.8)파일 다운로드1
13908정성태4/2/20252543닷넷: 2328. C# - MailKit: SMTP, POP3, IMAP 지원 라이브러리
13907정성태3/29/20252621VS.NET IDE: 198. (OneDrive, Dropbox 등의 공유 디렉터리에 있는) C# 프로젝트의 출력 경로 변경하기
13906정성태3/27/20252811닷넷: 2327. C# - 초기화되지 않은 메모리에 접근하는 버그?파일 다운로드1
13905정성태3/26/20252715Windows: 281. C++ - Windows / Critical Section의 안정화를 위해 도입된 "Keyed Event"파일 다운로드1
13904정성태3/25/20252247디버깅 기술: 218. Windbg로 살펴보는 Win32 Critical Section파일 다운로드1
13903정성태3/24/20251753VS.NET IDE: 197. (OneDrive, Dropbox 등의 공유 디렉터리에 있는) C++ 프로젝트의 출력 경로 변경하기
13902정성태3/24/20252045개발 환경 구성: 742. Oracle - 테스트용 hr 계정 및 데이터 생성파일 다운로드1
13901정성태3/9/20252338Windows: 280. Hyper-V의 3가지 Thread Scheduler (Classic, Core, Root)
13900정성태3/8/20252590스크립트: 72. 파이썬 - SQLAlchemy + oracledb 연동
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...