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를 소개해 주신 분도 있었는데 웬일인지 얼마 가지 않아 사이트 폐쇄를 하는 바람에 딱히 이젠 바꿀 노력도 안 하게 됐습니다. ^^;
정성태

... 46  47  48  49  50  51  [52]  53  54  55  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12642정성태5/14/202118789오류 유형: 717. The 'Microsoft.ACE.OLEDB.12.0' provider is not registered on the local machine.
12641정성태5/13/202117605디버깅 기술: 179. 윈도우용 .NET Core 3 이상에서 Windbg의 sos 사용법
12640정성태5/13/202121348오류 유형: 716. RDP 연결 - Because of a protocol error (code: 0x112f), the remote session will be disconnected. [1]
12639정성태5/12/202117721오류 유형: 715. Arduino: Open Serial Monitor - The module '...\detection.node' was compiled against a different Node.js version using NODE_MODULE_VERSION
12638정성태5/12/202117763사물인터넷: 63. NodeMCU v1 ESP8266 - 펌웨어 내 파일 시스템(SPIFFS, LittleFS) 및 EEPROM 활용
12637정성태5/10/202118170사물인터넷: 62. NodeMCU v1 ESP8266 보드의 A0 핀에 다중 아날로그 센서 연결 [1]
12636정성태5/10/202118438사물인터넷: 61. NodeMCU v1 ESP8266 보드의 A0 핀 사용법 - FSR-402 아날로그 압력 센서 연동파일 다운로드1
12635정성태5/9/202116555기타: 81. OpenTabletDriver를 (관리자 권한으로 실행하지 않고도) 관리자 권한의 프로그램에서 동작하게 만드는 방법
12634정성태5/9/202115068개발 환경 구성: 572. .NET에서의 필수 무결성 제어 - 외부 Manifest 파일을 두는 방법파일 다운로드1
12633정성태5/7/202118101개발 환경 구성: 571. UAC - 관리자 권한 없이 UIPI 제약을 없애는 방법
12632정성태5/7/202119205기타: 80. (WACOM도 지원하는) Tablet 공통 디바이스 드라이버 - OpenTabletDriver
12631정성태5/5/202118100사물인터넷: 60. ThingSpeak 사물인터넷 플랫폼에 ESP8266 NodeMCU v1 + 조도 센서 장비 연동파일 다운로드1
12630정성태5/5/202118845사물인터넷: 59. NodeMCU v1 ESP8266 보드의 A0 핀 사용법 - CdS Cell(GL3526) 조도 센서 연동파일 다운로드1
12629정성태5/5/202120651.NET Framework: 1057. C# - CoAP 서버 및 클라이언트 제작 (UDP 소켓 통신) [1]파일 다운로드1
12628정성태5/4/202118613Linux: 39. Eclipse 원격 디버깅 - Cannot run program "gdb": Launching failed
12627정성태5/4/202118599Linux: 38. 라즈베리 파이 제로 용 프로그램 개발을 위한 Eclipse C/C++ 윈도우 환경 설정
12626정성태5/3/202118753.NET Framework: 1056. C# - Thread.Suspend 호출 시 응용 프로그램 hang 현상 (2)파일 다운로드1
12625정성태5/3/202117124오류 유형: 714. error CS5001: Program does not contain a static 'Main' method suitable for an entry point
12624정성태5/2/202121652.NET Framework: 1055. C# - struct/class가 스택/힙에 할당되는 사례 정리 [10]파일 다운로드1
12623정성태5/2/202117990.NET Framework: 1054. C# 9 최상위 문에 STAThread 사용 [1]파일 다운로드1
12622정성태5/2/202113782오류 유형: 713. XSD 파일을 포함한 프로젝트 - The type or namespace name 'TypedTableBase<>' does not exist in the namespace 'System.Data'
12621정성태5/1/202118650.NET Framework: 1053. C# - 특정 레지스트리 변경 시 알림을 받는 방법 [1]파일 다운로드1
12620정성태4/29/202121981.NET Framework: 1052. C# - 왜 구조체는 16 바이트의 크기가 적합한가? [1]파일 다운로드1
12619정성태4/28/202121976.NET Framework: 1051. C# - 구조체의 크기가 16바이트가 넘어가면 힙에 할당된다? [2]파일 다운로드1
12618정성태4/27/202120183사물인터넷: 58. NodeMCU v1 ESP8266 CP2102 Module을 이용한 WiFi UDP 통신 [1]파일 다운로드1
12617정성태4/26/202117357.NET Framework: 1050. C# - ETW EventListener의 Keywords별 EventId에 따른 필터링 방법파일 다운로드1
... 46  47  48  49  50  51  [52]  53  54  55  56  57  58  59  60  ...