기본 데이터 타입 마샬링
.NET 애플리케이션에서 네이티브 코드를 호출할 때, 기본 데이터 타입의 마샬링은 자주 발생하는 작업 중 하나입니다. 기본 데이터 타입은 정수형, 부동소수점형, 불리언 등의 단순한 데이터 타입을 말하며, .NET과 네이티브 코드 간의 데이터 변환이 필요할 때 마샬링을 사용하여 이 데이터들을 처리할 수 있습니다.
정수형(Int) 마샬링
정수형 데이터는 .NET과 네이티브 코드 간에 가장 흔히 마샬링되는 타입 중 하나입니다. int, long, short 등의 데이터 타입은 C++과 .NET 간에 거의 동일한 메모리 표현 방식을 사용하므로, 상대적으로 마샬링이 간단하게 이루어집니다.
예시: Int 마샬링
다음 예제는 C++에서 작성된 Add
함수를 .NET에서 호출하는 방식입니다. 두 정수를 더한 값을 반환하는 단순한 함수입니다.
C++ 코드
// NativeLibrary.cpp
extern "C" __declspec(dllexport) int Add(int a, int b) {
return a + b;
}
.NET 코드
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Add(int a, int b);
static void Main()
{
int result = Add(3, 5);
Console.WriteLine($"Result from native code: {result}");
}
}
이 예제에서는 C++의 Add
함수를 호출하여 두 정수를 더한 값을 반환합니다. int 타입은 .NET과 네이티브 코드 간의 메모리 레이아웃이 동일하므로, 변환이 필요하지 않은 Blittable 타입으로 처리됩니다.
부동소수점형(Float, Double) 마샬링
부동소수점형 데이터도 기본 데이터 타입 중 하나로, float와 double 타입이 대표적입니다. .NET과 C++ 간의 부동소수점 데이터 타입은 메모리 표현 방식이 동일하여 Blittable 타입으로 마샬링됩니다.
예시: Double 마샬링
다음 예제는 부동소수점 값을 처리하는 네이티브 함수를 호출하는 방식입니다.
C++ 코드
// NativeLibrary.cpp
extern "C" __declspec(dllexport) double Multiply(double x, double y) {
return x * y;
}
.NET 코드
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern double Multiply(double x, double y);
static void Main()
{
double result = Multiply(2.5, 4.0);
Console.WriteLine($"Result from native code: {result}");
}
}
이 예제에서 Multiply
함수는 두 개의 double
값을 곱한 결과를 반환합니다. double 타입 역시 Blittable 타입이므로 특별한 데이터 변환 없이 마샬링됩니다.
불리언(Boolean) 마샬링
불리언(Boolean) 타입은 .NET과 네이티브 코드 간의 메모리 표현 방식이 다를 수 있어, 마샬링 과정에서 주의해야 합니다. .NET에서는 Boolean 값이 1바이트로 표현되는 반면, C/C++에서는 일반적으로 4바이트로 표현되거나 다른 메모리 표현 방식을 사용할 수 있습니다.
불리언 마샬링 방식
.NET에서는 MarshalAs
특성을 사용하여 불리언 값을 올바르게 마샬링할 수 있습니다. C/C++에서 불리언 값을 어떻게 표현하는지에 따라 적절한 마샬링 전략을 선택해야 합니다.
예시: 불리언 마샬링
다음 예제는 불리언 값을 네이티브 함수로 전달하는 방식입니다.
C++ 코드
// NativeLibrary.cpp
extern "C" __declspec(dllexport) bool IsPositive(int value) {
return value > 0;
}
.NET 코드
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool IsPositive(int value);
static void Main()
{
bool isPos = IsPositive(10);
Console.WriteLine($"Is the value positive? {isPos}");
}
}
이 예제에서는 IsPositive
함수가 정수값이 양수인지 확인하고, 불리언 값을 반환합니다. 이때, 불리언 값은 C++의 bool 타입과 호환되도록 마샬링됩니다. **MarshalAs(UnmanagedType.I1)
**을 사용하여 불리언 값을 1바이트로 마샬링할 수 있습니다.
비트 단위 데이터 마샬링
비트 단위로 데이터를 처리하는 경우도 있습니다. bitfield와 같은 구조는 네이티브 코드에서 사용되지만, .NET에서는 이를 지원하지 않기 때문에 비트 단위 데이터를 처리할 때는 적절한 마샬링을 수행해야 합니다.
비트 필드 데이터 처리 예
다음은 C++에서 비트 필드를 정의하고 .NET에서 해당 데이터를 처리하는 예시입니다.
C++ 코드
// NativeLibrary.cpp
struct BitFieldStruct {
unsigned int flag1 : 1;
unsigned int flag2 : 1;
unsigned int flag3 : 1;
};
.NET 코드
비트 필드 데이터는 .NET에서 직접적으로 표현할 수 없으므로, 정수로 변환한 후 적절히 마샬링하여 처리해야 합니다.
[StructLayout(LayoutKind.Explicit)]
public struct BitFieldStruct
{
[FieldOffset(0)]
public byte Flags;
public bool Flag1 => (Flags & 1) != 0;
public bool Flag2 => (Flags & 2) != 0;
public bool Flag3 => (Flags & 4) != 0;
}
위 예시에서, 비트 필드를 .NET에서 byte 값으로 마샬링한 후, 각 비트를 Boolean으로 처리합니다. 이 방식으로 비트 필드 데이터를 .NET에서 처리할 수 있습니다.
결론
기본 데이터 타입 마샬링은 .NET과 네이티브 코드 간의 데이터 교환에서 중요한 역할을 합니다. Blittable 타입(예: 정수형, 부동소수점형)은 별도의 변환 없이 쉽게 마샬링될 수 있지만, 불리언이나 비트 필드와 같은 데이터는 적절한 마샬링 전략을 적용해야 합니다. 특히, 불리언 타입의 마샬링에서는 MarshalAs 특성을 사용하여 적절히 처리하는 것이 중요합니다.