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

First-chance exception에 대해 조건에 따라 디버거가 멈추게 할 수는 없을까?

제목이 좀 난해한데요. ^^ 쉽게 이해하기 위해 예를 한번 들어볼까요?

가령 다음과 같은 코드를 작성한 후,

class Program
{
    static void Main(string[] args)
    {
        string host = "www.microsoft11111.com";
        Console.WriteLine(host + ": " + ConnectTest(host));
    }

    static bool ConnectTest(string host)
    {
        try
        {
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Connect(host, 80);

            socket.Close();
            return true;
        }
        catch { }

        return false;
    }
}

Visual Studio에서 "F5" 키를 눌러 디버깅을 시작하면 정상적으로 프로그램이 종료되는 것을 확인할 수 있습니다. 물론, "Output" 창에는 다음과 같은 예외 기록이 남습니다.

A first chance exception of type 'System.Net.Sockets.SocketException' occurred in System.dll

프로젝트가 커지면서 예외에 대한 try/catch를 남발하다 보면, 위와 같이 "first chance exception"이 발생하는 경우에 저 오류가 과연 아무런 영향이 없는 예외인지, 아니면 '의도치 않게 삼켜진 예외'인지 구분하기가 매우 어렵게 됩니다.

다행히, Visual Studio에서는 개발자가 try/catch로 처리했을지라도 예외가 발생한 경우 디버거로 하여금 Break가 되도록 하는 방법이 제공됩니다. 이에 대해서는 저번에 한번 설명을 드렸지요. ^^

First-Chance Exception
; https://www.sysnet.pe.kr/2/0/510

위의 마지막 부분에 설명한 데로, "Debug" / "Exceptions..." 메뉴로 "System.Net.Sockets.SocketException" 예외에 대해서 "Thrown" 상태를 설정해 두면 이후부터는 디버깅 시에 이런 예외가 발생하면 Visual Studio는 여지없이 디버거를 정지시키고 해당 예외가 발생한 라인에 멈추게 됩니다. 멋지죠? ^^




개인적으로, 저는 명시적으로 "Thrown" 설정을 해서 First-chance exception에 대한 예외를 어떻게든 없애는 방향으로 프로젝트를 진행합니다. 왜냐하면, 그렇지 않으면 나중에 프로젝트가 커져서 어떤 기능이 안될 때 도대체 왜 그 기능이 '아무런 오류도 없이 안되는지' 그 오류를 찾기 힘들기 때문입니다.

그런데, 그렇게 하다 보면 문제가 생기는 경우가 있습니다.

예를 들면, 위의 소켓 예제 코드에서 특정 서버에 대한 연결 가능 여부를 5초마다 체크하도록 했다고 가정해 보겠습니다.

ThreadPool.QueueUserWorkItem( (WaitCallback)(
    (obj) =>
        {
            while (true)
            {
                bool result = ConnectTest("www.rjrejfs.com");
                if (result == false)
                {
                    Console.WriteLine("Disconnected");
                }

                Thread.Sleep(5000);
            }
        }
    ));

만약, 위의 코드를 Visual Studio의 디버깅 상태에서 System.Net.Sockets.SocketException 예외에 대해 "Thrown" 상태로 두고 돌리면 5초마다 Visual Studio가 해당 소스 코드 라인에서 멈추는 부작용(!)이 생깁니다.

이 불편함을 없애려면 "System.Net.Sockets.SocketException" 예외의 "Thrown" 상태를 꺼야 하는데, 그렇게 되면 꼭 필요하게 체크해야 할 동일 예외에 대한 진단을 할 수 없게 됩니다.

바로 이럴 때, 특정 상황에서 발생하는 예외에 대해서는 "Thrown" 상태를 켜 두었어도 Visual Studio에서 멈추지 않게 하고 싶어지는데요. 혹시... 이에 대한 해답이 떠 오르는 분이 계실런지요? ^^

저는 한가지 생각났습니다. 바로, DebuggerNonUserCode / DebuggerStepThrough / DebuggerHidden 특성을 적절하게 사용하는 것입니다. 이를 반영해서 최종적으로 코드를 만들어 보면, 대충 다음과 같이 됩니다.

class Program
{
    static void Main(string[] args)
    {
        string host = "www.microsoft.com";
        Console.WriteLine(host + ": " + ConnectTest(host, true));

        ThreadPool.QueueUserWorkItem( (WaitCallback)(
            (obj) =>
                {
                    while (true)
                    {
                        bool result = ConnectTest("www.rjrejfs.com", false);
                        if (result == false)
                        {
                            Console.WriteLine("Disconnected");
                        }

                        Thread.Sleep(5000);
                    }
                }
            ));

        Console.ReadLine();
    }

    static bool ConnectTest(string host, bool thrown)
    {
        try
        {
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            if (thrown == true)
            {
                socket.Connect(host, 80);
            }
            else
            {
                if (ConnectNotThrown(socket, host, 80) == false)
                {
                    return false;
                }
            }

            socket.Close();
            return true;
        }
        catch { }

        return false;
    }

    [DebuggerHidden]
    static bool ConnectNotThrown(Socket socket, string host, int port)
    {
        try
        {
            socket.Connect(host, port);
            return true;
        }
        catch { }

        return false;
    }
}

