我在非托管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。
有没有人有关于如何做到这一点的提示或解决方案?
答案 0 :(得分:5)
转换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("<Unknown>", EntryPoint="sampleFunction")]
public static extern T_SAMPLE_STRUCT sampleFunction(T_SAMPLE_STRUCT ss) ;
}
此签名由CodePlex上提供的PInovke Interop助手(link)提供。它可以自动将大多数PInvoke签名从本机代码转换为C#或VB.Net。
答案 1 :(得分:2)
这不是P / Invoke编组的简单结构:编组包含char *而不是char []的结构更容易(尽管你的问题是从非托管代码中分配char *以及稍后从托管代码中解放出来。)
假设您坚持使用当前设计,一个选项是将字符串数组声明为:
public fixed char text[20];
不幸的是,您必须将unsafe
关键字添加到访问此数组的任何代码中。
答案 2 :(得分:2)
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;
}
答案 3 :(得分:0)
我设法通过将功能分离到:
来实现这一目的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;
}
现在它有效。
谢谢!
答案 4 :(得分:0)
有趣的是,既然你已经有了答案,那么这里的基本问题是结构只需要合适的大小。由于托管类型没有内联数组,因此您只需要弥补原本需要的空间。在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类型的概念,主要是如何在本机代码和托管代码之间传递类型。
答案 5 :(得分:-1)
首先你要放 您的结构上的StructLayout [Sequential]属性,我认为它将起作用
[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
struct SAMPLE_STRUCT
{
public int num;
[ MarshalAs( UnmanagedType.ByValArray, SizeConst=20 )]
public char[] text;
}