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)
1351정성태9/13/201228112디버깅 기술: 49. windbg - .NET Framework 스레드 개체의 COM Apartment 유형 확인하는 방법
1350정성태9/12/201228794개발 환경 구성: 167. (실은) 무료가 아니었던 AWS EC2 서비스 [4]
1349정성태9/11/201260506VS.NET IDE: 74. Visual Studio의 '새 파일'을 UTF-8 인코딩으로 지정하는 방법 [4]
1348정성태9/11/201227976오류 유형: 164. Active Directory - Functional Level 승격이 안 되는 문제
1347정성태9/10/201230403Windows: 62. 윈도우 서버 2012 - Hyper-V 서버 마이그레이션 [1]
1346정성태9/10/201231248Windows: 61. 윈도우 서버 2012 - Active Directory 서버 마이그레이션
1345정성태9/10/201235277스크립트: 12. 파이썬 - Win32 DLL 연동 [2]
1344정성태9/10/201228438오류 유형: 163. .NET Framework 4.5 제거 후 Visual Studio 2010 실행 시 Unknown Error
1343정성태9/8/201242210스크립트: 11. 파이썬(Python) 윈도우 개발 환경 [7]
1342정성태9/6/201226404VS.NET IDE: 73. Visual Studio 2012 - XmlCodeGenerator 마이그레이션
1341정성태9/4/201235714Windows: 60. Hyper-V에서 RemoteFX 없이 DirectX 11 제공 [12]
1340정성태9/4/201227895개발 환경 구성: 166. DOS - ping 결과에서 평균 응답 시간값 추출하기 [3]
1339정성태9/4/201230334개발 환경 구성: 165. 새로운 Visual Studio 2012 원격 디버깅 툴 [5]
1338정성태9/4/201232137.NET Framework: 335. C# - (핸들을 이용하여) 모든 열린 파일을 열람 [6]파일 다운로드1
1337정성태8/30/201221956Phone: 7. 디버거로 실습해 보는 윈도우 폰의 Tombstone 상태파일 다운로드1
1336정성태8/30/201239994.NET Framework: 334. 스레드 비정상 종료로 발생하는 CLOSE_WAIT 소켓 상태 [2]파일 다운로드1
1335정성태8/30/201228758Windows: 59. Hyper-V Internal 네트워크 VM의 인터넷 접속
1334정성태8/29/201248074.NET Framework: 333. 코드로 재현하는 소켓 상태(FIN_WAIT1, FIN_WAIT2, TIME_WAIT, CLOSE_WAIT, LAST_WAIT) [6]
1333정성태8/27/201251478개발 환경 구성: 164. system32 폴더에 있는 파일의 권한 조정 [2]
1332정성태8/23/201223378Team Foundation Server: 48. TFS - Team Project Collection 이전하는 방법
1331정성태8/23/201226576오류 유형: 162. Database '...' already exists. Choose a different database name. (Microsoft SQL Server, Error: 1801)
1330정성태8/22/201227339Team Foundation Server: 47. 5인 이내의 팀, 또는 개인 로컬 소스 관리를 위한 무료 TFS Express
1329정성태8/21/201222750오류 유형: 161. Azure - Storage 삭제가 안되는 경우 [1]
1328정성태8/20/201233135개발 환경 구성: 163. IIS 7 - "MIME Types" 설정 아이콘이 없는 경우
1327정성태8/19/201237900Windows: 58. Windows 8 정식 버전을 설치해 보고... [14]
1326정성태8/19/201224218오류 유형: 160. Visual Studio 2010 Team Explorer 설치 오류
... 136  137  138  139  140  141  142  143  144  145  146  147  [148]  149  150  ...