One way to do this is to use the C# Marshal function StructureToPtr in a loop. In most native languages (such as C), an array is stored in a contiguous region of memory. The size of the region is the size of one structure times the number of structures in the array. The structures are stored one after the other in memory. The C# language has an array type, but it uses managed memory so the layout is hidden. This is why using the C# Marshal class is necessary. It is used to translate the C# array to the native format. Without this translation, the native code can’t use the array. The following code example shows how to translate a C# array to a format suitable for native code using the C# Marshal:
using System; using System.Runtime.InteropServices;
namespace my_cs_namespace {
class MyCSApp { /* Size of a thing name */ public const int NAME_LEN = 64;
/* Declare the structure for a thing, this must match the native * library declaration. */ [StructLayout(LayoutKind.Sequential)] public struct thing_t {
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAME_LEN)] public string name;
public int height; /* cm */ };
/* Declare a native function that takes an array of things */ [DllImport(“test”, EntryPoint = “print_things”)] static extern int native_print_things(IntPtr t, int num_things);
/* This function translates a C# array of things to a native * array */ static int PrintThings(thing_t[] things) { int result; int num_things = things.Length; int thing_size = Marshal.SizeOf(typeof(thing_t)); IntPtr cbase, ccur;
/* Allocate contiguous memory for the array */ cbase = Marshal.AllocHGlobal(num_things * thing_size); ccur = cbase;
/* Layout each thing at the current position in the array */ for (int i = 0; i < num_things; i++, ccur += thing_size) { Marshal.StructureToPtr(things[i], ccur, false); }
/* Call the native function */ result = native_print_things(cbase, num_things);
/* Free the array and return the result */ Marshal.FreeHGlobal(cbase); return result; }
static void Main() { /* Create a C# array of things */ thing_t[] things = new thing_t[2]; things[0] = new thing_t(); things[0].name = “THING 1”; things[0].height = 96; /* cm */ things[1] = new thing_t(); things[1].name = “THING 2”; things[1].height = 102; /* cm */
PrintThings(things); } } }; |
The code first declares the structure using the StructLayout and MarshalAs attributes. These attributes tell C# about the exact layout the structure should have in memory. Next, the native function is declared using IntPtr as the type for the array. Native arrays are usually passed via pointers, and this type can be used for any pointer argument. The C# function PrintThings does the translation from the C# array to the native array. It first allocates space for the array, then fills out each structure element using the Marshal StructureToPtr function. After the translation is done, the native function can be called using the pointer to the allocated memory.