C# 如何将指针封送到结构数组的指针?

C# 如何将指针封送到结构数组的指针?,c#,.net,interop,pinvoke,marshalling,C#,.net,Interop,Pinvoke,Marshalling,我的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 ; [DllImport("myData.dll", EntryPoint = "myData"

我的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 ;
[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);
我的声明如下:

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 ;
[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);
然后,我调用托管函数,如下所示:

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 ;
[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
进行一些手动编组,但我不知道如何实现这一点,因此请提供任何建议。

指向指针的指针可以在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中的long与C#中的int是等价的。C#中的long是Int64,这在C中应该是long-long)

可以使用GCHandle类将DATASTRUCT[]编组到IntPtr

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或Copy方法也是一种选择,但为了证明这一概念,至少GCHandle应该做到这一点,对于非托管代码执行长时间运行操作的场景来说,这并不理想,因为您已将此对象固定在适当的位置,而GC在您释放它之前无法移动它。

好的,似乎您的本机库进行了分配,所以实际上,您需要做的就是提供一个指针,通过它可以访问分配的数据

将API定义更改为(注意,我将maxData参数更改为uint,long在.NET中为64位,在native中为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));
[StructLayoutAttribute(LayoutKind.Sequential,Pack=1)]
公共结构数据结构
{
公共字节Rel;
公众参与时间长;
公共字节验证;
public IntPtr Data;//指向非托管字符串的指针。
}
int szStruct=Marshal.SizeOf(typeof(DATASTRUCT));
DATASTRUCT[]localStructs=newdatastruct[nAllocs];
对于(uint i=0;i
现在应该有一个本地结构数组

注意事项
您可能需要将项目设置为编译为x86,以便将IntPtr的大小标准化为4字节(DWORD)而不是AnyCPU的默认8。

C函数是如何分配结构的?它是使用输入数组作为缓冲区,还是使用malloc生成新的结构?哦,而且你的C#声明看起来是错误的。无符号字符的大小与双精度字符不同(1对8字节)@JasonLarke,不幸的是,DLL库文档没有说明它是否为结构分配内存。我假设它没有。我没有为您提供
数据
的完全减速,事实上它是一个最多可以占用8字节的变量char,因此将其编组为双精度的原因是t每次我测试他的变量时,它肯定会返回正确的结果。请尝试此[Out,Marshallas(UnmanagedType.LPArray,SizeParamIndex:=3)]Out DATASTRUCT[]data;@user629926,当调用此变量时,我会遇到一个异常作为
myData(myHandle,dataToShow,Out time,Out maxData,Out dataInformation);
删除
输出
返回与
[out,marshallas(UnmanagedType.LPArray)]数据结构[]类似的输出data;
我尝试了上述操作,它返回dataInformation数组中的3个结构,但是它们都是空的。我还编辑了我的原始帖子,以适当地引用Int64,因为它实际上是一个u Int64。您是否也更新了c#struct定义?原始文档中的无符号字符不会转换为双精度的@JasonLarke在其他注释中观察到。如果你得到结构的大小,它是否与C代码中预期的大小相匹配?可能值得尝试封送并只传递指向字节数组而不是数据结构数组的指针。至少你应该能够看到其中是否有字节。是的,我有还更新了结构,
unsigned char Data[1];
正在返回我认为正确的值。我如何检查当前结构大小是否与C代码匹配?正如我所说,在C#Marshal.SizeOf(typeof(DATASTRUCT))中使用
ref DATASTRUCT[]Data;
时,肯定有数据返回1 struct ok将为您提供整个结构所需的字节大小。我不知道您如何在C中获得它,可能只是根据定义手动计算。我在C代码中设置为7个字节。谢谢Jason,所以我相信我们已经取得了一些进展。我现在得到了返回的结构的正确数量以及填充结构的数据。使用public IntPtr Data;
数据返回的数据看起来像垃圾,如果我将其更改回
public double Data;
它将正确返回数据。