C# P/Invoke와 네이티브 인터롭
P/Invoke 기본
섹션 제목: “P/Invoke 기본”using System.Runtime.InteropServices;
// kernel32.dll의 MessageBox 호출[DllImport("user32.dll", CharSet = CharSet.Unicode)]static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type);
MessageBox(IntPtr.Zero, "안녕하세요", "제목", 0);LibraryImport (C# 11, .NET 7+)
섹션 제목: “LibraryImport (C# 11, .NET 7+)”소스 제너레이터 기반으로 P/Invoke 래퍼 코드를 컴파일 타임에 생성합니다. AOT 친화적입니다.
// 기존 DllImport[DllImport("mylib.dll")]static extern int Add(int a, int b);
// C# 11 LibraryImport (권장)[LibraryImport("mylib.dll")]static partial int Add(int a, int b);마샬링 기본 규칙
섹션 제목: “마샬링 기본 규칙”| C# 타입 | 기본 마샬링 | C 타입 |
|---|---|---|
int | 직접 | int32_t |
bool | 4바이트 BOOL | BOOL |
string | ANSI/Unicode 문자열 | char* / wchar_t* |
byte[] | 포인터 | uint8_t* |
IntPtr | 포인터 크기 | void* |
// 문자열 인코딩 지정[DllImport("lib.dll", CharSet = CharSet.Ansi)]static extern void PrintA(string s);
[DllImport("lib.dll", CharSet = CharSet.Unicode)]static extern void PrintW(string s);구조체 마샬링
섹션 제목: “구조체 마샬링”// C 구조체:// struct Point { int x; int y; };
[StructLayout(LayoutKind.Sequential)]struct Point { public int X; public int Y;}
[DllImport("lib.dll")]static extern double Distance(Point a, Point b);
var p1 = new Point { X = 0, Y = 0 };var p2 = new Point { X = 3, Y = 4 };double d = Distance(p1, p2); // 5.0패딩/정렬 제어:
[StructLayout(LayoutKind.Sequential, Pack = 1)] // 1바이트 정렬 (패딩 없음)struct PackedHeader { public byte Type; public short Length; public int Checksum;}배열과 포인터
섹션 제목: “배열과 포인터”// C: void Fill(int* arr, int len, int val)[DllImport("lib.dll")]static extern void Fill(int[] arr, int len, int val);
// 고정 버퍼 전달 (unsafe)unsafe { fixed (byte* ptr = buffer) { NativeWrite(ptr, buffer.Length); }}콜백 (함수 포인터)
섹션 제목: “콜백 (함수 포인터)”// C: typedef int (*Comparator)(const void*, const void*);// void QSort(void* arr, int n, int size, Comparator cmp);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]delegate int Comparator(IntPtr a, IntPtr b);
static int Compare(IntPtr a, IntPtr b) => Marshal.ReadInt32(a).CompareTo(Marshal.ReadInt32(b));
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]static extern void qsort(int[] arr, int n, int size, Comparator cmp);
int[] data = { 3, 1, 4, 1, 5 };qsort(data, data.Length, 4, Compare);SafeHandle로 리소스 안전 관리
섹션 제목: “SafeHandle로 리소스 안전 관리”public class SafeFileHandle : SafeHandleZeroOrMinusOneIsInvalid { public SafeFileHandle() : base(ownsHandle: true) {}
protected override bool ReleaseHandle() { return CloseHandle(handle); }
[DllImport("kernel32.dll")] static extern bool CloseHandle(IntPtr h);}
[DllImport("kernel32.dll")]static extern SafeFileHandle CreateFile(string path, ...);SafeHandle은 GC Finalize 시 자동으로 네이티브 핸들을 해제합니다.
DllImport는 전통적 P/Invoke,LibraryImport는 C# 11+ 권장 방식[StructLayout(LayoutKind.Sequential)]로 구조체 레이아웃 제어- 문자열은
CharSet으로 ANSI/Unicode 명시 - 콜백은
[UnmanagedFunctionPointer]델리게이트로 래핑 - 네이티브 핸들은
SafeHandle로 자동 해제 보장