Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
196 views
in Technique[技术] by (71.8m points)

c# - How do I marshall a pointer to a pointer of an array of structures?

My C declarations 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;
}

I then call the managed function as follows:

string dataToShow = "description";
long Time;
uint maxData; // How many structs will be returned, i.e. 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);

Upon execution the above function will return successfully with only one structure even though there are 3 to return. Why is this so?

Additional information; I have tried passing the pointer to a pointer of an array of structs the following ways:

- 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 might need to do some manual marshalling using IntPtr, I do not know how to implement this however, so any advice would be appreciated.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Okay, it seems as though your native library does the allocation, so really all you need to do is provide a pointer through which you can access the allocated data.

Change your API definition to (note, I changed the maxData param to uint, long is 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);

Off the top of my head I can't quite 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, to marshal these into managed memory isn't too difficult:

[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 structs.

A point to note You may need to set your project to compile as x86, to standardize the size of an IntPtr to 4 bytes (DWORD) instead of AnyCPU's default 8.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...