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

问题描述:

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

I have a simple C++ win32 DLL developed in visual studio 2017 and compiled in 64 bit environment having the following code:

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结构作为参数来返回两个整数的和.

The above code contains a method which returns the sum of two integers by taking a typedef struct as an argument.

我有一个C#客户端应用程序,该应用程序使用PInvoke使用此Win32 C ++ DLL.以下是我的C#客户端应用程序的代码:

I have a C# client application which consumes this Win32 C++ DLL using PInvoke. Following is the code of my C# client application:

[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'.

With the above code, I am expecting '12' as output, but instead I am getting '-1504178328' as output.

我是一位C#开发人员,完全没有C ++经验.请帮我解决这个问题.

I am a C# developer with no experience in C++ at all. Please help me to solve this problem.

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

Use a simpler P/Invoke wrapper:

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);
}

在您的示例中,您不需要任何内存分配和封送处理,因为:

In your example, you don't need any memory allocation and marshaling, because:

  • LibWrap.Nums是一个结构,因此CSharpExample()中的局部变量nums完全在堆栈上分配.
  • 将托管结构LibWrap.Numsref传递给LibWrap.Initialize会将指针传递给堆栈上的局部变量nums.
  • LibWrap.Initialize被同步调用,因此传递给它的指针在LibWrap.Initialize函数退出后不会在任何地方使用.这很重要,因为CSharpExample()退出后指针立即变为无效.
  • LibWrap.Nums is a struct, thus local variable nums in CSharpExample() is allocated completely on stack.
  • passing managed struct LibWrap.Nums by ref to LibWrap.Initialize will pass the pointer to local variable nums on stack.
  • LibWrap.Initialize is called synchronously, so that the pointer you pass to it isn't used anywhere after LibWrap.Initialize function exits. This is important because the pointer becomes invalid as soon as CSharpExample() exits.