How to set a marker to a pointer to an array of structures?

My C listings are as follows:

int myData(uint myHandle, tchar *dataName, long *Time, uint *maxData, DATASTRUCT **data); typedef struct { byte Rel; __int64 Time; char Validated; unsigned char Data[1]; } DATASTRUCT ; 

My C # declarations are as follows:

 [DllImport("myData.dll", EntryPoint = "myData")] public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out long Time, out uint maxData, ref DATASTRUCT[] data); [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct DATASTRUCT { public sbyte Rel; public long Time; public byte Validated; public double Data; } 

Then I call the managed function as follows:

 string dataToShow = "description"; long Time; uint maxData; // How many structs will be returned, ie how much data is available uint myHandle = 1; DATASTRUCT[] dataInformation = new DATASTRUCT[3]; // doesn't matter what I specify as the array size? myData(myHandle, dataToShow, out Time, out maxData, ref dataInformation); 

After execution, the above function will return successfully with only one structure, although there are 3 to return. Why is this so?

Additional Information; I tried passing a pointer to an array of structures as follows:

 - ref DATASTRUCT[] data; // Works but only returns one struct - [Out, MarshalAs(UnmanagedType.LPArray)] DATASTRUCT[] data; // returns the number of defined structs with garbage 

As I understand it, I may need to do manual sorting using IntPtr , but I don’t know how to implement this, so any advice would be appreciated.

+6
source share
2 answers

Well, it seems that your native library performs the selection, so really all you have to do is provide a pointer through which you can access the selected data.

Change the API definition to (note: I changed the maxData parameter to uint, long - 64 bits in .NET and 32 bits in native.

 [DllImportAttribute("myData.dll", EntryPoint = "myData")] public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out uint Time, out uint maxData, out IntPtr pData); 

At the top of my head, I can't remember if you need the out keyword for the final parameter, but I think so.

Then call myData:

 uint nAllocs = 0, time = 0; IntPtr pAllocs = IntPtr.Zero; myData(1, "description", out time, out nAllocs, out pAllocs); 

Now pAllocs should point to unmanaged memory so as not to reinstall them into managed memory:

 [StructLayoutAttribute(LayoutKind.Sequential, Pack = 1)] public struct DATASTRUCT { public byte Rel; public long Time; public byte Validated; public IntPtr Data; //pointer to unmanaged string. } int szStruct = Marshal.SizeOf(typeof(DATASTRUCT)); DATASTRUCT[] localStructs = new DATASTRUCT[nAllocs]; for(uint i = 0; i < nallocs; i++) localStructs[i] = (DATASTRUCT)Marshal.PtrToStructure(new IntPtr(pAllocs.ToInt32() + (szStruct * i)), typeof(DATASTRUCT)); 

And now you should have an array of local structures.

Note You may need to compile the project as x86 in order to standardize the IntPtr size to 4 bytes (DWORD) instead of AnyCPU by default of 8.

+7
source

A pointer to a pointer may be represented in your dllimport declaration as a ref IntPtr, so your declaration will be as follows:

 [DllImportAttribute("myData.dll", EntryPoint = "myData")] public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out long Time, out uint maxData, ref IntPtr data); 

(As an aside, I think long in C are just equivalent to int in C #. Long in C # is Int64, which would be long in C)

Routing your DATASTRUCT [] to IntPtr can be done using the GCHandle class

 DATASTRUCT [] dataInformation = new DATASTRUCT[3]; GCHandle gch = GCHandle.Alloc(dataInformation , GCHandleType.Pinned); IntPtr ptr = gch.AddrOfPinnedObject(); myData(myHandle, dataToShow, out Time, out maxData, ref ptr); //It absolutely essential you do this next bit so the object can be garbage collected again, //but it should only be done once the unmanaged code is definitely done with the reference. gch.Free(); 

Using the Marshal class and the StructureToPtr or Copy methods would also be an option, but in order to prove the concept, at least GCHandle should do the trick, it’s just not ideal for scenarios where unmanaged code works for a long time because you bound this object in place, and GC cannot move it until you release it.

0
source

Source: https://habr.com/ru/post/918631/


All Articles