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

时间:2011-01-02 07:45:20

标签: c# pinvoke marshalling

我有以下C结构

struct XYZ
{
void            *a;
char            fn[MAX_FN];     
unsigned long   l;          
unsigned long   o;  
};

我想从C#调用以下函数:

extern "C"  int     func(int handle, int *numEntries, XYZ *xyzTbl);

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

我定义了以下C#结构:

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

当然它不起作用。有人能说清楚我做错了吗?

4 个答案:

答案 0 :(得分:1)

[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),因此这里的第二行是冗余的(结构总是被初始化)

 [DllImport(@"xyzdll.dll", CallingConvention = CallingConvention.Cdecl)]
 public static extern Int32 func(Int32 handle, ref Int32 numntries,
 [MarshalAs(UnmanagedType.LPArray)] XYZ[] arr);

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

答案 1 :(得分:0)

如果您拥有托管结构,我认为您不能使用LPArray。只需将其取出,然后使用[In][Out](如果需要)。

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


编辑:

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

答案 2 :(得分:0)

检查一下:Marshal C++ struct array into C#,可能会有所帮助。

答案 3 :(得分:0)

我会手动编组。首先,将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(handle, ref numEntries, unmanaged);然后,该作业将非托管内存解组为托管类型。

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