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

时间:2009-04-27 11:51:50

标签: c# .net c pinvoke unmanaged

我在非托管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。

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

6 个答案:

答案 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;
}