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

비밀번호

댓글 작성자
 



2025-05-09 08시10분
오래된 소프트웨어는 보안 위협이 되기도 합니다. 혹시 어떤 기능 때문에 이 소프트웨어를 사용하시는지 여쭤봐도 될까요?
이종효
2025-05-09 09시23분
그냥 RSS Reader 기능과 약간의 UI 편의성 때문에 사용합니다. ^^ 간혹, 온라인 RSS Reader를 소개해 주신 분도 있었는데 웬일인지 얼마 가지 않아 사이트 폐쇄를 하는 바람에 딱히 이젠 바꿀 노력도 안 하게 됐습니다. ^^;
정성태

... 136  [137]  138  139  140  141  142  143  144  145  146  147  148  149  150  ...
NoWriterDateCnt.TitleFile(s)
1635정성태2/14/201422738오류 유형: 221. SharePoint - System.InvalidOperationException: The farm is unavailable.
1634정성태2/14/201422963.NET Framework: 424. C# - CSharpCodeProvider로 컴파일한 메서드의 실행이 일반 메서드보다 더 빠르다? [1]파일 다운로드1
1633정성태2/13/201425789오류 유형: 220. 2014년 2월 13일 이후로 Visual Studio 2010 Macro가 동작하지 않는다면? [3]
1632정성태2/12/201443770.NET Framework: 423. C#에서 DirectShow를 이용한 미디어 재생 [2]파일 다운로드1
1631정성태2/11/201422706개발 환경 구성: 217. Realtek 사운드 장치에서 재생되는 오디오를 GraphEditor로 녹음하는 방법
1630정성태2/5/201422987개발 환경 구성: 216. Hyper-V에 올려진 윈도우 XP VM에서 24bit 컬러 및 ClearType 활성화하는 방법
1629정성태2/5/201432843개발 환경 구성: 215. DOS batch - 하나의 .bat 파일에서 다중 .bat 파일을 (비동기로) 실행하는 방법 [1]
1628정성태2/4/201434152Windows: 87. 윈도우 8.1에서 .NET 3.5 설치가 안된다면? [2]
1627정성태2/4/201429226개발 환경 구성: 214. SQL Server Reporting Services를 이용해 간단한 리포트 제작하는 방법
1626정성태2/4/201421248Windows: 86. 윈도우 8.1의 Skydrive 내용이 동기화가 안된다면?
1625정성태2/2/201428450.NET Framework: 422. C++과 C#의 Event 공유파일 다운로드1
1624정성태2/2/201424066.NET Framework: 421. ASP.NET에서 Server.CreateObject와 COM Interop 클래스 생성의 차이점
1623정성태2/1/201428782개발 환경 구성: 213. x86/x64별로 나뉘어진 어셈블리를 한 프로젝트에서 참조하는 방법 [1]파일 다운로드1
1622정성태1/31/201429310VC++: 74. 어떤 것을 쓰면 좋을까요? wvnsprintf, _vsnwprintf_s, StringCbVPrintfW [4]
1621정성태1/31/201421061.NET Framework: 420. 베트남의 11학년(한국의 고2)이 45분만에 푼다는 알고리즘 문제파일 다운로드1
1620정성태1/30/201430949.NET Framework: 419. C# - BigDecimal파일 다운로드1
1619정성태1/30/201427658VS.NET IDE: 85. T4를 이용한 INotifyPropertyChanged 코드 자동 생성파일 다운로드1
1618정성태1/29/201443334Linux: 2. 우분투에서 Active Directory 계정을 이용한 파일 공유
1617정성태1/29/201424480.NET Framework: 418. Thread.Abort 호출의 hang 현상 [1]
1616정성태1/29/201425171디버깅 기술: 63. windbg 디버깅 사례: AppDomain 간의 static 변수 사용으로 인한 crash
1615정성태1/29/201427045.NET Framework: 417. WPF WebBrowser 컨트롤에서 SHDocVw.IWebBrowser2 인터페이스를 구하는 방법 및 순수 WPF 웹 브라우저 컨트롤 소개
1614정성태1/29/201424027.NET Framework: 416. System.Net.Sockets.NetworkStream이 Thread-safe할까?파일 다운로드1
1613정성태1/29/201426070.NET Framework: 415. IIS 작업자 프로세스 재생(recycle)하는 방법 [1]
1612정성태1/29/201422795오류 유형: 219. IIS 500 Internal Server Error - Skydrive에 공유된 경우
1611정성태1/27/201454227.NET Framework: 414. C# - 컴퓨터에서 알아낼 수 있는 고윳값 정리 [3]파일 다운로드1
1610정성태1/26/201438154.NET Framework: 413. C# - chromiumembedded 사용 [11]파일 다운로드1
... 136  [137]  138  139  140  141  142  143  144  145  146  147  148  149  150  ...