위의 코드를 보면, 혹시나 연결이 안되었을 때 예외가 발생하기를 기대하는 코드에서는 ConnectTest(..., true)를 호출하고, 그렇지 않고 일상적으로 발생할 수 있거나 자주 발생해서 디버깅에 장애가 될 경우라면 ConnectTest(..., false)를 호출하는 것으로 해결하고 있습니다.

나름 유용하게 써 먹고 있는 팁입니다. ^^ (첨부된 파일은 위의 소스 코드가 포함된 간단한 프로젝트입니다.)

참고로, 위의 용도로는 DebuggerNonUserCode / DebuggerStepThrough / DebuggerHidden 특성 중에서 어느 것을 사용해도 무방하지만 다른 경우에서는 각각의 분리된 용도를 가지고 있는데요. 이에 대해서는 다음의 글을 참고하시면 되겠습니다. ^^

Change Debugger behavior with Attributes
; http://blogs.msdn.com/b/stevejs/archive/2005/12/03/499803.aspx






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







[최초 등록일: ]
[최종 수정일: 5/21/2021]

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

비밀번호

댓글 작성자
 



2012-06-05 03시56분
참고로, "Enable Just My Code (Managed only)" 옵션이 꺼져 있다면 위와 같은 상황에서도 Visual Studio에서 예외 발생에 대한 BP가 잡힙니다.
정성태

... 136  137  138  139  140  141  142  143  [144]  145  146  147  148  149  150  ...
NoWriterDateCnt.TitleFile(s)
1453정성태5/22/201325221기타: 32. Microsoft FTP 사이트에 접속하는 방법
1452정성태5/21/201332934Windows: 73. TabProcGrowth 값 삭제 후 IE를 실행시키면 다시 복원되는 경우 [3]
1451정성태5/17/201331870Windows: 72. 윈도우 서버 2012 기초 사용법
1450정성태5/16/201322697오류 유형: 176. SQL10007N Message "0" could not be retrieved. Reason code: "3"
1449정성태5/15/201329793오류 유형: 175. SpeechRecognitionEngine 사용 시 오류 유형 2가지
1448정성태5/14/201324775VC++: 68. #pragma warning(disable: ...)로 오류 제어가 안된다면?
1447정성태5/3/201326450개발 환경 구성: 191. Debugging Tools for Windows 독립 설치 버전 [1]
1446정성태4/30/201327205.NET Framework: 368. Encoding 타입의 대체(fallback) 메카니즘 [1]
1445정성태4/26/201325445디버깅 기술: 54. NT 서비스의 Main 메서드 안에서 Process.GetProcessesByName 호출 시 멈춤 현상 [1]
1444정성태4/26/201329477기타: 31. Internet Explorer: 자바스크립트로 숨겨진 파일 다운로드 경로를 알아내는 방법 [1]
1443정성태4/24/201325132개발 환경 구성: 190. Azure PaaS 웹 응용 프로그램 배포 후 SMTP 서버 구성 [2]
1442정성태4/21/201328719기타: 30. 마이크로소프트 워드의 CPU 점유 현상으로 글자 입력이 느려졌다면? [1]
1441정성태4/21/201335320.NET Framework: 367. LargeAddressAware 옵션이 적용된 닷넷 32비트 프로세스의 가용 메모리 [14]
1440정성태4/19/201324039오류 유형: 174. dumpbin.exe 실행시 mspdb110.dll 로드 오류
1439정성태4/18/201327914VS.NET IDE: 76. Visual Studio 2012와 Itanium 빌드 옵션 [2]
1438정성태4/17/201327298.NET Framework: 366. 다른 프로세스에 환경 변수 설정하는 방법 - 두 번째 이야기 [1]파일 다운로드1
1437정성태4/17/201327524VC++: 67. CRT(C Runtime DLL: msvcr...dll)에 대한 의존성 제거
1436정성태4/17/201332947.NET Framework: 365. Local SYSTEM 권한으로 코드를 실행하는 방법파일 다운로드1
1435정성태4/15/201341831Windows: 71. ad-hoc 보다 더 편리한 "가상 Wifi" 를 이용한 인터넷 공유 [2]
1434정성태4/9/201323106오류 유형: 173. TFS 서버의 이벤트 로그 오류 - WebHost failed to process a request. Parameter name: certificate
1433정성태4/9/201323386개발 환경 구성: 189. TFS에 설치된 SharePoint 의 PowerShell 콘솔 띄우는 방법
1432정성태4/5/201324398오류 유형: 172. System.Web.PipelineModuleStepContainer.GetEventCount 에서 NullReferenceException 이 발생한다면?
1431정성태4/5/201325054기타: 29. 부팅 가능한 (외장) HDD를 기존 부팅 메뉴에 추가하는 방법
1430정성태4/4/201326874제니퍼 .NET: 23. 모바일용 웹 사이트에서 발생하는 응답 시간 지연 현상 [5]파일 다운로드1
1429정성태3/29/201323253개발 환경 구성: 188. SCOM 2012 - ASP.NET 모니터링 방법
1428정성태3/29/201324094개발 환경 구성: 187. SCOM 2012 환경 구성 - Management Packs
... 136  137  138  139  140  141  142  143  [144]  145  146  147  148  149  150  ...