简体   繁体   English

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

[英]Passing C# struct to C++ unmanaged DLL returning incorrect result

I have a simple C++ win32 DLL developed in visual studio 2017 and compiled in 64 bit environment having the following code: 我有一个在Visual Studio 2017中开发并在64位环境中编译的简单C ++ win32 DLL,具有以下代码:

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

}

The above code contains a method which returns the sum of two integers by taking a typedef struct as an argument. 上面的代码包含一个方法,该方法通过将typedef结构作为参数返回两个整数的和。

I have a C# client application which consumes this Win32 C++ DLL using PInvoke. 我有一个使用PInvoke消耗此Win32 C ++ DLL的C#客户端应用程序。 Following is the code of my C# client application: 以下是我的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);
    }
}

With the above code, I am expecting '12' as output, but instead I am getting '-1504178328' as output. 使用上面的代码,我期望输出为'12',但是我将输出为'-1504178328'。

I am a C# developer with no experience in C++ at all. 我是一位完全没有C ++经验的C#开发人员。 Please help me to solve this problem. 请帮我解决这个问题。

Use a simpler P/Invoke wrapper: 使用更简单的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;
    }
}

and use it like this: 并像这样使用它:

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

On the C# side, you are not handling the nested struct correctly. 在C#端,您没有正确处理嵌套结构。 Try this instead: 尝试以下方法:

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

That being said, having a struct whose sole data member is another struct is redundant and unnecessary. 话虽这么说,拥有唯一的数据成员是另一个结构的结构是多余的和不必要的。 You should remove the outer struct altogether, eg: 您应该完全删除外部结构,例如:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM