c# - 在结构中使用 boolean 值的一些 P/Invoke C# 到 C 编码问题

标签 c# c struct pinvoke boolean

我在处理 boolean 类型以及在 C# 和 C 之间来回编码结构时遇到一些问题。我对 C 很生疏,但希望这部分没有什么严重错误。

据我所读/所见,.NET Boolean 和C# bool 类型为 4 个字节长,而C 类型 bool 仅 1 个字节。由于内存占用的原因,我不知道在 C 代码中使用定义的 BOOL 4 字节版本

这是一些简单的测试代码,希望能让我的问题清楚:


C 代码:

typedef struct
{
        double SomeDouble1;
        double SomeDouble2;
        int SomeInteger;
        bool SomeBool1;
        bool SomeBool2;
} TestStruct;

extern "C" __declspec(dllexport) TestStruct* __stdcall TestGetBackStruct(TestStruct* structs);

__declspec(dllexport) TestStruct* __stdcall TestGetBackStruct(TestStruct* structs)
{
    return structs;
}

我使用以下定义在 C# 中调用此代码:

    [StructLayout(LayoutKind.Explicit)]
    public struct TestStruct
    {
        [FieldOffset(0)]
        public double SomeDouble1;
        [FieldOffset(8)]
        public double SomeDouble2;

        [FieldOffset(16)]
        public int SomeInteger;

        [FieldOffset(17), MarshalAs(UnmanagedType.I1)]
        public bool SomeBool1;
        [FieldOffset(18), MarshalAs(UnmanagedType.I1)]
        public bool SomeBool2;
    };

    [DllImport("Front.dll", CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
    public static extern IntPtr TestGetBackStruct([MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] TestStruct[] structs);

这是 C# 中的实际测试函数:

    [Test]
    public void Test_CheckStructParsing()
    {
        var theStruct = new TestStruct();
        theStruct.SomeDouble1 = 1.1;
        theStruct.SomeDouble2 = 1.2;
        theStruct.SomeInteger = 1;
        theStruct.SomeBool1 = true;
        theStruct.SomeBool2 = false;

        var structs = new TestStruct[] { theStruct };

        IntPtr ptr = TestGetBackStruct(structs);

        var resultStruct = (TestStruct)Marshal.PtrToStructure(ptr, typeof(TestStruct));
    }

这在某种意义上是有效的,我确实得到了一个结构(使用调试器来检查它),但值完全错误。 IE。编码根本不起作用。我尝试了不同版本的 C# 结构,但没有成功。这是我的问题(1 和 2 最重要):

  1. C 函数是否适合此目的?
  2. 如何正确编写结构体,以便将结构体中的正确值返回到 C#?(是否有必要使用 StructLayout(LayoutKind.Explicit) 定义结构体属性使用 FieldOffset 值还是我可以使用 StructLayout(LayoutKind.Sequential) 代替)?
  3. 由于我在 C 中返回一个指向 TestStruct 的指针,因此我想应该可以在 C# 中返回 TestStructs 数组。但这似乎无法使用 Marshal.PtrToStructure 函数实现。是否可以通过其他方式实现?
  4. 显然,可以通过使用相同的 FieldOffset 属性值让多个结构体字段指向相同的内存分配,从而使用 C 中称为“union ”的东西。我理解这一点,但我仍然还不知道这种情况何时有用。请赐教。
  5. 有人可以推荐一本关于 C# P/Invoke to C/C++ 的好书吗? 我有点厌倦了在网络上到处获取信息。

非常感谢您对这些问题的帮助。我希望他们不要太多。

最佳答案

停止使用 LayoutKind.Explicit 并删除 FieldOffset 属性,您的代码将正常工作。您的偏移量未正确对齐字段。

public struct TestStruct
{
    public double SomeDouble1;
    public double SomeDouble2;
    public int SomeInteger;
    [MarshalAs(UnmanagedType.I1)]
    public bool SomeBool1;
    [MarshalAs(UnmanagedType.I1)]
    public bool SomeBool2;
};

在 C# 中声明该函数,如下所示:

public static extern void TestGetBackStruct(TestStruct[] structs);

默认编码将与您的 C++ 声明匹配(您的代码实际上是 C++ 而不是 C),但您必须确保在调用函数之前在 C# 代码中分配 TestStruct[] 参数。通常,您还会将数组的长度作为参数传递,以便 C++ 代码知道有多少个结构。

请不要尝试从函数返回结构数组。使用 structs 参数作为输入/输出参数。

据我所知,没有一本书强调 P/Invoke。这似乎是一种黑魔法!

关于c# - 在结构中使用 boolean 值的一些 P/Invoke C# 到 C 编码问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6612030/

相关文章:

c# - 是否可以轮询任务是否完成?

c# - 如何从 SQL 查询生成 List<String>?

c++ - C++ 可以包含在 Ruby C 扩展中吗?

c - 为什么在 C 中写入二进制文件时 fwrite 会打印空字节?

c - 是否可以在同一个函数中使用两种不同类型的二进制文件进行写入?

c - -> 结构体中的运算符

c - 为什么这个程序中的指针指向不相关的字段?

c# - 如何在 ASP.NET 的代码隐藏中获取安全的 cookie 值

c++ - 从存储在 std::container 中的结构中提取元素

c# - 使用 WCF JSON Web 服务的客户端配置