方法的类型签名不与PInvoke兼容

时间:2014-04-02 06:23:24

标签: c# c++ pinvoke marshalling dllimport

我正在尝试使用c dll中的函数,在c#应用程序中,每次我尝试运行应用程序并调用有问题的函数时,我都会遇到此错误。
起初我想也许这是因为我正在使用错误的签名,但我试图让它尽可能简单但没有运气。!
长话短说:
这是我实际的c dll源代码:

#include <stdio.h>
#include <string.h>
extern "C"
{
    struct teststruct
    {
        char acharacter;
        int  anumber;
        char* astring;
        char  anarray[10];
        const char* conststring;
    };

    __declspec(dllexport) teststruct  TestDLL()
    {
        teststruct stc;
        stc.acharacter = 'A';
        stc.anumber = 10;
        strcpy(stc.anarray, "Test");
        stc.astring = "astring!";
        stc.conststring = "Crash?";

        return stc;
    }
}

这是c#计数器部分:

    [StructLayout(LayoutKind.Sequential)]
public struct teststruct
{
    public char acharacter;
    public  int anumber;
    [MarshalAs(UnmanagedType.LPStr)]
    public string astring;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
    public char[] anarray;
    [MarshalAs(UnmanagedType.LPStr)]
    public string conststring;
}

namespace Tcp_connections_list
{

    public partial class Form1 : Form
    {

        [DllImport("simple c dll.dll",CallingConvention= CallingConvention.Cdecl)]
        public static extern teststruct TestDLL();

        public Form1()
        {
            InitializeComponent();
        }

        private void btnTestDll_Click(object sender, EventArgs e)
        {
            teststruct test = TestDLL(); //Crash at the very begining!
            textBox1.Text = test.acharacter.ToString();
            textBox1.Text = test.anarray.ToString();
            textBox1.Text = test.anumber.ToString();
            textBox1.Text = test.astring.ToString();
            textBox1.Text = test.conststring.ToString(); 


        }

    }
}

以下代码段给出了相同的确切错误,我将结构更改为

struct teststruct
{
    char acharacter;
    int  anumber;
};

和它的C#相当于

[StructLayout(LayoutKind.Sequential)]
public struct teststruct
{
    public char acharacter;
    public  int anumber;
}

尽可能简单,但我又得到了同样的错误! 我在这里缺少什么?

1 个答案:

答案 0 :(得分:5)

问题是空终止的C字符串的编组。你不能指望p / invoke marshaller处理函数返回值中的那些。结构需要像这样声明:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct teststruct
{
    public byte acharacter; // don't use C# char which is 2 bytes wide
    public int anumber; 
    public IntPtr astring;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
    public char[] anarray;
    public IntPtr conststring;
}

您需要使用Marshal.PtrToStringAnsi来提取C字符串的内容。

作为一个更一般的建议,将大型结构作为函数返回值传递是一个坏主意。这样做没有一个普遍接受的ABI。不同的编译器以不同的方式处理它。更好的方法是在调用代码中分配结构,并传递其地址。像这样:

__declspec(dllexport) void TestDLL(teststruct *ts)
{
    ts->... = ...;
    ...
}

在C#方面:

[DllImport(...)]
public static extern void TestDLL(out teststruct ts);

事实上,如果您尝试使用的结构不能作为返回值进行编组,那就不会让我感到惊讶。你真的应该把它作为out参数传递。