문자열 마샬링
.NET과 C++ 네이티브 코드 간의 상호작용에서 문자열을 주고받는 것은 자주 발생하는 작업입니다. 그러나 문자열은 메모리 레이아웃이 다르고, 네이티브 코드와 매니지드 코드 간에 인코딩 방식도 다를 수 있기 때문에 올바르게 마샬링하기 위해서는 주의가 필요합니다. .NET에서는 문자열 마샬링 시 ANSI와 유니코드(UTF-16) 두 가지 주요 인코딩 방식을 사용하며, 이를 명시적으로 처리해야 합니다.
문자열 마샬링 방법
.NET에서는 기본적으로 string 타입을 사용하여 문자열을 처리하지만, C++에서는 char* 또는 wchar_t* 타입을 사용하여 문자열을 처리합니다. 마샬링 과정에서 .NET의 문자열을 네이티브 코드에서 처리할 수 있는 형식으로 변환해야 하며, 이를 위해 마샬링 특성이나 Marshal
클래스를 사용할 수 있습니다.
기본적인 ANSI 문자열 마샬링
ANSI 문자열은 ASCII와 유사한 인코딩 방식으로, 네이티브 C++에서 일반적으로 **char***를 통해 처리됩니다. C#에서는 CharSet.Ansi
특성을 사용하여 문자열을 ANSI로 마샬링할 수 있습니다.
예시: ANSI 문자열 마샬링
다음은 C++에서 ANSI 문자열을 받아 출력하는 함수와 이를 C#에서 호출하는 예시입니다.
C++ 코드
// NativeLibrary.cpp
extern "C" __declspec(dllexport) void PrintAnsiString(const char* str) {
printf("Received ANSI string: %s\n", str);
}
C# 코드
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void PrintAnsiString(string str);
static void Main()
{
PrintAnsiString("Hello, ANSI!");
}
}
이 예제에서는 C#의 string 타입이 ANSI 인코딩으로 C++ 네이티브 함수에 전달됩니다. CharSet.Ansi
특성을 통해 문자열이 ANSI 형식으로 마샬링되며, C++에서 이를 올바르게 처리할 수 있습니다.
유니코드(UTF-16) 문자열 마샬링
유니코드 문자열은 UTF-16 인코딩을 사용하여 다국어를 지원하는 문자열 처리 방식입니다. .NET의 기본 문자열 인코딩은 유니코드이므로, 별도의 특성을 지정하지 않으면 기본적으로 유니코드로 마샬링됩니다. C++에서는 **wchar_t***를 사용하여 유니코드 문자열을 처리할 수 있습니다.
예시: 유니코드 문자열 마샬링
다음은 C++에서 유니코드 문자열을 받아 출력하는 함수와 이를 C#에서 호출하는 예시입니다.
C++ 코드
// NativeLibrary.cpp
#include <wchar.h>
extern "C" __declspec(dllexport) void PrintUnicodeString(const wchar_t* str) {
wprintf(L"Received Unicode string: %ls\n", str);
}
C# 코드
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern void PrintUnicodeString(string str);
static void Main()
{
PrintUnicodeString("Hello, Unicode!");
}
}
이 예제에서는 C#의 string 타입이 **유니코드(UTF-16)**로 마샬링되며, C++에서 **wchar_t***로 받아들여집니다. 기본적으로 .NET에서는 유니코드를 지원하므로, CharSet.Unicode
를 사용하여 네이티브 코드로 전달할 수 있습니다.
문자열을 포인터로 마샬링
때때로 네이티브 코드에서 문자열을 처리하는 동안, 문자열의 포인터를 직접 전달받아야 할 수 있습니다. 이때, C#에서는 IntPtr을 사용하여 문자열 포인터를 처리할 수 있으며, Marshal.StringToHGlobalAnsi
또는 Marshal.StringToHGlobalUni
를 사용하여 문자열을 마샬링한 후, 해당 메모리 주소를 네이티브 코드로 전달합니다.
예시: 문자열을 포인터로 마샬링
다음은 C++에서 문자열 포인터를 받아 처리하는 예시입니다.
C++ 코드
// NativeLibrary.cpp
extern "C" __declspec(dllexport) void PrintStringPointer(const char* str) {
printf("Received string pointer: %s\n", str);
}
C# 코드
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void PrintStringPointer(IntPtr str);
static void Main()
{
string message = "Hello, from pointer!";
IntPtr strPtr = Marshal.StringToHGlobalAnsi(message);
PrintStringPointer(strPtr);
Marshal.FreeHGlobal(strPtr);
}
}
이 예제에서는 Marshal.StringToHGlobalAnsi
메서드를 사용하여 C# 문자열을 ANSI 문자열로 변환한 후, 해당 문자열 포인터를 네이티브 C++ 함수에 전달합니다. 사용 후에는 **Marshal.FreeHGlobal
**을 호출하여 메모리를 해제합니다.
문자열 배열 마샬링
때때로 네이티브 코드에서 문자열 배열을 전달해야 할 경우가 있습니다. .NET에서는 배열을 전달할 수 있지만, 문자열 배열은 각각의 문자열 포인터가 필요하기 때문에 별도의 처리 과정이 필요합니다.
예시: 문자열 배열 마샬링
다음은 C++에서 문자열 배열을 받아 출력하는 예시입니다.
C++ 코드
// NativeLibrary.cpp
extern "C" __declspec(dllexport) void PrintStringArray(const char** strArray, int count) {
for (int i = 0; i < count; i++) {
printf("String %d: %s\n", i, strArray[i]);
}
}
C# 코드
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void PrintStringArray(IntPtr[] strArray, int count);
static void Main()
{
string[] messages = { "Hello", "World", "From C#" };
IntPtr[] ptrArray = new IntPtr[messages.Length];
for (int i = 0; i < messages.Length; i++)
{
ptrArray[i] = Marshal.StringToHGlobalAnsi(messages[i]);
}
PrintStringArray(ptrArray, messages.Length);
// 메모리 해제
for (int i = 0; i < messages.Length; i++)
{
Marshal.FreeHGlobal(ptrArray[i]);
}
}
}
이 예제에서는 문자열 배열을 각각의 포인터로 마샬링한 후 네이티브 코드로 전달합니다. 배열의 각 문자열은 포인터로 변환된 후 전달되며, 마샬링 후에는 반드시 메모리를 해제해야 합니다.
결론
문자열 마샬링은 .NET과 C++ 간의 상호작용에서 중요한 부분을 차지합니다. ANSI와 유니코드 문자열을 올바르게 마샬링하기 위해서는 CharSet 특성을 활용하고, 문자열을 포인터로 마샬링할 때는 Marshal.StringToHGlobal
을 사용하여 적절하게 처리해야 합니다. 또한 문자열 배열을 처리할 때는 각각의 문자열을 포인터로 변환하여 네이티브 코드로 전달하고, 사용 후 메모리 해제를 잊지 말아야 합니다.