从C#调用接受调用者分配的结构数组的C函数

从C#调用接受调用者分配的结构数组的C函数,c#,pinvoke,marshalling,C#,Pinvoke,Marshalling,我有下面的C结构 struct XYZ { void *a; char fn[MAX_FN]; unsigned long l; unsigned long o; }; 我想从C#调用以下函数: 其中,xyzTbl是由调用者分配的大小为numEntires的XYZ数组 我定义了以下C#struct: [StructLayoutAttribute(Sequential, CharSet = CharSet.An

我有下面的C结构

struct XYZ
{
void            *a;
char            fn[MAX_FN];     
unsigned long   l;          
unsigned long   o;  
};
我想从C#调用以下函数:

其中,xyzTbl是由调用者分配的大小为numEntires的XYZ数组

我定义了以下C#struct:

[StructLayoutAttribute(Sequential, CharSet = CharSet.Ansi)]
public struct XYZ
{
   public System.IntPtr rva;
   [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]
   public string fn;
   public uint l;
   public uint o;
}
以及一种方法:

 [DllImport(@"xyzdll.dll", CallingConvention = CallingConvention.Cdecl)]
 public static extern Int32 func(Int32 handle, ref Int32 numntries,
     [MarshalAs(UnmanagedType.LPArray)] XYZ[] arr);
然后我尝试调用函数:

XYZ xyz = new XYZ[numEntries];
for (...) xyz[i] = new XYZ();
func(handle,numEntries,xyz);

当然,这是行不通的。有人能说明我做错了什么吗?

我不相信当你有一个托管结构时,你可以使用
LPArray
。把它拿出来,用
[In]
[out]
(如果需要的话)代替

IIRC,如果使用
LPArray
,则会尝试传递指向第一个元素的指针,这是非法的,因为该结构不可blittable。您需要完全删除
[MarshalAs(…)]


编辑:

我不记得了,但是您可能需要在传递字符串字段之前初始化它们。。。当我有机会的时候,我会检查的。

检查这个:,可能会有帮助

[StructLayoutAttribute(Sequential, CharSet = CharSet.Ansi)]
public struct XYZ
{
   public System.IntPtr rva;
   [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]
   public string fn;
   public uint l;
   public uint o;
}
那些
uint
不应该是
ulong
?还有,MAX_FN是128对吗

XYZ xyz = new XYZ[numEntries];
for (...) xyz[i] = new XYZ(); 
XYZ是一种值类型(struct),因此这里的第二行是冗余的(struct总是初始化的)


[marshallas(UnmanagedType.LPArray)]
是冗余的,编译器会看到它是一个结构数组。

我会手动封送它。首先,将xyzTbl设置为
IntPtr

[DllImport(@"xyzdll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 func(Int32 handle, ref Int32 numntries, IntPtr xyzTb);
与其像现在这样分配XYZ数组,不如分配足够的非托管内存来存储表

IntPtr unmanaged = 
    Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XYZ)) * numEntries);
调用您的
func(句柄,ref numEntries,非托管)然后,任务是将非托管内存解编回托管类型

IntPtr[] entries = new IntPtr[numEntries];
List<XYZ> xyz = new List<XYZ>();
Marshal.Copy(unmanaged, entries, 0, numEntries);
foreach (IntPtr entry in entries)
    xyz.Add(Marshal.PtrToStructure(entry, typeof(XYZ)));

Marsha.FreeHGlobal(unmanaged);
IntPtr[]条目=新的IntPtr[numEntries];
List xyz=新列表();
Marshal.Copy(非托管,条目,0,numEntries);
foreach(条目中的IntPtr条目)
Add(Marshal.PtrToStructure(条目,typeof(xyz));
Marsha.FreeHGlobal(非托管);

顺便问一下,有什么错误吗?调用
func
时,第二个参数前面需要一个
ref
,因为它在函数声明中是
ref
。谢谢Nickolay,你说的帖子不一样。我需要在C#中分配结构数组……是的,实际上。我还发现,MSDN告诉您应该(可能)也传递SizeConst参数。您好,我尝试了您的建议,但它不起作用。我认为编组是必需的,因为C和C++代表不同的数组。我可以创建C#数组作为连续的结构块吗?@lifey:Hm。。。你的错误是什么?知道这一点真的会有帮助。是的,封送处理确实需要发生,只是我不认为您将其显式指定为
LPArray
。只有在非托管代码将对传入内存的引用保留在被调用函数返回点之外时,才需要这种手动封送。而且您的终结代码也不是很健壮。但这很可能只在少数应用中起作用。通常是那些需要干净卸载AppDomains的应用程序。
IntPtr unmanaged = 
    Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XYZ)) * numEntries);
IntPtr[] entries = new IntPtr[numEntries];
List<XYZ> xyz = new List<XYZ>();
Marshal.Copy(unmanaged, entries, 0, numEntries);
foreach (IntPtr entry in entries)
    xyz.Add(Marshal.PtrToStructure(entry, typeof(XYZ)));

Marsha.FreeHGlobal(unmanaged);