【问题标题】:What are the C# equivalent of these C++ structs这些 C++ 结构的 C# 等价物是什么
【发布时间】:2011-01-20 22:57:28
【问题描述】:
typedef union _Value {
    signed char    c; 
    unsigned char  b; 
    signed short   s; 
    unsigned short w; 
    signed long    l; 
    unsigned long  u; 
    float          f; 
    double        *d; 
    char          *p; 
} Value;


typedef struct _Field {
 WORD    nFieldId;
 BYTE    bValueType;
 Value Value;
} Field;


typedef struct _Packet {
 WORD    nMessageType;
 WORD    nSecurityType;
 BYTE    bExchangeId;
 BYTE    bMarketCenter;
 int     iFieldCount;
 char    cSymbol[20];
    Field FieldArr[1];

} Packet;

这些 C++ 结构的 C# 等价物是什么?

我正在将一些代码从 C++ 迁移到 C#,但在迁移这些结构时遇到了问题。我尝试了一些事情,但我总是遇到编组问题。

【问题讨论】:

标签: c# c++ struct marshalling


【解决方案1】:

我假设 'char' 被用作 8 位数字,如果是这样,那么这里是你的映射:

signed char    c; -> SByte    c; 
unsigned char  b; -> Byte     b;
signed short   s; -> Int16    s;
unsigned short w; -> UInt16   w;
signed long    l; -> Int32    l;
unsigned long  u; -> UInt32   u;
float          f; -> Single   f; (though 'float' still works)
double        *d; -> Double   d; (was this meant to be a pointer???)
char          *p; -> String   s; (assuming its a string here, in the marshaling you can tell it whether it is ASCII or wide char format)

有了这些信息,翻译这些结构应该相对容易(只需确保将它们保留为结构并为其赋予属性“[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential) ]",这将确保编组器以相同的顺序保存所有数据。

