2017-10-24 6 views
2

配列を含む構造体をC#のバイト配列に変換するにはどうすればよいですか?配列を含む構造体をバイト配列に変換する方法は?

配列なしの構造体に関する質問がありました。here

しかし、構造体は、このような配列が含まれている場合:

public struct DiObject 
{ 
    public byte Command; 
    public byte ErrorClass; 
    public byte Reserved; 
    public byte Flags; 
} 

public struct MyPacket 
{ 
    public uint ProtocolIdentifier; 
    public uint NumDi;  
    public DiObject[] Di; 
} 

をバイトで構造体を変換するときに、アクセス違反の例外を除いて結果:

private static byte[] GetBytes(MyPacket packet, int packetSize) 
{ 
    var data = new byte[packetSize]; 
    var ptr = Marshal.AllocHGlobal(packetSize); 

    // ==== Access violation exception occurs here ==== 
    Marshal.StructureToPtr(packet, ptr, true); 

    Marshal.Copy(ptr, data, 0, packetSize); 
    Marshal.FreeHGlobal(ptr); 
    return data; 
} 

私の目標は、メッセージを送信することですMSMQを使用するメッセージキュー内のバイト数。

ここでは、問題をコンパイルして再現する完全なコードです。

using System; 
//using System.IO; 
//using System.Messaging; 
using System.Runtime.InteropServices; 

namespace StructToBytes 
{ 
    // 4 bytes 
    [Serializable] 
    public struct DiObject 
    { 
     public byte Command; 
     public byte ErrorClass; 
     public byte Reserved; 
     public byte Flags; 
    } 

    // 8 + (numDi*4) bytes 
    [Serializable] 
    public struct MyPacket 
    { 
     public uint ProtocolIdentifier; 
     public uint NumDi;  
     public DiObject[] Di; 
    } 

    internal class Program 
    { 
     private static byte[] GetBytes(MyPacket packet, int packetSize) 
     { 
      var data = new byte[packetSize]; 
      var ptr = Marshal.AllocHGlobal(packetSize); 

      // ==== Access violation exception occurs here ==== 
      Marshal.StructureToPtr(packet, ptr, true); 

      Marshal.Copy(ptr, data, 0, packetSize); 
      Marshal.FreeHGlobal(ptr); 
      return data; 
     } 

     private static MyPacket FromBytes(byte[] data) 
     { 
      var packet = new MyPacket(); 
      var dataSize = Marshal.SizeOf(packet); 
      var ptr = Marshal.AllocHGlobal(dataSize); 
      Marshal.Copy(data, 0, ptr, dataSize); 
      packet = (MyPacket) Marshal.PtrToStructure(ptr, packet.GetType()); 
      Marshal.FreeHGlobal(ptr); 
      return packet; 
     } 

     private static void Main(string[] args) 
     { 
      const string queuePath = @".\private$\test_msmq"; 

      // Create the packet 
      var packet = new MyPacket(); 

      // 8 bytes 
      packet.ProtocolIdentifier = 1; 
      packet.NumDi = 2; 

      // 8 bytes 
      packet.Di = new DiObject[packet.NumDi]; 
      packet.Di[0].Command = 2; 
      packet.Di[0].ErrorClass = 3; 
      packet.Di[0].Flags = 4; 
      packet.Di[0].Reserved = 5; 
      packet.Di[1].Command = 6; 
      packet.Di[1].ErrorClass = 7; 
      packet.Di[1].Flags = 8; 
      packet.Di[1].Reserved = 9; 

      // Convert the struct in bytes 
      const int packetSize = 16; 
      var packetBytes = GetBytes(packet, packetSize); 

      // Create the message 
      /* 
      var msg = new Message(); 
      msg.BodyStream = new MemoryStream(packetBytes); 

      // Open or create the message queue 
      if (!MessageQueue.Exists(queuePath)) 
       MessageQueue.Create(queuePath); 

      // Open the queue 
      var q = new MessageQueue(queuePath); // {Formatter = new BinaryMessageFormatter()}; 

      // Send the message to the queue 
      q.Send(msg); 
      */ 
     } 
    } 
} 
+0

