如何将结构从 C++ 迁移到 C#

Posted

技术标签:

【中文标题】如何将结构从 C++ 迁移到 C#【英文标题】:How to Migrate structs form C++ to C# 【发布时间】:2012-06-21 14:26:12 【问题描述】:

我正在编写一个接收复杂数据结构的 UDP 客户端。 我确实有 C++ 中的结构,但我的程序是 C#

Bitfiels 和联合有很多不同的结构。

有什么方法可以不用手动转换结构吗?

还有在 C# 中实现位域和联合的简单方法吗?

现在我正在为位域使用属性,但这是一项艰巨的工作,极有可能出错。

我提供了一个我现在正在做的简化示例,大约有 50 个结构,每个结构有 100 行代码。

C++ 示例:

typedef struct Message_s

    unsigned char var : 2;
    unsigned char var2 : 6;

    union
    
         struct
         
            unsigned char sVar;
            unsigned char sVar2;
         
         char arr[32];
         
Message_t;

示例 C#:

 [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
struct Message_s

    private byte Field1
    public byte var
    
       get
       
           return (byte)(Field1 & 0x03);
       
       set
       
           Field1 = (byte)((Field1 & ~0x03) | value);
       
    public byte var2
    
       get
       
           return (byte)(Field1 & 0xFC);
       
       set
       
           Field1 = (byte)((Field1 & 0x03) | value<<2);
       
     
//unions may be possible with properties...

【问题讨论】:

关于操作位域,您可能需要查看 System.Collections.Specialized.BitVector32。此外,根据您的操作,您可能希望查看使用适当宽度的枚举并使用 Flags 属性对其进行装饰。 好的,这样怎么样:我编写了一个包含所有结构的管理 C++ DLL,这将允许我从 C# 访问它们,不是吗?另外,另一个问题,C++ 编译器是否保持结构的字节和位顺序? 不幸的是,@ChristianElsner,我有同样的想法,这似乎不起作用。这类结构可以编译,但不能从 C# 访问它们的成员。您通过将struct 替换为value class 使其成为托管结构,但随后您会得到位域和联合的编译器错误。 是否可以使用 COM 访问您的 C++ 结构? 【参考方案1】:

简短的回答是否定的,似乎没有一种简单的方法可以将 C++ 结构转换为 C# 格式。但是,有一种方法可以在 C# 结构中进行联合;这是一个好主意,还是从头开始学习更好,这取决于您。

要在 C# 结构中获取联合,请使用 LayoutKind.ExplicitFieldOffset 属性:

[StructLayout(LayoutKind.Explicit)]
struct StructTest

    // A byte at the beginning of the struct.
    [FieldOffset(0)]
    public byte byte1;

    // Another byte immediately after it.
    [FieldOffset(1)]
    public byte byte2;

    // Now an integer which covers both.
    [FieldOffset(0)]
    public Int16 int1;

你可以按照你的期望使用它:

        var foo = new StructTest();
        foo.byte1 = 3;
        foo.byte2 = 6;
        Console.WriteLine("0", foo.int1);

        var bar = new StructTest();
        bar.int1 = 5050;
        Console.WriteLine("0, 1", bar.byte1, bar.byte2);

产生1539186, 19

这为您提供了联合结构,但它仍然不能解决 BitFields 的主要问题。对此似乎没有达成共识(当然,除了您已经完成的工作;例如,参见 [1])。使用MarshalAs 属性可能会做一些聪明的事情,将自定义 BitField 类映射到底层结构,但在这种努力下,继续编写自定义类可能会更容易。

【讨论】:

【参考方案2】:

作为替代方案,一种在代码中使用非托管 C++ 类型的方法,但它仍然涉及一些类型。在 VS C++ 项目中,创建非托管结构:

struct Date_s

    union
    
        struct
        
            unsigned nWeekDay  : 3;    // 0..7   (3 bits)
            unsigned nMonthDay : 6;    // 0..31  (6 bits)
            unsigned           : 0;    // Force alignment to next boundary.
            unsigned nMonth    : 5;    // 0..12  (5 bits)
            unsigned nYear     : 8;    // 0..100 (8 bits)
        ;
        unsigned bob;
    ;
;

现在我们创建一个封装这个结构的托管类型:

public ref struct Date_ms

    AutoPtr<Date_s> data;
    Date_ms()
    
        data = new Date_s();
    

    property unsigned nWeekDay
    
        unsigned get()  return data->nWeekDay; 
        void set(unsigned value)  data->nWeekDay = value; 
    
    property unsigned nMonthDay
    
        unsigned get()  return data->nMonthDay; 
        void set(unsigned value)  data->nMonthDay = value; 
    
    property unsigned nMonth
    
        unsigned get()  return data->nMonth; 
        void set(unsigned value)  data->nMonth = value; 
    
    property unsigned nYear
    
        unsigned get()  return data->nYear; 
        void set(unsigned value)  data->nYear = value; 
    
    property unsigned bob
    
        unsigned get()  return data->bob; 
        void set(unsigned value)  data->bob = value; 
    
;

那里有一些复制和粘贴操作。我在这里还使用了一个外部项目:来自 Kerr 的 AutoPtr。我还在 AutoPtr 代码中添加了 T* operator=(T* rhs) return (m_ptr = rhs); 行(正如 Kerr 帖子的 cmets 中所建议的那样)以使初始化程序工作。

您现在可以在 C# 代码中使用这个新结构,一切都应该按预期工作:

var test = new Date_ms();
test.nMonth = 3;
test.nMonthDay = 28;
test.nYear = 98;
Console.WriteLine("0, 1, 2", test.nMonth, test.nMonthDay, test.nYear);
Console.WriteLine("0", test.bob);

导致3, 28, 98224

大警告

像这样混合托管和非托管代码是危险的,这就是 Kerr 首先编写 AutoPtr 的原因(顺便说一句,他的 blog post 关于这个主题非常值得一读)。这对我有用,但我不能保证它不会导致内存泄漏。您应该明确在部署它之前检查它是否按预期工作。

【讨论】:

以上是关于如何将结构从 C++ 迁移到 C#的主要内容,如果未能解决你的问题,请参考以下文章

如何将结构中的char *从c#传递到c++

如何将带有 unsigned char* 的结构从 C# 传递到 C++?

如何将对象列表从 C++ 传递到 C#?

如何将 System::^array 从 C# 函数转换为 C++ 中的等效数据类型

将结构数组从 C#(.NET Core) 传递到 C++(未管理)

从 C# 迁移到 C++ [关闭]