调用非托管函数char返回char *

我在非托管C / C ++代码(dll)中有一个函数,它返回一个包含char数组的结构。 我创建了C#struct来接收调用该函数的返回值。 并且调用此函数的uppon得到’System.Runtime.InteropServices.MarshalDirectiveException’

这是C声明:

typedef struct T_SAMPLE_STRUCT { int num; char text[20]; } SAMPLE_STRUCT; SAMPLE_STRUCT sampleFunction( SAMPLE_STRUCT ss ); 

这是C#声明:

 struct SAMPLE_STRUCT { public int num; public string text; } class Dllwrapper { [DllImport("samplecdll.dll")] public static extern SAMPLE_STRUCT sampleFunction(SAMPLE_STRUCT ss); } 

我使用1字节ASCII。

有没有人有如何做到这一点的提示或解决方案?

转换C数组成员的技巧是使用MarshalAs(UnmanagedType.ByValTStr)。 这可以用来告诉CLR将数组编组为内联成员与普通非内联数组。 请尝试以下签名。

 [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet=System.Runtime.InteropServices.CharSet.Ansi)] public struct T_SAMPLE_STRUCT { /// int public int num; /// char[20] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst=20)] public string text; } public partial class NativeMethods { /// Return Type: SAMPLE_STRUCT->T_SAMPLE_STRUCT ///ss: SAMPLE_STRUCT->T_SAMPLE_STRUCT [System.Runtime.InteropServices.DllImportAttribute("", EntryPoint="sampleFunction")] public static extern T_SAMPLE_STRUCT sampleFunction(T_SAMPLE_STRUCT ss) ; } 

此签名由CodePlex上提供的PInovke Interop Assistant( 链接 )提供给您。 它可以自动将大多数PInvoke签名从本机代码转换为C#或VB.Net。

对于P / Invoke编组来说,这不是一个简单的结构:它更容易编组包含char *而不是char []的结构(尽管你的问题是从非托管代码中分配char *然后从中释放它托管代码)。

假设您坚持使用当前设计,一个选项是将字符串数组声明为:

 public fixed char text[20]; 

不幸的是,您必须将unsafe关键字添加到访问此数组的任何代码中。

C中的结构定义:

 #pragma pack(push, 1) typedef struct T_SAMPLE_STRUCT { int num; char text[20]; }; #pragma pack(pop) 

C#中的定义:

 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] public struct T_SAMPLE_STRUCT { [MarshalAs(UnmanagedType.I4)] public int num; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)] public string text; } 

我已经设法通过将function分离到:

 void receiveStruct( SAMPLE_STRUCT ss ) void returnStruct(SAMPLE_STRUCT &ss) 

我改变了结构定义,因为JaredPar告诉我:

 [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct T_SAMPLE_STRUCT { /// int public int num; /// char[20] [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 20)] public string text; } 

现在它有效。

谢谢!

有趣的是,既然你已经有了答案,那么这里的基本问题是结构只需要合适的大小。 由于托管类型没有内联数组,因此您只需要弥补原本需要的空间。 在C ++ / CLI中编写时,您经常会看到带有显式Size参数的StructLayoutAttribute。 这使得运行时为该类型分配了适当的内存量,这使得它可以对本机端进行blittable。 因此,这些也应该有效:

 [StructLayout(LayoutKind.Sequential, Size=24)] public struct T_SAMPLE_STRUCT { public int num; // to get the string here, you'd need to get a pointer public char firstChar; } // or [StructLayout(LayoutKind.Sequential)] public struct T_SAMPLE_STRUCT { public int num; public byte c0; public byte c1; public byte c2; public byte c3; public byte c4; public byte c5; public byte c6; public byte c7; public byte c8; public byte c9; public byte c10; public byte c11; public byte c12; public byte c13; public byte c14; public byte c15; public byte c16; public byte c17; public byte c18; public byte c19; } 

当然,这些在托管代码中使用起来要困难得多(您需要复制内存或使用指针),但它们说明了blittable类型的概念,主要是在本机代码和托管代码之间传递类型的方式。

首先,你必须在struct上放置StructLayout [Sequential]属性,我认为它会起作用

 [ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )] struct SAMPLE_STRUCT { public int num; [ MarshalAs( UnmanagedType.ByValArray, SizeConst=20 )] public char[] text; }