C# Marshal

★━─….NET/C# 2010. 1. 22. 09:58
 Win32 응용프로그램과 .Net 응용프로그램을 연결시켜주는 놀라운 BCL 이다. 당분간 이 클래스를 사용할 일이 많을 것 같아 주요한 것 만 정리해 보았다.



1. DataType

Marshal 클래스의 메소드를 보면 IntPtr 이라는 데이터 타입이 눈에 들어온다.

IntPtr
포인터나 핸들을 나타내는데 사용되는 플랫폼별 형식이다. (cf. MSDN)

 IntPtr 형식은 그 크기가 플랫폼마다 고유한 정수로 디자인되었습니다. 즉, 이 형식의 인스턴스는 32비트 하드웨어 및 운영 체제에서는 32비트로, 64비트 하드웨어 및 운영 체제에서는 64비트여야 합니다.

IntPtr 형식은 포인터를 지원하는 언어에서 사용할 수 있으며, 포인터를 지원하는 언어와 포인터를 지원하지 않는 언어 사이에서 데이터를 참조하는 일반적인 방법이 됩니다.

핸들을 보관하는 데도 IntPtr 개체를 사용할 수 있습니다. 예를 들어, IntPtr의 인스턴스는 파일 핸들을 보관하기 위해 System.IO..::.FileStream 클래스에서 광범위하게 사용됩니다.

IntPtr 형식은 CLS 규격이지만, UIntPtr 형식은 그렇지 않습니다. 공용 언어 런타임에서는 IntPtr 형식만 사용됩니다. UIntPtr 형식은 IntPtr 형식과의 구조적 대칭을 유지하기 위해 주로 사용됩니다.


참고로 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비트
출처 : (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 를 호출한다고 한다.



Posted by six605
,