티스토리 뷰
C/C++ 에서 작성한 코드를 C#에서 사용하기 위한 작업을 하던 중, 가변길이 포인터 배열에 대해 변환하는 방법에 대해 공유합니다.
샘플로 만든 C 코드는 아래와 같습니다.
typedef struct { char *name; int age; } my_data; typedef struct { int len; my_data **data_list; } my_data_list; __declspec(dllexport) void print_my_data_list(my_data_list *list) { int i; if (!list) { printf("[ERROR] no list\n"); return; } printf("[INFO] list length : %d\n", list->len); for (i = 0; i < list->len; i++) { print_my_data(list->data_list[i]); } }
목표는 print_my_data_list 를 C#에서 호출하는 것입니다.
my_data_list struct에는 my_data pointer array를 위한 double pointer 멤버(data_list)가 있습니다.
이를 위한 C#코드는 아래와 같습니다.
[Serializable] [StructLayout(LayoutKind.Sequential)] public struct MyData { public string Name; public int Age; } [Serializable] [StructLayout(LayoutKind.Sequential)] public struct MyDataList { public int Len; public IntPtr List; // MyData pointer list (MyData **data_list) } class MyLib { [DllImport("MyLib.dll", EntryPoint = "print_my_data_list")] public extern static void PrintMyDataList(ref MyDataList list); [DllImport("MyLib.dll", EntryPoint = "print_my_data")] public extern static void PrintMyData(ref MyData data); }
배열의 길이가 고정이었다면 ConstSize로 미리 만들 수 있겠지만, 가변 길이 배열이기 떄문에 IntPtr로 선언하였습니다.
이 포인터(IntPtr)의 크기를 배열의 길이 x 포인터(IntPtr)의 크기로 Alloc하고,
배열 인덱스 위치에 MyData struct data를 alloc합니다.
코드는 아래와 같습니다.
IntPtr ptr; MyData[] data = { new MyData() { Name = "aaa", Age = 10 }, new MyData() { Name = "bbb", Age = 20 }, new MyData() { Name = "ccc", Age = 30 } }; MyDataList list = new MyDataList(); list.Len = data.Length; // 포인터의 크기 * 배열 길이만큼 Alloc list.List = Marshal.AllocHGlobal(Marshal.SizeOf() * data.Length); try { for (int i = 0; i < data.Length; i++) { // MyData 마샬링 ptr = Marshal.AllocHGlobal(Marshal.SizeOf(data[0])); Marshal.StructureToPtr(data[i], ptr, false); // list[포인터크기 * 배열 index] 위치에 포인터 복사 Marshal.StructureToPtr(ptr, (IntPtr)((int)list.List + Marshal.SizeOf () * i), false); } MyLib.PrintMyDataList(ref list); } catch (Exception e) { Console.Write("Error: " + e.Message); }
실행을 해보면 잘 동작하는 것을 확인할 수 있습니다.
반응형
댓글