콘텐츠로 이동

C# 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);

소스 제너레이터 기반으로 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
bool4바이트 BOOLBOOL
stringANSI/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);
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로 자동 해제 보장