「BinaryMessageFormatter」はこれをシリアル化してみませんか?メッセージを小さくしようとしているのでしょうか? MSMQメッセージのオーバーヘッドは、すでにメッセージの内容自体よりも大きくなるため、このような小さなものでは、大きな違いはありません。 –

+0

BinaryMessageFormatterを使用すると、データは16バイトではなく340バイトとして保存されます。私は可能な限り小さなパケットを望みます。ここにこれを示すコード://メッセージキューを開くか作成する if(!MessageQueue.Exists(queuePath)) MessageQueue.Create(queuePath); //キューを開く var q =新しいMessageQueue(queuePath); // {Formatter = new BinaryMessageFormatter()}; varメッセージ=新しいメッセージ(パケット、新しいBinaryMessageFormatter()); q.Send(メッセージ); –

+0

要するに、私はデジタルとアナログの多くの点を保存したいので、最小限のパケットを可能にしたいと思います。この例は単純であるために小さいです。 –

答えて

2

問題は構造がC#の

// 8 + (numDi*4) bytes 
[Serializable] 
public struct MyPacket 
{ 
    public uint ProtocolIdentifier; 
    public uint NumDi; 
    public DiObject[] Di; 
} 

public DiObject[] DiメンバーのサイズはnumDi * 4が真ではないという仮定を表現する方法についての間違った仮定です。このフィールドの代わりに、構造体の配列へのポインタがあります。 Arrayは.NETのクラスであり、構造体宣言の場所には含まれていません。

この問題を解決するには、固定配列を使用できます。私はデザインの背後にある考え方は可変長配列を取得することであることを理解しており、それは次のコードリストに示されています。

このコードはexecutin中AccessViolationExceptionを上げない:以下

using System; 
using System.IO; 
using System.Messaging; 
using System.Runtime.InteropServices; 

namespace StructToBytes 
{ 
    // 4 bytes 
    [Serializable] 
    [StructLayout(LayoutKind.Explicit)] 
    public unsafe struct DiObject 
    { 
     [FieldOffset(0)] 
     public byte Command; 

     [FieldOffset(1)] 
     public byte ErrorClass; 

     [FieldOffset(2)] 
     public byte Reserved; 

     [FieldOffset(3)] 
     public byte Flags; 
    } 

    // 8 + (numDi*4) bytes 
    [Serializable] 
    public unsafe struct MyPacket 
    { 
     public uint ProtocolIdentifier; 
     public uint NumDi; 
     public fixed byte Di[2 * 4]; 
    } 

    internal unsafe class Program 
    { 
     private static byte[] GetBytes(MyPacket packet, int packetSize) 
     { 
      var data = new byte[packetSize]; 
      var ptr = Marshal.AllocHGlobal(packetSize); 

      // ==== Access violation exception occurs here ==== 
      Marshal.StructureToPtr(packet, ptr, true); 

      Marshal.Copy(ptr, data, 0, packetSize); 
      Marshal.FreeHGlobal(ptr); 
      return data; 
     } 

     private static MyPacket FromBytes(byte[] data) 
     { 
      var packet = new MyPacket(); 
      var dataSize = Marshal.SizeOf(packet); 
      var ptr = Marshal.AllocHGlobal(dataSize); 
      Marshal.Copy(data, 0, ptr, dataSize); 
      packet = (MyPacket)Marshal.PtrToStructure(ptr, packet.GetType()); 
      Marshal.FreeHGlobal(ptr); 
      return packet; 
     } 

     private static void Main(string[] args) 
     { 
      const string queuePath = @".\private$\test_msmq"; 

      // Create the packet 
      var packet = new MyPacket(); 

      // 8 bytes 
      packet.ProtocolIdentifier = 1; 
      packet.NumDi = 2; 

      // 8 bytes 
      // packet.Di = new DiObject[packet.NumDi]; 
      packet.Di[0] = 2; 
      packet.Di[1] = 3; 
      packet.Di[2] = 4; 
      packet.Di[3] = 5; 
      packet.Di[4] = 6; 
      packet.Di[5] = 7; 
      packet.Di[6] = 8; 
      packet.Di[7] = 9; 

      // Convert the struct in bytes 
      int packetSize = Marshal.SizeOf<MyPacket>(); 
      var packetBytes = GetBytes(packet, packetSize); 

      // Create the message 

      var msg = new Message(); 
      msg.BodyStream = new MemoryStream(packetBytes); 

      // Open or create the message queue 
      if (!MessageQueue.Exists(queuePath)) 
       MessageQueue.Create(queuePath); 

      // Open the queue 
      var q = new MessageQueue(queuePath); // {Formatter = new BinaryMessageFormatter()}; 

      // Send the message to the queue 
      q.Send(msg); 

     } 
    } 
} 

