Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)
(시리즈 글이 13개 있습니다.)
.NET Framework: 397. C# - OCX 컨트롤에 구현된 메서드에 배열을 in, out으로 전달하는 방법
; https://www.sysnet.pe.kr/2/0/1547

.NET Framework: 652. C# 개발자를 위한 C++ COM 객체의 기본 구현 방식 설명
; https://www.sysnet.pe.kr/2/0/11175

.NET Framework: 792. C# COM 서버가 제공하는 COM 이벤트를 C++에서 받는 방법
; https://www.sysnet.pe.kr/2/0/11679

.NET Framework: 907. C# DLL로부터 TLB 및 C/C++ 헤더 파일(TLH)을 생성하는 방법
; https://www.sysnet.pe.kr/2/0/12220

.NET Framework: 977. C# PInvoke - C++의 매개변수에 대한 마샬링을 tlbexp.exe를 이용해 확인하는 방법
; https://www.sysnet.pe.kr/2/0/12443

.NET Framework: 1008. 배열을 반환하는 C# COM 개체의 메서드를 C++에서 사용 시 메모리 누수 현상
; https://www.sysnet.pe.kr/2/0/12491

.NET Framework: 1064. C# COM 개체를 PIA(Primary Interop Assembly)로써 "Embed Interop Types" 참조하는 방법
; https://www.sysnet.pe.kr/2/0/12662

.NET Framework: 1069. C# - DLL Surrogate를 이용한 Out-of-process COM 개체 제작
; https://www.sysnet.pe.kr/2/0/12668

.NET Framework: 1095. C# COM 개체를 C++에서 사용하는 예제
; https://www.sysnet.pe.kr/2/0/12791

.NET Framework: 2003. C# - COM 개체의 이벤트 핸들러에서 발생하는 예외에 대한 CLR의 특별 대우
; https://www.sysnet.pe.kr/2/0/13050

닷넷: 2177. C# - (Interop DLL 없이) CoClass를 이용한 COM 개체 생성 방법
; https://www.sysnet.pe.kr/2/0/13469

닷넷: 2248. C# - 인터페이스 타입의 다중 포인터를 인자로 갖는 C/C++ 함수 연동
; https://www.sysnet.pe.kr/2/0/13607

닷넷: 2254. C# - COM 인터페이스의 상속 시 중복으로 메서드를 선언
; https://www.sysnet.pe.kr/2/0/13614




C# - COM 개체의 이벤트 핸들러에서 발생하는 예외에 대한 CLR의 특별 대우

재미있는 질문이 있었습니다.

UI 스레드 관련 질문드립니다.
; https://www.sysnet.pe.kr/3/0/5655

저도 그동안 인지를 못했었는데, 웹 브라우저의 DocumentCompleted 이벤트 핸들러에서 발생한 예외의 경우 catch를 하지 않아도 응용 프로그램은 종료하지 않습니다. 즉, 내부적으로 예외를 무시하기 때문에 Application.ThreadException에서도 잡히지 않는 것입니다.

이에 대한 테스트는 다음과 같이 간단하게 할 수 있습니다.

using System;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            throw new ApplicationException("TEST");
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.webBrowser1.Navigate("https://www.naver.com");
        }
    }
}

저 프로그램을 실행시켜 button1을 누르면 DocumentCompleted가 호출되면서 예외가 발생할 텐데, 저렇게 try/catch로 처리하지 않아도 응용 프로그램은 종료하지 않습니다. 도대체 여기에는 어떤 특별함이 있을까요?

우선, DocumentCompleted 콜백이 발생했을 때의 호출 스택을 보면,

