구조체 전달 방식
C++ 코드
extern "C" __declspec(dllexport) bool GetVersionInfo(OSINFO* info);
extern "C" __declspec(dllexport) bool GetVersionRef(OSINFO& info);
C# 구조체 정의
[StructLayout(LayoutKind.Sequential)]
public struct OSINFO
{
public int osVersion;
public int majorVersion;
public int minorVersion;
public int buildNumber;
public int platformId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string versionString;
}
방법 1: 참조 전달 (ref 키워드 사용)
[DllImport("jnalib.dll", EntryPoint = "GetVersionInfo")]
public static extern bool GetVersionInfo(ref OSINFO data);
[DllImport("jnalib.dll", EntryPoint = "GetVersionRef")]
public static extern bool GetVersionRef(ref OSINFO data);
방법 2: IntPtr를 통한 직접 메모리 접근
IntPtr buffer = Marshal.AllocHGlobal(Marshal.SizeOf<OSINFO>());
try
{
if (GetVersionInfo(buffer))
{
Console.WriteLine("버전: {0}", Marshal.ReadInt32(buffer));
Console.WriteLine("메이저: {0}", Marshal.ReadInt32(buffer, 4));
Console.WriteLine("빌드 번호: {0}", Marshal.ReadInt32(buffer, 12));
Console.WriteLine("버전 문자열: {0}", Marshal.PtrToStringAnsi(new IntPtr(buffer.ToInt64() + 20)));
}
}
finally
{
Marshal.FreeHGlobal(buffer);
}
구조체 배열 전달
C++ 함수 선언
extern "C" __declspec(dllexport) bool GetVersionList(OSINFO* list, int count);
C#에서의 처리
[DllImport("jnalib.dll", EntryPoint = "GetVersionList")]
public static extern bool GetVersionList(IntPtr buffer, int count);
OSINFO[] items = new OSINFO[2];
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<OSINFO>() * 2);
try
{
GetVersionList(ptr, 2);
for (int i = 0; i < 2; i++)
{
IntPtr itemPtr = new IntPtr(ptr.ToInt64() + i * Marshal.SizeOf<OSINFO>());
items[i] = (OSINFO)Marshal.PtrToStructure(itemPtr, typeof(OSINFO));
Console.WriteLine($"OS 버전: {items[i].osVersion}, 이름: {items[i].versionString}");
}
}
finally
{
Marshal.FreeHGlobal(ptr);
}
복합 구조체 및 배열 처리
C++ 복합 구조체 정의
struct Student
{
char name[20];
int age;
double scores[30];
};
struct ClassRoom
{
int classId;
Student students[50];
};
extern "C" __declspec(dllexport) int PopulateClass(ClassRoom* classroom, int size);
C#에서의 매핑 및 사용
[StructLayout(LayoutKind.Sequential)]
public struct Student
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string name;
public int age;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
public double[] scores;
}
[StructLayout(LayoutKind.Sequential)]
public struct ClassRoom
{
public int classId;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)]
public Student[] students;
}
[DllImport("jnalib.dll", EntryPoint = "PopulateClass")]
public static extern int PopulateClass(IntPtr buffer, int size);
// 사용 예시
int totalSize = Marshal.SizeOf<ClassRoom>() * 50;
IntPtr memoryBlock = Marshal.AllocHGlobal(totalSize);
try
{
PopulateClass(memoryBlock, 50);
ClassRoom[] classrooms = new ClassRoom[50];
for (int i = 0; i < 50; i++)
{
IntPtr itemPtr = new IntPtr(memoryBlock.ToInt64() + i * Marshal.SizeOf<ClassRoom>());
classrooms[i] = (ClassRoom)Marshal.PtrToStructure(itemPtr, typeof(ClassRoom));
}
}
finally
{
Marshal.FreeHGlobal(memoryBlock);
}
입력 파라미터로 구조체 설정
대규모 구조체를 입력으로 전달할 때는 Marshal.StructureToPtr를 사용해 메모리에 직접 쓰기 가능:
Student studentData = new Student();
// 값 설정...
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<Student>());
Marshal.StructureToPtr(studentData, ptr, false); // false: 복사 후 해제
// 사용 후 반드시 해제 필요
Marshal.FreeHGlobal(ptr);