コードは、バイト配列および可変内部配列サイズのMyPacket構造体のためのバイト配列からの効率的な変換を提供します。実装では、安全でないポインタ演算を使用してキャストと境界チェックを回避します。

using System; 
using System.IO; 
using System.Messaging; 
using System.Runtime.InteropServices; 

namespace StructToBytes 
{ 
    // 4 bytes 
    [Serializable] 
    [StructLayout(LayoutKind.Explicit)] 
    public unsafe struct DiObject 
    { 
     [FieldOffset(0)] 
     public byte Command; 

     [FieldOffset(1)] 
     public byte ErrorClass; 

     [FieldOffset(2)] 
     public byte Reserved; 

     [FieldOffset(3)] 
     public byte Flags; 
    } 

    [Serializable] 
    public unsafe struct MyPacket 
    { 
     public uint ProtocolIdentifier; 
     public uint NumDi; 
     public DiObject[] Di; 

     public byte[] ToBytes() 
     { 
      byte[] buffer = new byte[NumDi]; 

      fixed(DiObject* pDi = Di) 
      fixed(byte* pBuff = buffer) 
      { 
       var pBuffDi = (DiObject*)pBuff; 
       var pDiPtr = pDi; 
       for (int i = 0; i < NumDi; i++) 
        *pBuffDi++ = *pDiPtr++; 
      } 
      return buffer; 
     } 

     public static MyPacket Create(byte[] buffer) 
     { 
      // argument checking code here 

      var packet = new MyPacket(); 
      packet.ProtocolIdentifier = buffer[0]; 
      packet.NumDi = buffer[1]; 
      packet.Di = new DiObject[packet.NumDi]; 

      fixed (byte* pBuf = buffer) 
      fixed (DiObject* pDi = packet.Di) 
      { 
       byte* pBufPtr = pBuf; 
       pBufPtr += 2; 
       var pBufDi = (DiObject*)pBufPtr; 
       var pDiPtr = pDi; 

       for (int i = 0; i < packet.NumDi; i++) 
        *pDiPtr++ = *pBufDi++; 
      } 

      return packet; 
     } 
    } 

    internal unsafe class Program 
    { 

     private static void Main(string[] args) 
     { 
      const string queuePath = @".\private$\test_msmq"; 

      // Create the packet 
      var packet = new MyPacket(); 

      // 8 bytes 
      packet.ProtocolIdentifier = 1; 
      packet.NumDi = 5; 

      // 8 bytes 
      packet.Di = new DiObject[packet.NumDi]; 
      packet.Di[0].Command = 2; 
      packet.Di[0].ErrorClass = 3; 
      packet.Di[0].Flags = 4; 
      packet.Di[0].Reserved = 5; 
      packet.Di[1].Command = 6; 
      packet.Di[1].ErrorClass = 7; 
      packet.Di[1].Flags = 8; 
      packet.Di[1].Reserved = 9; 
      packet.Di[2].Command = 6; 
      packet.Di[2].ErrorClass = 7; 
      packet.Di[2].Flags = 8; 
      packet.Di[2].Reserved = 9; 
      packet.Di[3].Command = 6; 
      packet.Di[3].ErrorClass = 7; 
      packet.Di[3].Flags = 8; 
      packet.Di[3].Reserved = 9; 

      // Create the message 

      var msg = new Message(); 
      msg.BodyStream = new MemoryStream(packet.ToBytes()); 

      // Open or create the message queue 
      if (!MessageQueue.Exists(queuePath)) 
       MessageQueue.Create(queuePath); 

      // Open the queue 
      var q = new MessageQueue(queuePath); 

      // Send the message to the queue 
      q.Send(msg); 

     } 
    } 
} 
+0

アレイについての明確な説明をありがとう。あなたのソリューションは完璧に動作し、C++プログラムとキューに接続する必要があることをよく理解しています。 –

関連する問題