>    WindowsFormsApp1.exe!WindowsFormsApp1.Form1.webBrowser1_DocumentCompleted(object sender, System.Windows.Forms.WebBrowserDocumentCompletedEventArgs e) Line 17   C#  Symbols loaded.
    System.Windows.Forms.dll!System.Windows.Forms.WebBrowser.OnDocumentCompleted(System.Windows.Forms.WebBrowserDocumentCompletedEventArgs e)   Unknown Non-user code. Skipped loading symbols.
    [Native to Managed Transition]      Annotated Frame
    [Managed to Native Transition]      Annotated Frame
    [Native to Managed Transition]      Annotated Frame
    [Managed to Native Transition]      Annotated Frame
    System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(System.IntPtr dwComponentID, int reason, int pvLoopData)  Unknown Non-user code. Skipped loading symbols.
    System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason, System.Windows.Forms.ApplicationContext context)    Unknown Non-user code. Skipped loading symbols.
    System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context) Unknown Non-user code. Skipped loading symbols.
    WindowsFormsApp1.exe!WindowsFormsApp1.Program.Main() Line 20    C#  Symbols loaded.

중간에 Interop Ax DLL에 해당하는 OnDocumentCompleted의 역어셈블 코드를 봐도 별도의 try/catch는 없습니다. 그리고 그 하위에 CCW/RCW로 예측되는 Transition 계층이 나오고, 마지막의 System.Windows.Forms 호출 스택은 일반적인 메시지 루프에 불과합니다.

그렇다면, 이제 문제를 살짝 좁힐 수 있습니다. 즉, 예외를 무시하도록 COM 개체 측에서 처리했거나, 아니면 Transition 계층에서 그렇게 처리하고 있는 것입니다. 과연 어느 쪽일까요? ^^




혹시 WebBrowser와 같은 ActiveX는 마이크로소프트가 만들었기 때문에 예외 처리를 잘 하고 있는 것이 아닐까요? 이에 대한 테스트를 위해 간단한 COM 개체를 만들고,

// ...[생략]...

// 참고: https://www.sysnet.pe.kr/2/0/588
interface IATLSimpleObject : IDispatch
{
    [id(1)] HRESULT DoMethod([in] LONG lParam);
};

dispinterface _IATLSimpleObjectEvents
{
    properties:
    methods:
        [id(1)] HRESULT DoCallback();
};

// ...[생략]...

// ...[생략]...

STDMETHODIMP CATLSimpleObject::DoMethod(LONG lParam)
{
    Fire_DoCallback();
    return S_OK;
}

C# 측에서는 DoMethod 코드를 호출해 DoCallback 이벤트를 수신하는 코드를 다음과 같이 작성해 줍니다.

private void Form1_Load(object sender, EventArgs e)
{
    Guid objGuid = new Guid("{043c73f1-f1c6-4004-8c8a-c14901b492da}");
    Type type = Type.GetTypeFromCLSID(objGuid);

    object obj = Activator.CreateInstance(type);

    IConnectionPointContainer connectionPoint = obj as IConnectionPointContainer;

    IConnectionPoint point;
    Guid guid = new Guid("{c8fadc19-aec4-48e1-997c-6167b80b07c9}");

    connectionPoint.FindConnectionPoint(ref guid, out point);

    int dwCookie;
    point.Advise(this, out dwCookie);

    dynamic dn = obj;
    dn.DoMethod(5);
}

public void DoCallback()
{
    throw new ApplicationException("TEST");
}

그럼, Form1.Load가 실행되면서 COM 개체를 생성해 DoMethod를 호출하는데, C++ COM의 DoMethod 내부에서는 DoCallback 이벤트를 발생시키므로 WinForm 측에서 지정한 DoCallback 이벤트 핸들러 코드가 실행됩니다.

확인 결과, 이렇게 간단한 COM 개체조차도 이벤트 핸들러에서의 "throw" 예외 발생은 Windows Forms의 실행을 종료시키지 못했습니다. 따라서, 아마도 CCW/RCW 계층에서 예외를 무시하는 코드가 있을 거라는... 짐작이 가능합니다.

(이 글의 예제 코드는 첨부 파일에 있습니다.)




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 5/7/2022]

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

비밀번호

댓글 작성자
 




