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, 0x400, out 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, 0x400, out 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 두 언어가 제공되지 않을 때엔 알려드린 방법을 사용해주시기 바랍니다 (...)
긴 글 읽어주셔서 감사합니다!
'.NET' 카테고리의 다른 글
.NET에서 API 선언 없이 간단하게 특권(Privilege) 활성화/비활성화 시키기. (0) | 2014.10.08 |
---|---|
Win32Exception - 참으로 친절한 예외 클래스 (1) | 2014.09.30 |
'크로스 스레드 작업이 잘못되었습니다' - 넌 왜 나타나서 날 괴롭게 하니.. (3) | 2014.09.29 |
ArgIterator? 이 구조체는 어디에 사용되는 녀석일까? (0) | 2014.09.28 |
PInvoke(Platform Invoke)를 사용해보자! (2) | 2014.09.08 |