2010년 6월 4일 금요일

C# Marshal : 데이터 변환





C# Marshal : 데이터 변환














 대부분에 있어 Managed 의 string 을 Unmanaged 의 CString 이나 LPSTR 로 변환하는 경우가 많았는데 오늘 다른 타입으로 변경해야 되는 경우가 생겼다... 시간좀 많이 소비한것 같다..











■ 윈도우 핸들(HWND) 변환





Win32API나 MFC를 보면 함수 파라미터로 윈도우 핸들 즉, HWND 를 많이 사용한다. Managed Code 에서 HWND에 대응하는 타입은 IntPtr 이다.


HWND ↔ IntPtr





IntPtr 은 void * 또는 핸들타입에 사용된다.





호출하고자 하는 함수 선언



void Func(HWND hWnd);




Managed Code delegate



[UnmanagedFunctionPointer(CallingConvention.Cdel)]
private delegate void deleFunc(IntPtr hWnd);




참고로 Control을 상속받는 클래스에서는 Handle 프로퍼티로 쉽게 핸들값을 얻을 수 있다. Handle 프로퍼티는 컨트롤 창의 핸들값을 IntPtr로 리턴한다.



[BrowsableAttribute(false)]public IntPtr Handle { get; }












■ out 타입 포인터 변수 전달





Unmanaged Code 의 함수가 인수로 DWORD * 타입을 갖으며 해당 포인터 변수에 값을 저장한다. 따라서 이 함수를 호출하고 난 뒤에 포인터 변수에 저장된 값을 조사해서 사용하는 경우이다.






호출하고자 하는 함수 선언



void Func(DWORD *pdwListenPort);






Func 함수 호출결과 pdwListenPort 포인터 변수에 값을 얻게 된다. 즉, out 속성.





Managed Code delegate



[UnmanagedFunctionPointer(CallingConvention.Cdel)]
private delegate void deleFunc(out IntPtr pdwListenPort);






마샬링에서 void * 는 IntPtr 로 매칭된다.


void * ↔ IntPtr





DWORD * 이므로 일단 IntPtr 을 사용하였고, 메소드 결과 포인터 변수로 값을 얻으므로 out 키워드를 사용하였다.


IntPtr 은 void * 이므로 DWORD * 형식의 데이터를 얻기 위해선 적절한 타입 캐스팅을 해주면 된다.


IntPtr 변수의 메소드로 To- 관련 메소드를 사용하면 쉽게 변환이 가능하다. 원래는 DWORD 는 32bit Unsigned int 타입이다. 그렇다면 Managed Code쪽에서는 uint 또는 UInt32 타입으로 변환해 주어야 한다. 올바르지 않지만 ToInt32() 메소드를 이용하여 변환해 주었다.


DWORD * (out 속성의 포인터 변수) ↔ out IntPtr






// deleFunc Func 에 연결

IntPtr value = IntPtr.Zero;
Func(out value);

Int32 tmp = 0;
tmp = value.ToInt32();










■ in 타입 포인터 변수 변환





C++ 에서 포인터 변수는 기본적으로 값을 읽고 쓸 수 있다. 물론 const 키워드를 통해 읽기만 가능하게 할 수는 있다. 이렇게 Managed Code 쪽에서 포인터 변수값을 읽고 쓸 수 있게 넘기는 법은 아직 구현해보지 못했다...


포인터 변수를 전달하여 값을 전달 받는 것은 위에 방법을 사용하면 되고 (■ out 타입 포인터 변수 전달),


포인터 변수의 값을 저장하여 전달하는 방법을 알아보자.





호출하고자 하는 함수 선언



void Func(DWORD *dwPort);




Managed Code delegate



[UnmanagedFunctionPointer(CallingConvention.Cdel)]
private delegate void deleFunc([MarshalAs(UnmanagedType.AsAny)] Object pdwPort);




우선 이 변환은 IntPtr 타입을 사용하지 않았다. MarshalAs 프로퍼티를 사용하였으며, AsAny 를 사용하였다.


여기서 AsAny 설명은 다음과 같다.


런타임에서 개체의 형식을 결정하고 해당 형식으로 개체를 마샬링하는 동적 형식입니다. 플랫폼 호출 메서드에만 유효합니다.





사용 예



uint port = 9100;

Func(port);




즉, uint 값이 런타임시에 적잘하게 포인터를 만들고 port 값을 그 포인터 변수에 저장하여 Unmananged 코드에 전달하게 된다.







■ in/out 타입 포인터 변수 변환

왠만한 경우 위의 in 또는 out 타입 포인터 변수 변환으로 사용할 수 있는데 in, out 타입의 포인터 변수를 사용해야 할 때 어떻게 해야 할까? 아.. 근데 이 마샬링 변환을 매번 사용하는게 아니라 간혹가다 사용하니 할때마다 새롭다...

호출하고자 하는 함수 선언


void Func(DWORD *dwPort);



dwPort 포인터 변수에 값을 설정하여 Func함수에 전달한다. Func함수 호출 후 dwPort에 새로운 값이 저장된다.

Managed Code delegate

[UnmanagedFunctionPointer(CallingConvention.Cdel)]
private delegate void deleFunc(ref uint pdwPort);





DWORD 는 Unsigned int 32bit 타입이브로 C#의 uint 별칭을 사용한다.


uint portNum = 0;
Func(ref portNum); 


Func함수 호출 후 새로운 값이 portNum에 저장되어 있다.



■ LPCTSTR

LPCTSTR ↔ [MarshalAs(UnmanagedType.LPWStr] string







참조)








@



댓글 없음:

댓글 쓰기