如何正确地将结构指针从 C# 传递到 C DLL

How to properly pass struct pointer from C# to C DLL

我需要从 C DLL 导出函数。这是我写的例子

typedef struct tag_struct {
  unsigned char first;
  unsigned char second;
} st_struct;

__declspec(dllexport) unsigned char Func( st_struct *ptr )
{
    return ptr->first + ptr->second;
}

这是我用来导入上述函数的 C# 代码。

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace ImportTest
{
    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    public class st_struct
    {
        public byte first;
        public byte second;
    }

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            st_struct x = new st_struct();

            x.first = 1;
            x.second = 2;

            byte result = Func(ref x);
        }

        [DllImport("MarshalTest.dll")]
        protected static extern byte Func(ref st_struct inputs);
    }
}

我的问题是 Func 的 return 值不是 3,它应该是 (1 + 2)。

我正在使用调试器查看 DLL 中的值 - 它们不同(不是我提供的 1 和 2)。

当我像这样更改 C# 代码时,函数 return 的正确值:

public Form1()
{
    InitializeComponent();

    st_struct x = new st_struct();

    x.first = 1;
    x.second = 2;

    byte result = Func(x);
}

[DllImport("MarshalTest.dll")]
protected static extern byte Func(st_struct inputs);

删除 ref 后问题消失。但是我不明白为什么。

你能解释一下吗?

您可能认为 ref 关键字是 passing parameter by reference 所必需的。但是由于st_struct定义为引用类型(class是引用类型),所以参数是按引用传递的,不是按值传递的。您不需要 ref 关键字。

如果 st_struct 被定义为 struct,当您使用 ref 关键字时,您可能会发现它有效。

正如@kennyzx 所提到的,由于您的 st_struct 是一个 class,它已经是一个引用类型并将作为指针传递。我怀疑将 ref 放在上面会给你一个双指针,这在混合托管和非托管代码时没有多大意义。如果指针发生变化,编组器可能会处理它并为您创建一个新对象,但这似乎是一件粗略的事情。

因此,当在没有 ref 的情况下传递 class 时,它会按预期工作(C 代码获得一个指针)。如果将其更改为 struct,则在不使用 ref 的情况下传递它应该将其传递到堆栈上,而使用 ref 传递它会将其作为指针传递。

使用 struct 在你的情况下似乎是显而易见的选择,因为它可以直接传递(CLR 只需要固定它并传递一个指针)。使用 class 我怀疑会涉及更多编组。