Win32 응용프로그램과 .Net 응용프로그램을 연결시켜주는 놀라운 BCL 이다. 당분간 이 클래스를 사용할 일이 많을 것 같아 주요한 것 만 정리해 보았다.
1. DataType
Marshal 클래스의 메소드를 보면 IntPtr 이라는 데이터 타입이 눈에 들어온다.
IntPtr
포인터나 핸들을 나타내는데 사용되는 플랫폼별 형식이다. (cf. MSDN)
참고로 Win32 응용프로그램 즉, Unmanaged Code 데이터 타입에 대응되는 Managed Code 의 데이터 타입은 아래 표와 같다. 정리해두신 분이 있어 자료를 링크한다.
출처 : (P/Invoke series_ No.01 ) _플랫폼 호출 데이터 형식
위 표에서 알 수 있듯이 IntPtr 은 관리되지 않는 코드(Unmanaged Code) 에서는 void * 타입이다.
관리되는 코드에서 IntPtr 을 보게된다면 이렇게 생각하면 될 것 같다.
"IntPtr, 관리되지 않는 메모리 블록에 대한 포인터"
관리되는 코드에서 편리하게 사용하기 위해, 또 혹시 모를 오류의 가능성을 피하기 위해 관리되지 않는 메모리 블록의 데이터를 관리되는 데이터 블록으로 변환해 주자. Marshal 의 메소드를 이용하면 된다.
2. Marshal.PtrToStructur
관리되지 않는 메모리 블럭의 데이터를 지정된 형식의 새로 할당된 관리되는 개체로 마샬링한다.
자세한 내용은 MSDN 참조 (Marshal.PtrToStructure 메서드 (IntPtr, Type))
[ComVisibleAttribute(true)]
public static Object PtrToStructure (
IntPtr ptr,
Type structureType
)
structureType 는 형식이 지정된 클래스나 구조체여야 한다. 즉, 참조 타입, 값 타입 모두 가능하다. 다만 레이아웃이 sequential 또는 Explicit 로 설정되어 있어야 한다. 값 형식이 전달될 경우 반환값은 boxing 된 인스턴스이다. 리턴값이 object 타입이므로 사용할 때는 사용하려는 타입으로 형변환 하여 사용해야 한다.
3. Marshal.Copy
관리되는 배열에서 관리되지 않는 메모리 포인터로 데이터를 복사 또는,
관리되지 않는 메모리 포인터에서 관리되는 배열로 데이터를 복사한다.
자세한 내용은 MSDN 참조 (Marshal.Copy 메서드)
메서드의 인수 중에서 복사될 곳을 가리키는 인수는 반드시 메모리 생성 되어 있어야 한다. 즉, 관리되는 배열이라면 new 연산자로 먼저 메모리 할당을 해야 하고, 관리되지 않는 배열이라면 Marshal.AllocHGlobal 등으로 메모리를 먼저 할당해주어야 한다. 그렇지 않으면 ArgumentNullException 이 발생한다.
4. Marshal.AllocHGlobal
프로세스의 관리되지 않는 메모리에서 메모리를 할당한다. 사용할 상황은 이렇다. 관리되는 코드의 데이터를 관리되지 않는 메모리 블록에 복사해야 한다. 그럴때 이 메소드를 사용한다.
[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static IntPtr AllocHGlobal(
int cb
)
cb 는 요청된 메모리의 바이트 수 이다. IntPtr 은 관리되지 않는 메모리 블록에 생성된 메모리를 가리키는 IntPtr 값이다.
여기서 중요한 것은!!!
" 관리되지 않는 메모리에 우리가 직접 메모리를 생성하였으므로 수동으로 직접 메모리를 해제시켜줘야 한다."
Marshal.FreeHGlobal 을 이용하면 된다.
Marshal 메소드 중에서 메모리 할당 API 는 Marshal.AllocHGlobal, Marshal.AllocCoTastMem 이렇게 2가지가 있다. AllocCoTastMem 메소드는 kernel32.dll 로 부터 LocalAlloc Win32 API 를 호출한다고 한다.
1. DataType
Marshal 클래스의 메소드를 보면 IntPtr 이라는 데이터 타입이 눈에 들어온다.
IntPtr
포인터나 핸들을 나타내는데 사용되는 플랫폼별 형식이다. (cf. MSDN)
IntPtr 형식은 그 크기가 플랫폼마다 고유한 정수로 디자인되었습니다. 즉, 이 형식의 인스턴스는 32비트 하드웨어 및 운영 체제에서는 32비트로, 64비트 하드웨어 및 운영 체제에서는 64비트여야 합니다. |
참고로 Win32 응용프로그램 즉, Unmanaged Code 데이터 타입에 대응되는 Managed Code 의 데이터 타입은 아래 표와 같다. 정리해두신 분이 있어 자료를 링크한다.
Wtypes.h의 관리되지 않는 형식 |
관리되지 않는 C 언어 형식 |
관리되는 클래스 이름 | 설명 |
HANDLE | void* | System.IntPtr | 32비트 Windows 운영 체제의 경우 32비트, 64비트 Windows 운영 체제의 경우 64비트 |
BYTE | unsigned char | System.Byte | 8비트 |
SHORT | short | System.Int16 | 16비트 |
WORD | unsigned short | System.UInt16 | 16비트 |
INT | int | System.Int32 | 32비트 |
UINT | unsigned int | System.UInt32 | 32비트 |
LONG | long | System.Int32 | 32비트 |
BOOL | long | System.Int32 | 32비트 |
DWORD | unsigned long | System.UInt32 | 32비트 |
ULONG | unsigned long | System.UInt32 | 32비트 |
CHAR | char | System.Char | ANSI로 데코레이트 |
LPSTR | char* | System.String 또는 System.Text.StringBuilder |
ANSI로 데코레이트 |
LPCSTR | Const char* | System.String 또는 System.Text.StringBuilder |
ANSI로 데코레이트 |
LPWSTR | wchar_t* | System.String 또는 System.Text.StringBuilder |
유니코드로 데코레이트 |
LPCWSTR | Const wchar_t* | System.String 또는 System.Text.StringBuilder |
유니코드로 데코레이트 |
FLOAT | Float | System.Single | 32비트 |
DOUBLE | Double | System.Double | 64비트 |
위 표에서 알 수 있듯이 IntPtr 은 관리되지 않는 코드(Unmanaged Code) 에서는 void * 타입이다.
관리되는 코드에서 IntPtr 을 보게된다면 이렇게 생각하면 될 것 같다.
"IntPtr, 관리되지 않는 메모리 블록에 대한 포인터"
관리되는 코드에서 편리하게 사용하기 위해, 또 혹시 모를 오류의 가능성을 피하기 위해 관리되지 않는 메모리 블록의 데이터를 관리되는 데이터 블록으로 변환해 주자. Marshal 의 메소드를 이용하면 된다.
2. Marshal.PtrToStructur
관리되지 않는 메모리 블럭의 데이터를 지정된 형식의 새로 할당된 관리되는 개체로 마샬링한다.
자세한 내용은 MSDN 참조 (Marshal.PtrToStructure 메서드 (IntPtr, Type))
[ComVisibleAttribute(true)]
public static Object PtrToStructure (
IntPtr ptr,
Type structureType
)
structureType 는 형식이 지정된 클래스나 구조체여야 한다. 즉, 참조 타입, 값 타입 모두 가능하다. 다만 레이아웃이 sequential 또는 Explicit 로 설정되어 있어야 한다. 값 형식이 전달될 경우 반환값은 boxing 된 인스턴스이다. 리턴값이 object 타입이므로 사용할 때는 사용하려는 타입으로 형변환 하여 사용해야 한다.
3. Marshal.Copy
관리되는 배열에서 관리되지 않는 메모리 포인터로 데이터를 복사 또는,
관리되지 않는 메모리 포인터에서 관리되는 배열로 데이터를 복사한다.
자세한 내용은 MSDN 참조 (Marshal.Copy 메서드)
메서드의 인수 중에서 복사될 곳을 가리키는 인수는 반드시 메모리 생성 되어 있어야 한다. 즉, 관리되는 배열이라면 new 연산자로 먼저 메모리 할당을 해야 하고, 관리되지 않는 배열이라면 Marshal.AllocHGlobal 등으로 메모리를 먼저 할당해주어야 한다. 그렇지 않으면 ArgumentNullException 이 발생한다.
4. Marshal.AllocHGlobal
프로세스의 관리되지 않는 메모리에서 메모리를 할당한다. 사용할 상황은 이렇다. 관리되는 코드의 데이터를 관리되지 않는 메모리 블록에 복사해야 한다. 그럴때 이 메소드를 사용한다.
[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static IntPtr AllocHGlobal(
int cb
)
cb 는 요청된 메모리의 바이트 수 이다. IntPtr 은 관리되지 않는 메모리 블록에 생성된 메모리를 가리키는 IntPtr 값이다.
여기서 중요한 것은!!!
" 관리되지 않는 메모리에 우리가 직접 메모리를 생성하였으므로 수동으로 직접 메모리를 해제시켜줘야 한다."
Marshal.FreeHGlobal 을 이용하면 된다.
Marshal 메소드 중에서 메모리 할당 API 는 Marshal.AllocHGlobal, Marshal.AllocCoTastMem 이렇게 2가지가 있다. AllocCoTastMem 메소드는 kernel32.dll 로 부터 LocalAlloc Win32 API 를 호출한다고 한다.