System.String → char * (marshal_context)






■ System.String → char *

 예전 C++/CLI 에서는 System.String 문자열을 char * 형으로 변환하기 위해선
메모리를 할당하고, 할당된 메모리에 문자열을 복사하기 위해 Marshal::StringToHGlobalAuto 메소드를 호출했다.
그런 후, HGlobal 포인터를 char * 에 캐스팅 해주었다.

const char* unmanagedString = NULL;
try
{
    String^ managedString = gcnew String("managed string");
    // Note the double cast.
    unmanagedString = (char*)(void*)Marshal::StringToHGlobalAnsi(managedString);
}
finally
{
    // Don't forget to release. Note the ugly casts again. 
    Marshal::FreeHGlobal((IntPtr)(void*)unmanagedString);
}

 

 Visual Studio 2008 에서는 새로운 Marshalling Library 를 통하여 문자열을 복사하는 새로운 방법이 추가 되었다.
이 라이브러리는 Managed Code 의 System.String 과 Native Code 에서 문자열을 나타내는데 자주 쓰이는 타입
(char *, wchar_t *, BSTR, CStringT<wchar_t>, ...) 간의 변환을 쉽게 할 수 있게 해준다.

 Marshalling Library 에서는 marshal_context 라고 하는 새로운 클래스를 포함한다. 이 클래스는 문자열 변환 시
기본의 명시적으로 메모리를 해제 시켜 줘야 하는 작업을 대신 해준다. 따라서 문자열 변환 작업 시의 메모리 해제 및 누수에
대한 걱정을 덜어준다. marshal_context 클래스를 사용하는 예제는 다음과 같다.

marshal_context^ context = gcnew marshal_context();
String^ managedString = gcnew String("managed string");
const char* unmanagedString = context->marshal_as( managedString );



 기본 방법에 비해 코드가 상당히 줄어들었다. 또한 형변환을 위한 캐스팅 연산도 직접 작성하지 않아도 된다.
무엇보다도 제일 중요한 점은 char * 의 메모리 해제 시켜주는 작업을 작성하지 않아도 된다.
marshal_context 클래스는 모든 string 에 대한 참조를 유지한다. 유효 범위(scope) 를 벗어 날 때 소멸자가
그것들을 메모리 해제 시켜준다.






■ Native String → System:String




반대로 변환 하는 방법 역시 비슷하다.



System::String^ 타입을 const char *, const wchar_t *, BSTR 으로 변환하기 위해 marshal_as 를 직접 사용할 수는 없다. 왜냐하면 이러한 변환들은 사용 후에 메모리 해제를 위한 비 관리(Unmanaged) resource 를 필요로 한다. 아래와 같이 context 객체를 사용해야 한다. (즉, marshal_context 클래스의 객체 context 를 사용함으로써 marshal_context 의 소멸자가 메모리 해제를 해준다.)



context 객체는 동적 생성된 객체를 유지하다가 소멸자에서 그 메모리를 해제 시킨다. 내부적으로 할당된 객체들은 Linked-List 로 유지된다. 위의 코드에서 Console::WriteLine(context._clean_up_list.Count); 는 3을 출력 한다.




■ Extend the Marshaling Library 사용

String 타입을 변환할 때 다음과 같은 에러 메시지를 발생할 수 도 있다.

오류 1 error C4996: 'msclr::interop::error_reporting_helper<_To_Type,_From_Type>::marshal_as': This conversion is not supported by the library or the header file needed for this conversion is not included.  Please refer to the documentation on 'How to: Extend the Marshaling Library' for adding your own marshaling method.

CString 타입을 사용하려 하니까 나타난 에러 메시지 인데, MFC 사용할 때 CString 이 ATL data type 이라는 것을 얼 추 추측하긴 했지만 위와 같은 에러 메시지로 확신하게 되었다.

에러 메시지 내용대로 MSDN 의 How to:Extend the Marshaling Library 문서를 확인해 보니 ATL data type 을 위해선 marshal_atl.h 헤더 파일을 include 시켜줘야 한다.








■ 참조

1. More Tales from the Unmanaged Side - System.String -> char* : marshal_context 사용 전의 문자열 변환 방법
2. Tales from the Unmanaged Side – System.String –> char* (pt. 2)  : marshal_context 이용한 방법
3. Using marshal_as for mixed-mode string conversions : 여러 string type 의 conversion
4. Overview of Marshaling in C++
5. How to:Extend the Marshaling Library
Posted by six605
,