마샬링(Marshalling)
서로 다른 실행 환경 또는 프로그래밍 언어 간에 데이터를 전달하는 과정
마샬링은 다양한 데이터 형식 및 메모리 레이아웃 간의 변환을 수행하는 프로세스로 관리 코드(Managed Code)와 비관리 코드(Unmanaged Code)인 네이티브 코드 간의 데이터 전달을 제공하기 위한 중요한 기술로 주로 C, C++ 등과 같은 비관리 코드 또는 외부 라이브러리와 통신할 때 사용하는 기술
기본 개념
데이터 형식 변환
> 'MarshalAs' 특성을 사용하여 데이터 형식을 명시적으로 지정하며 주로 원시 데이터 타입(Primitive Data Type)에 사용
[MarshalAs(UnmanagedType.I4)]
public int count;
[MarshalAs(UnmanagedType.R4)]
public float pi;
메모리 레이아웃
> 'StructLayout' 특성을 사용하여 구조체의 메모리 레이아웃을 명시적으로 지정
[StructLayout(LayoutKind.Sequential)]
public struct ExampleStruct { }
구조체 Marshalling
> 'Marshal' 클래스를 사용하여 구조체를 다른 데이터 형식으로 변환하거나 비관리 코드로 전달
ExampleStruct exampleStruct = new ();
IntPtr examplePtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ExampleStruct));
Marshal.StructureToPtr(exampleStruct, examplePtr, false);
ExampleStruct restoredStruct
= (ExampleStruct)Marshal.PtrToStucture(examplePtr, typeof(ExampleStruct));
Marshal.FreeHGlobal(examplePtr);
문자열 Marshalling
> 'MarshalAs' 특성을 사용하여 C#에서 관리되는 문자열과 비관리 코드에서 사용하는 문자열 간의 변환
[DllImport("example.dll")]
public static extern void ExampleString([MarshalAs(UnmanagedType.LPStr)] string param);
C#에서 마샬링 유형
값(Value)
- 값의 복사를 통해 데이터를 전달
- 단순한 데이터 타이이나 구조체
메모리 주소(Reference)
- 메모리 주소를 공유하는 방식
- 클래스 인스턴스나 복잡한 데이터 구조
형식 매핑
형식 | 설명 |
원시 데이터 형식 | 'UnmanagedType' 열거형을 사용하여 형식 지정 |
문자열 | 문자열에 대한 다양한 형식 지정 |
포인터 및 배열 | 포인터 = IntPtr을 사용, 배열의 형식과 길이 지정 |
구조체 | 'StructLayout' 특성을 사용하여 구조체 메모리 레이아웃 지정 |
대리자 | MarshalAs를 사용하여 호출 규약 지정 |
주의 사항
❗️ 메모리 할당 및 해제
Marshallig은 데이터를 메모리에서 다른 메모리로 이동 → 적절한 메모리를 할당하고 이를 해제하는 작업이 필요
// 메모리 할당
IntPtr examplePtr = Marshal.AllocHGlobal(exampleSize);
// 메모리 해제 → 해제를 하지 않을 경우 정상 작동하지 않는다.
Marshal.FreeHGlobal(examplePtr);
실제 사용 사례 및 예시
※ 실무에서 직접 경험한 사례를 작성하였으며 예시 코드는 링크를 통해 확인할 수 있습니다.
- SDK 사용 : 장치 업체에서 제공받은 SDK(Software Development Kit)를 사용하여 장치를 관리 및 제어하는 프로젝트를 맡아 진행하게 되었다. 장치의 펌웨어는 블랙박스(기능은 알지만 작동 원리를 이해할 수 없는 복잡한 기계 장치나 시스템) 형태로 어떤 언어로 구성되어 있는지 알 수는 없지만 인터페이스를 통해 장치에서 제공하는 기능을 사용할 수 있게 DLL 파일로 제공해 주었다. 제공받은 SDK를 통해 장치의 관리 및 제어 기능을 핸들링하기 위해 마샬링(Marshalling) 기술을 사용하여 진행하게 되었으며, 제공하는 인터페이스 방법에 따라 달라지겠지만 C, C++, VB, Java 등 다른 언어들로 작성된 라이브러리를 사용할 때 마샬링 기술을 사용할 수 있다.
마치며 (개인적인 생각)
지금까지 C#, .NET의 마샬링(Marshalling)에 대해 정리하였다. C# 언어를 사용해 다른 언어의 인터페이스 규약을 가진 라이브러리와 통신할 때 사용하는기술로, 어렵지 않게 사용하려면 C, C++의 포인터와 같이 제공해 주는 다른 언어의 데이터 형식이나 메모리 레이아웃 등을 어느 정도는 알고 있어야 할 것 같다. 해당 마샬링 기술을 사용해 보고 이번 글을 포스팅하며 기존 Java 진영에서 JNI(Java Native Interface)를 사용해 다른 언어와 통신해 본 경험도 떠올랐고, 개인적으로 마샬링, JNI를 이용하여 다른 언어들과 인터페이스를 통해 통신해 보는 것은 좋은 경험이라 생각한다. 추가로 JNI 개념 및 사용 방법도 포스팅해 볼 예정이다.
Reference