另外,我建议查看 System.Runtime.InteropServices 中的类和属性,因为它们确实提供了很多方法来自动将数据编组为 c/c++ 代码(并且它不需要“不安全”的 c# 代码要么)。

【讨论】:

  • signed longunsigned long 应该分别映射到 IntPtrUIntPtr。 C++ 中的double* 将映射到C# 中的IntPtrdouble*,后者显然只允许在不安全的代码中使用。 StructLayout 属性可以指定CharSet
  • System.Runtime.InteropServices.LayoutKind.Sequential 似乎不适合工会,它应该有 System.Runtime.InteropServices.LayoutKind.Explicit
【解决方案2】:

其他一些帖子已经有很好的信息,我想我会分享一个快速提示。我最近不得不经历这种问题。这可能很明显,但是如果您拥有界面两侧的代码,我发现注释掉除一个字段之外的所有字段并确保其有效,然后将它们一个一个缓慢地添加回来是一种更安全的方法来使其正常工作。

【讨论】:

    【解决方案3】:

    请参阅this MSDN article,了解使用 PInvoke 编组结构。

    关键是使用StructLayout 属性来确保PInvoke 正确处理该结构,并使用MarshalAs 属性来处理不完全对齐的类型。

    【讨论】:

    • 对不起,我没有提到;我已经在我的 C# 等效项上定义了 StructLayout,我不想使用 PInvoke。基本上数据是作为字节从套接字传入的,我想将它们编组到我的 c# 结构中。
    • 如果数据来自网络,我只需将 Packet 类型的固定部分声明为 C# 结构,然后通过网络流使用 BinaryReader 读取剩余数据。在 C# 中处理可变长度的结构是非常重要的。
    【解决方案4】:

    回到 .Net 2.0 天,我也有一个网络套接字并将字节流转换为有意义的结构。目前唯一的解决方案是使用 BitConverterBuffer 类手动完成。

    很遗憾,我再也无法在网上找到该示例。所以我剥离了我的旧课程(天哪,这看起来又旧又丑......)。也许由于精简,其中有一些小的拼写错误,但它应该让您对如何完成问题有一个很好的了解。

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace VehicleSpeedTracer
    {
        public class Datagram
        {
            //Offsets im ByteArray
            private const int SizeOffset = 0;
            private const int TimeOffset = SizeOffset + sizeof(uint);
            private const int SpeedOffset = TimeOffset + sizeof(double);
            private const int UnitOffset = SpeedOffset + sizeof(char);
            private const int UnitMaxSize = (int)MaxSize - UnitOffset;
    
            //Daten Current
            public const uint MaxSize = 128;
            public TimeSpan CurrentTime;
            public double CurrentSpeed;
            public string Unit;
    
            public uint Size
            {
                get { return MaxSize - (uint)UnitMaxSize + (uint)Unit.Length; }
            }
    
            public Datagram()
            {
            }
    
            public Datagram(Datagram Data)
            {
                CurrentTime = Data.CurrentTime;
                CurrentSpeed = Data.CurrentSpeed;
                Unit = Data.Unit;
            }
    
            public Datagram(byte[] RawData)
            {
                CurrentTime = TimeSpan.FromSeconds(GetDouble(RawData, TimeOffset));
                CurrentSpeed = GetDouble(RawData, SpeedOffset);
                Unit = GetString(RawData, UnitOffset, (int)(GetUInt(RawData, SizeOffset) - UnitOffset));
            }
    
            public override string ToString()
            {
                return this.CurrentTime.Hours.ToString().PadLeft(2, '0') + ":" +
                        this.CurrentTime.Minutes.ToString().PadLeft(2, '0') + ":" +
                        this.CurrentTime.Seconds.ToString().PadLeft(2, '0') + "." +
                        this.CurrentTime.Milliseconds.ToString().PadLeft(3, '0') + "  " +
                        this.Unit;
            }
    
            public static implicit operator byte[](Datagram Data)
            {
                byte[] RawData;
                RawData = new byte[Data.Size];
                SetUInt(RawData, SizeOffset, Data.Size);
                SetDouble(RawData, TimeOffset, Data.CurrentTime.TotalDays);
                SetDouble(RawData, SpeedOffset, Data.CurrentSpeed);
                SetString(RawData, UnitOffset, Data.Unit);
    
                return RawData;
            }
    
            #region Utility Functions
            // utility:  get a uint from the byte array
            private static uint GetUInt(byte[] aData, int Offset)
            {
                return BitConverter.ToUInt32(aData, Offset);
            }
    
            // utility:  set a uint into the byte array
            private static void SetUInt(byte[] aData, int Offset, uint Value)
            {
                byte[] buint = BitConverter.GetBytes(Value);
                Buffer.BlockCopy(buint, 0, aData, Offset, buint.Length);
            }
    
            // utility:  get a ushort from the byte array
            private static ushort GetUShort(byte[] aData, int Offset)
            {
                return BitConverter.ToUInt16(aData, Offset);
            }
    
            // utility:  set a ushort into the byte array
            private static void SetUShort(byte[] aData, int Offset, int Value)
            {
                byte[] bushort = BitConverter.GetBytes((short)Value);
                Buffer.BlockCopy(bushort, 0, aData, Offset, bushort.Length);
            }
    
            // utility:  get a double from the byte array
            private static double GetDouble(byte[] aData, int Offset)
            {
                return BitConverter.ToDouble(aData, Offset);
            }
    
            // utility:  set a double into the byte array
            private static void SetDouble(byte[] aData, int Offset, double Value)
            {
                byte[] bushort = BitConverter.GetBytes(Value);
                Buffer.BlockCopy(bushort, 0, aData, Offset, bushort.Length);
            }
    
            // utility:  get a unicode string from the byte array
            private static string GetString(byte[] aData, int Offset, int Length)
            {
                String sReturn = Encoding.ASCII.GetString(aData, Offset, Length);
                return sReturn;
            }
    
            // utility:  set a unicode string in the byte array
            private static void SetString(byte[] aData, int Offset, string Value)
            {
                byte[] arr = Encoding.ASCII.GetBytes(Value);
                Buffer.BlockCopy(arr, 0, aData, Offset, arr.Length);
            }
            #endregion
        }
    
        public delegate void DatagramEventHandler(object sender, DatagramEventArgs e);
    
        public class DatagramEventArgs : EventArgs
        {
            public Datagram Data;
    
            public DatagramEventArgs(Datagram Data)
            {
                this.Data = Data;
            }
        }
    }
    

    【讨论】:

    • -1 这种方法现在真的过时了。 C# 3.0(可能在早期版本中可用,我只从 3 开始)对大部分数据的自动翻译提供了出色的支持。我自己不需要做任何复杂的事情来让任何 c 函数在 c# 中工作,即使是包含可变长度字符串的结构也很容易做到。
    • 是的,对于您希望通过 DLLImport 访问的函数,这是正确的。但是如果你有一个 byte[] (例如来自套接字),就不会有这样的事情(据我所知)。
    猜你喜欢
    • 2011-05-31
    • 2014-05-08
    • 1970-01-01
    • 1970-01-01
    • 2021-06-15
    • 1970-01-01
    • 2011-01-19
    • 2010-12-07
    • 2010-12-01
    相关资源
    最近更新 更多