API를 사용하다 보면, 실패할 때가 있습니다.

대부분의 API는 반환 값이 0이면 실패한 것으로 간주하는데 (예외적으로 네이티브 API 등이 있습니다), 이럴 땐 GetLastError API, C#, VB.NET의 경우 Marshal.GetLastWin32Error 메서드를 이용할 수도 있고.. 해서 오류 코드를 가져올 수가 있습니다.


하지만, 이 오류 코드는 숫자에 불과할 뿐이지 오류 코드가 뭘 의미하는지는 인터넷을 봐야 알 수가 있습니다.

오류는 계속 발생하고, 코드는 뭔지 모를 숫자가 나오고.. 이럴 땐 정말 답답하기 그지 없습니다.


그럼 정작 이 오류 코드를 우리가 알아볼 수 있는 문자열로 바꿀 방법은 없는걸까요?

당연히~ 방법은 있습니다.

바로 FormatMessage 라는 API를 이용하면 됩니다.


바로 설명부터 하도록 해보죠.

아래의 코드는 정말 어처구니 없는 폴더를 만들려고 시도를 합니다.

using System;
using System.Runtime.InteropServices;

namespace ApiReference {
    class ApiExample {
        [DllImport("kernel32", CharSet = CharSet.Auto)]
        public static extern Int32 CreateDirectory(String PathName, IntPtr SecAttr);
        
        public static void Main(String[] args) {
            // 말도 안되는 폴더를 한번 생성해보죠.
            Int32 result = CreateDirectory(@"C:\h?ell*o? worl\\ld\\\\\\d\d\d?", IntPtr.Zero);
            Console.WriteLine("폴더 만들기 결과: {0}", result);
            Console.ReadKey(true);
        }
    }
}


C:\h?ell*o? worl\\ld\\\\\\d\d\d? 라는 이름을 가진 폴더를 만들 수 있을까요?

와일드 카드 문자인 물음표와 별표, 역슬래시를 포함한 참 말도안되는 폴더입니다.

그럼 결과는 어떻게 나올까요?



역시.. 예상한대로 결과는 0이 나옵니다. API 호출에 실패했다는 뜻이죠.


근데, 왜 실패했는지는 알 수가 없습니다.

그래서, 여기에 GetLastError API를 추가하고 마지막 오류 코드를 출력하도록 코드를 살짝 바꿔보았습니다.

using System;
using System.Runtime.InteropServices;

namespace ApiReference {
    class ApiExample {
        [DllImport("kernel32", CharSet = CharSet.Auto)]
        public static extern Int32 CreateDirectory(String PathName, IntPtr SecAttr);
        [DllImport("kernel32", CharSet = CharSet.Auto)]
        public static extern Int32 GetLastError();
        
        public static void Main(String[] args) {
            // 말도 안되는 폴더를 한번 생성해보죠.
            Int32 result = CreateDirectory(@"C:\h?ell*o? worl\\ld\\\\\\d\d\d?", IntPtr.Zero);
            Console.WriteLine("폴더 만들기 결과: {0}", result);
            Console.WriteLine("마지막 오류 코드: {0}"GetLastError());
            Console.ReadKey(true);
        }
    }
}


그럼 이제 좀 더 자세한 정보를 알 수가 있습니다. (그래봤자 숫자일 뿐이지만)


오류 코드는 123번 이라고 하네요. MSDN에서 한번 검색을 해보죠

MSDN에 따르면 이렇다고 합니다.

ERROR_INVALID_NAME

The filename, directory name, or volume label syntax is incorrect.


파일 이름, 디렉토리 이름 또는 볼륨 레이블 구문이 올바르지 않습니다. 라고 하네요

정확합니다. 말도안되는 폴더를 만들려고 했잖아요? ㅋㅋㅋ


근데 이 오류 코드가 나올때마다 일일히 검색하기엔 너무 힘듭니다... 

그래서! 이 글을 쓰게 된겁니다.


이제 코드에 FormatMessage API를 추가할 시간입니다.

그리고 함수 하나를 만들어요.

public static String GetErrorMessage(Int32 errcode) {
    String errmsg;
    FormatMessage(0x1300, IntPtr.Zero, errcode, 0x400out errmsg, 260, IntPtr.Zero);
    return errmsg;
}


GetErrorMessage 란 함수를 만드는데, 매개 변수는 정수형으로 하나,

FormatMessage API를 호출할 때 옵션을 0x1300으로 주었구요.

(0x1300 옵션은 FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_IGNORE_INSERTS, FORMAT_MESSAGE_ALLOCATE_BUFFER 이 세가지 입니다)

메세지 번호에는 오류 코드를, 언어 번호에는 0x400 (시스템 기본 언어)로 입력을 했습니다.


그럼 이 함수를 코드에 반영하고 호출까지 해서, 원하는 결과가 나오는지 확인해 보도록 하겠습니다.

using System;
using System.Runtime.InteropServices;

namespace ApiReference {
    class ApiExample {
        [DllImport("kernel32", CharSet = CharSet.Auto)]
        public static extern Int32 CreateDirectory(String PathName, IntPtr SecAttr);
        [DllImport("kernel32", CharSet = CharSet.Auto)]
        public static extern Int32 GetLastError();
        [DllImport("kernel32", CharSet = CharSet.Auto)]
        public static extern Int32 FormatMessage(Int32 dwFlags, IntPtr lpSource, Int32 dwMessageId, Int32 dwLanguageId, out String lpBuffer, Int32 dwSize, IntPtr lpArguments);
        
        public static void Main(String[] args) {
            // 말도 안되는 폴더를 한번 생성해보죠.
            Int32 result = CreateDirectory(@"C:\h?ell*o? worl\\ld\\\\\\d\d\d?", IntPtr.Zero);
            Console.WriteLine("폴더 만들기 결과: {0}", result);
            Int32 lec = GetLastError();
            Console.WriteLine("마지막 오류 코드: {0}", lec);
            Console.WriteLine("마지막 오류 코드에 대한 메세지: {0}"GetErrorMessage(lec));
            Console.ReadKey(true);
        }
        public static String GetErrorMessage(Int32 errcode) {
            String errmsg;
            FormatMessage(0x1300, IntPtr.Zero, errcode, 0x400out errmsg, 260, IntPtr.Zero);
            return errmsg;
        }
    }
}


결과는 어떨까요?


두구두구두구두ㅜ구두구두굳구둑둑두


짜쟌!! 오류코드 123번에 대한 오류 메세지가 정확하게 표시되는 것을 알 수 있습니다.

원래 VB.NET을 많이 썼는데, C#을 공부하기 위해서 요즘은 계속 C#을 주로 하고있네요..

VB.NET은 아는데 C# 모르는 분들께는 정말로 죄송할 따름입니다(ㅠㅠ)


가능하면 C#, VB.NET 두개를 같이 하려고 노력해보긴 할텐데.. 제가 써놓은 예제 코드를 C# 코드를 VB.NET 코드로 변환해주는 사이트에서 한번 돌리면 VB.NET 코드로 정확하게 나오게 됩니다~~


그러니... 다소 번거로우시겠지만, C#, VB.NET 두 언어가 제공되지 않을 때엔 알려드린 방법을 사용해주시기 바랍니다 (...)


긴 글 읽어주셔서 감사합니다!


+ Recent posts