... 76  77  78  79  80  81  82  [83]  84  85  86  87  88  89  90  ...
NoWriterDateCnt.TitleFile(s)
11862정성태4/7/201920186개발 환경 구성: 437. .NET EXE의 ASLR 기능을 끄는 방법
11861정성태4/6/201919630디버깅 기술: 126. windbg - .NET x86 CLR2/CLR4 EXE의 EntryPoint
11860정성태4/5/201923511오류 유형: 527. Visual C++ 컴파일 오류 - error C2220: warning treated as error - no 'object' file generated
11859정성태4/4/201920737디버깅 기술: 125. WinDbg로 EXE의 EntryPoint에서 BP 거는 방법
11858정성태3/27/201921634VC++: 129. EXE를 LoadLibrary로 로딩해 PE 헤더에 있는 EntryPoint를 직접 호출하는 방법파일 다운로드1
11857정성태3/26/201919532VC++: 128. strncpy 사용 시 주의 사항(Linux / Windows)
11856정성태3/25/201919778VS.NET IDE: 134. 마이크로소프트의 CoreCLR 프로파일러 리눅스 예제를 Visual Studio F5 원격 디버깅하는 방법 [1]파일 다운로드1
11855정성태3/25/201921955개발 환경 구성: 436. 페이스북 HTTPS 인증을 localhost에서 테스트하는 방법
11854정성태3/25/201917613VS.NET IDE: 133. IIS Express로 호스팅하는 사이트를 https로 접근하는 방법
11853정성태3/24/201920408개발 환경 구성: 435. 존재하지 않는 IP 주소에 대한 Dns.GetHostByAddress/gethostbyaddr/GetNameInfoW 실행이 느리다면? - 두 번째 이야기 [1]
11852정성태3/20/201919624개발 환경 구성: 434. 존재하지 않는 IP 주소에 대한 Dns.GetHostByAddress/gethostbyaddr/GetNameInfoW 실행이 느리다면?파일 다운로드1
11851정성태3/19/201923375Linux: 8. C# - 리눅스 환경에서 DllImport 대신 라이브러리 동적 로드 처리 [2]
11850정성태3/18/201922435.NET Framework: 813. C# async 메서드에서 out/ref/in 유형의 인자를 사용하지 못하는 이유
11849정성태3/18/201921777.NET Framework: 812. pscp.exe 기능을 C#으로 제어하는 방법파일 다운로드1
11848정성태3/17/201918523스크립트: 14. 윈도우 CMD - 파일이 변경된 경우 파일명을 변경해 복사하고 싶다면?
11847정성태3/17/201922995Linux: 7. 리눅스 C/C++ - 공유 라이브러리 동적 로딩 후 export 함수 사용 방법파일 다운로드1
11846정성태3/15/201921646Linux: 6. getenv, setenv가 언어/운영체제마다 호환이 안 되는 문제
11845정성태3/15/201921770Linux: 5. Linux 응용 프로그램의 (C++) so 의존성 줄이기(ReleaseMinDependency) [3]
11844정성태3/14/201923103개발 환경 구성: 434. Visual Studio 2019 - 리눅스 프로젝트를 이용한 공유/실행(so/out) 프로그램 개발 환경 설정 [1]파일 다운로드1
11843정성태3/14/201918039기타: 75. MSDN 웹 사이트를 기본으로 영문 페이지로 열고 싶다면?
11842정성태3/13/201916397개발 환경 구성: 433. 마이크로소프트의 CoreCLR 프로파일러 예제를 Visual Studio CMake로 빌드하는 방법 [1]파일 다운로드1
11841정성태3/13/201916698VS.NET IDE: 132. Visual Studio 2019 - CMake의 컴파일러를 기본 g++에서 clang++로 변경
11840정성태3/13/201918321오류 유형: 526. 윈도우 10 Ubuntu App 환경에서는 USB 외장 하드 접근 불가
11839정성태3/12/201922279디버깅 기술: 124. .NET Core 웹 앱을 호스팅하는 Azure App Services의 프로세스 메모리 덤프 및 windbg 분석 개요 [3]
11838정성태3/7/201925893.NET Framework: 811. (번역글) .NET Internals Cookbook Part 1 - Exceptions, filters and corrupted processes [1]파일 다운로드1
11837정성태3/6/201939822기타: 74. 도서: 시작하세요! C# 7.3 프로그래밍 [10]
... 76  77  78  79  80  81  82  [83]  84  85  86  87  88  89  90  ...