.NET 환경에서는 함수와 상수, 구조체 등이 정의된 헤더 파일이 존재하지 않기 때문에 사람들이 .NET 에 맞게 작성해놓은 것을 보거나 pinvoke.net 같은 사이트에서 검색을 해야합니다.


하지만, 문서화되지 않았거나 사람들이 작성해놓은 것이 없을 경우엔 매우 난처한 상황에 놓이게 됩니다.

그럼 이런 상황을 어떻게 타파할 수 있을지 한번 보도록 하죠!


우선, C/C++에서 사용되는 자료형을 보겠습니다.

(왜 C/C++이냐면, 사용하는 Windows API의 정의는 대부분이 C/C++로 짜여져있기 때문입니다.)


 형식 이름

 정의

 크기

 .NET 자료형

 CHAR

 char

 1 Byte

 Char, SByte

 UCHAR, BYTE

 unsigned char

 1 Byte

 Byte

 WORD, USHORT

 unsigned short

 2 Bytes

 UInt16

 DWORD, ULONG, UINT

 unsigned long

 4 Bytes

 UInt32

 DWORDLONG, ULONGLONG

 unsigned long long

 8 Bytes

 UInt64

 ULONG_PTR

 unsigned long*

 4 / 8 Bytes

 UIntPtr

 SHORT

 short

 2 Bytes

 Int16

 INT, LONG

 int

 4 Bytes Int32
 LONGLONG

 long long

 8 Bytes Int64
 LONG_PTR long* 4 / 8 Bytes IntPtr

 LPSTR,LPCSTR, LPWSTR 등

 char*, const char* 등

 -

 String 또는 StringBuilder

 FLOAT, REAL

 float 4 Bytes Single
 DOUBLE double

 8 Bytes

 Double

위의 표에 정의되지 않은 자료형도 있겠지만, 일반적으로 사용되는 자료형은 표에 모두 적어보았습니다.

그리고, 포인터 식이 있는 HFILE, HANDLE 등의 모든 형식들은 IntPtr 로 통일하시면 됩니다.

(누락된게 있었네요. BOOL은 Boolean 입니다)


이렇게 하면 일단 90%는 완료했다고 봐도 무방합니다.

그 다음은 매개 변수의 자료형 앞에 붙는 수식어들을 처리할 때입니다.


수식어는 보통 _In_, _Out_, _In_opt_, _Out_opt_, _Opt_, _Inout_ 를 주로 사용합니다.


_In_ 의 경우는 크게 살펴볼 필요가 없습니다.

값을 전달하기만 하는 매개변수로써 정의할 때 추가로 써줘야하는 키워드는 없습니다.


_Out_ 의 경우는 C#에선 out 키워드를, VB에선 ByRef 키워드를 사용해야 합니다.

전달하는 매개변수에 값이 저장될 때 사용됩니다.

out 키워드를 사용하지 않는다면, 결과가 제대로 나오지 않을 뿐더러 PInvokeStackImbalance 예외가 발생할 수 있습니다.


_In_opt_ 의 경우 _In_ 과 다르지 않습니다.

매개변수가 선택적일 경우에 사용됩니다. (그렇다고 해서 Optional 키워드를 사용하는건 아닙니다)


_Opt_ 의 경우에도 _In_ 과 같이 사용하시면 되겠습니다.


_Out_opt_ 의 경우 좀 까다롭습니다.

VB의 경우 ByRef 키워드를 사용한 매개변수에도 Nothing을 전달할 수 있어서 불편함이 없지만,

C#의 경우 out 키워드를 사용하게 되면 null을 입력할 수가 없어서 변수를 선언해야 합니다.

하지만, 변수가 사용되지 않아도 상관은 없습니다.


_Out_ 의 경우 마찬가지로 out 키워드를 사용합니다.


_Inout_ 의 경우엔 ref 키워드를 사용합니다. VB는 ByRef 사용하면 됩니다.



out, ref, ByRef, ByVal 이 키워드는 뭘 하는 것일까

VB부터 살펴보도록 하겠습니다.

VB에서는 ByVal, ByRef 이 두 키워드로 매개변수를 전달하는 방법을 구현하는데요.

ByVal의 경우 값에 의한 전달(By Value), ByRef의 경우 참조에 의한 전달(By Reference)이라 칭합니다.

ByVal 로 전달할 경우 속도가 빠르지만, 함수 내부에서 값을 변경할 수 없다는 단점을 가지고 있고

ByRef 로 전달할 경우 속도가 다소 느리지만, 함수 내부에서도 값을 변경할 수 있다는 특징을 가지고 있습니다.


문자열 관련된 대부분의 API는 반환 값이 DWORD 또는 BOOL 형식입니다.

그리고 매개변수에서 LPCTSTR* 이런식으로 결과값을 전달받게 되는데, 이러한 경우에 ByRef를 사용하여 전달한 매개변수에 문자열이 저장되게끔 합니다.


C#의 경우 out, ref 두 개가 있습니다.

out, ref 를 사용하지 않는다면 값에 의한 전달을 하게 됩니다.

out 의 경우엔 출력 매개변수라 하고, ref 의 경우 참조 매개변수라 합니다.


출력 매개변수? 참조 매개변수?

출력 매개변수는 값을 초기화하지 않아도 함수 내부에서 값을 초기화한 뒤 전달된 매개변수에 값을 저장해주는 방식입니다.

참조 매개변수는 함수 내부에서 전달된 매개변수의 값을 사용하고, 결과를 매개변수에 저장해주는 방식입니다.


출력 매개변수의 경우 초기화가 필요 없고, 참조 매개변수의 경우 함수 내부에서 참조를 하기 때문에 초기화가 필요합니다.



문자열

문자열의 경우 String 을 사용할 수도 있지만, System.Text.StringBuilder 클래스를 사용하는 것이 훨씬 쉽습니다.

out, ref 키워드를 사용하지 않아도 된다는 매우 큰 메리트가 있죠.

한 가지 예를 들어보겠습니다.

현재 디렉터리의 경로를 가져오는 GetCurrentDirectory API 인데요.

DWORD nBufferLength

_Out_ LPTSTR lpBuffer

두 개의 매개변수를 필요로 합니다.


위의 표를 대조하며 바꿨을 때는 

UInt32 nBufferLength

out String lpBuffer

이렇게 바꿀 수 있죠.


하지만 out 키워드를 사용하지 않고 StringBuilder를 사용하면

UInt32 nBufferLength

StringBuilder lpBuffer

이렇게 바뀔 수도 있습니다.


실제로 ref, out 키워드를 사용하는 문자열(String) 매개변수에는 StringBuilder 클래스를 사용하는 것이 더 안전합니다.



그냥 생각나서 주저리주저리 써봤습니다.

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


+ Recent posts