将C#struct传递给C ++非托管DLL,返回错误的结果

时间:2017-11-22 06:10:02

标签: c# c++ pinvoke marshalling

我在Visual Studio 2017中开发了一个简单的C ++ win32 DLL,并在64位环境中编译,具有以下代码:

typedef struct sum {
    struct  {
        int num1;
        int num2;
    } nums;
} sum1;

extern "C" {

__declspec(dllexport) int initialize(sum1 *summing)
{
    int res;
    res = summing->nums.num1 + summing->nums.num2;
    return res;
}

}

上面的代码包含一个方法,它通过将typedef结构作为参数来返回两个整数的总和。

我有一个C#客户端应用程序,它使用PInvoke来使用这个Win32 C ++ DLL。以下是我的C#客户端应用程序的代码:

[StructLayout(LayoutKind.Sequential)]
public struct nums
{
    public int a;
    public int b;
}

[StructLayout(LayoutKind.Sequential)]
public struct mydef
{
    public IntPtr sum;
}

public class LibWrap
{    
    [DllImport("C++.dll", EntryPoint = "initialize")]
    public static extern int Initialize(ref mydef mydef);
}

class Program
{
    static void Main(string[] args)
    {
        mydef mydef = new mydef();
        nums nums;
        nums.a = 6;
        nums.b = 6;

        IntPtr buffer1 = Marshal.AllocCoTaskMem(Marshal.SizeOf(nums));
        Marshal.StructureToPtr(nums, buffer1, false);
        mydef.sum = buffer1;

        int res = LibWrap.Initialize(ref mydef);

        Console.WriteLine(res);
    }
}

通过上面的代码,我期待着' 12'作为输出,但我正在得到' -1504178328'作为输出。

我是一名C#开发人员,根本没有C ++经验。请帮我解决这个问题。

2 个答案:

答案 0 :(得分:0)

使用更简单的P / Invoke包装器:

public static class LibWrap
{
    [DllImport("C++.dll", EntryPoint = "initialize")]
    public static extern int Initialize(ref Nums nums);

    [StructLayout(LayoutKind.Sequential)]
    public struct Nums
    {
        public int a;
        public int b;
    }
}

并像这样使用它:

void CSharpExample()
{
    LibWrap.Nums nums;
    nums.a = 6;
    nums.b = 7;
    int res = LibWrap.Initialize(ref nums);
    Console.WriteLine(res);
}

在您的示例中,您不需要任何内存分配和编组,因为:

  • LibWrap.Nums是一个结构,因此nums中的局部变量CSharpExample()完全分配在堆栈上。
  • LibWrap.Nums的托管结构ref传递给LibWrap.Initialize会将指针传递给堆栈上的本地变量nums
  • LibWrap.Initialize被同步调用,因此在LibWrap.Initialize函数退出后,您传递给它的指针不会被使用。这很重要,因为只要CSharpExample()退出,指针就会变为无效。

答案 1 :(得分:0)

在C#端,您没有正确处理嵌套结构。试试这个:

[StructLayout(LayoutKind.Sequential)]
public struct mynums {
    public int num1;
    public int num2;
}

[StructLayout(LayoutKind.Sequential)]
public struct sum1 {
    public mynums nums;
}

public class LibWrap {
    [DllImport("C++.dll", EntryPoint = "initialize")]
    public static extern int Initialize(ref sum1 summing);
}

class Program {
    static void Main(string[] args) {
        sum1 mysum;
        mysum.nums.num1 = 6;
        mysum.nums.num2 = 6;
        int res = LibWrap.Initialize(ref mysum);
        Console.WriteLine(res);
    }
}

话虽这么说,拥有一个唯一数据成员是另一个结构的结构是多余的,没有必要。您应该完全删除外部结构,例如:

struct nums {
    int num1;
    int num2;
};

extern "C" {

__declspec(dllexport) int initialize(nums *summing) {
    return summing->num1 + summing->num2;
}

}
[StructLayout(LayoutKind.Sequential)]
public struct nums {
    public int num1;
    public int num2;
}

public class LibWrap {
    [DllImport("C++.dll", EntryPoint = "initialize")]
    public static extern int Initialize(ref nums summing);
}

class Program {
    static void Main(string[] args) {
        nums mynums;
        mynums.num1 = 6;
        mynums.num2 = 6;
        int res = LibWrap.Initialize(ref mynums);
        Console.WriteLine(res);
    }
}