我如何马歇尔一个指向结构数组的指针?(How do I marshall a pointer to

2019-06-25 15:32发布

我的C声明如下:

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 ;

我的C#声明如下:

[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;
}

那么我所说的管理功能如下:

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);

在执行上面的功能只有一个结构成功返回即使有3回。 为什么会这样?

附加信息; 我已经尝试了指针传递到结构通过以下方式的阵列的指针:

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

据我所知,我可能需要做一些使用手工编组IntPtr ,我不知道但是如何实现这一点,所以任何建议,将不胜感激。

Answer 1:

好吧,这好像你的本地库不分配,所以真的,所有你需要做的是提供通过它可以访问分配的数据的指针。

改变你的API定义(注意,我改变了MAXDATA参数去UINT,长在.NET 64位,并在本机32位。

[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);

关闭我的头顶,我不太记得了,如果你需要的最后一个参数out关键字,但我是这么认为的。

然后,调用myData的:

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

现在,pAllocs应该指向非托管内存,元帅到这些托管内存是不是太困难:

[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));

现在,你应该有本地结构的数组。

的一点要注意 ,您可能需要设置你的项目编译为X86,到一个IntPtr的尺寸标准化为4个字节(DWORD),而不是AnyCPU默认的8。



Answer 2:

指针的指针可以在你的dllimport的声明为ref IntPtr的数据来表现,所以你的宣言将成为:

[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);

(顺便说一句,我觉得长在C是只是相当于在C#中的int。在C#中龙是一个Int64,这将是一个漫长的C)

编组你DATASTRUCT []到一个IntPtr可以使用的GCHandle类完成

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's 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(); 

使用Marshal类和它的StructureToPtr或复制的方法也将是一种选择,而是为了证明至少的GCHandle应该做的伎俩概念的目的,它只是不理想的情况下,其中的非托管代码没有长时间运行的操作,因为你“已经就位固定该对象,直到你释放它的GC不能移动它。



文章来源: How do I marshall a pointer to a pointer of an array of structures?