views:

1501

answers:

4

C++ code:

struct tPacket
{
    WORD word1;
    WORD word2;
    BYTE byte1;
    BYTE byte2;
    BYTE array123[8];
}

static char data[8192] = {0};
...
some code to fill up the array
...
tPacket * packet = (tPacket *)data;

We can't do that as easy in C#.

Please note there is an array in the C++ structure.

Alternatively, using this source file could do the job for us, but not if there is an array in the structure.

+4  A: 

I'm unsure of exactly what you are asking. Are you trying to get an equivalent structure definition in C# for plain old C# usage or for interop (PInvoke) purposes? If it's for PInvoke the follownig structure will work

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct tPacket {

    /// WORD->unsigned short
    public ushort word1;

    /// WORD->unsigned short
    public ushort word2;

    /// BYTE->unsigned char
    public byte byte1;

    /// BYTE->unsigned char
    public byte byte2;

    /// BYTE[8]
    [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=8, ArraySubType=System.Runtime.InteropServices.UnmanagedType.I1)]
    public byte[] array123;
}

If you are looking for a plain old C# structure that has the same characteristics, it's unfortunately not possible to do with a struct. You cannot define an inline array of a contstant size in a C# structure nor can you force the array to be a specific size through an initializer.

There are two alternative options in the managed world.

Use a struct which has a create method that fills out the array

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct tPacket {
    public ushort word1;
    public ushort word2;
    public byte byte1;
    public byte byte2;
    public byte[] array123;
    public static tPacket Create() { 
      return new tPacket() { array123 = new byte[8] };
    }
}

Or alternatively use a class where you can initialize the array123 member variable directly.

EDIT OP watns to know how to convert a byte[] into a tPacket value

Unfortunately there is no great way to do this in C#. C++ was awesome for this kind of task because has a very weak type system in that you could choose to view a stream of bytes as a particular structure (evil pointer casting).

This may be possible in C# unsafe code but I do not believe it is.

Essentially what you will have to do is manually parse out the bytes and assign them to the various values in the struct. Or write a native method which does the C style casting and PInvoke into that function.

JaredPar
Hmm,alright,but how to add the array into the structure?
John
@John, both samples include the array in the structure. I'm not quite sure what you're asking
JaredPar
@JaredPar, the C++ code is used along with receiving a packet.When the packet is received ,the variable packet stores the data in the structure tPacket and later accesses them.The real members are Size,Opcode,SeedCRC,SeedSecurity,EncryptedData and the fatal one - blowfish,which is the array.
John
I'm asking how to do it in C#?Put the data in the structure,like it's done in C++.Please check the code above.
John
@John, I updated my answer
JaredPar
+1  A: 

I think what you are looking for (if you are using a similar structure definition like JaredPar posted) is something like this:

tPacket t = new tPacket();
byte[] buffer = new byte[Marshal.SizeOf(typeof(tPacket))];
socket.Receive(buffer, 0, buffer.length, 0);

GCHandle pin = GCHandle.Alloc(buffer, GCHandleType.Pinned);
t = (tPacket)Marshal.PtrToStructure(pin.AddrOfPinnedObject(), typeof(tPacket));
pin.free();

//do stuff with your new tPacket t
scottm
Additionally, you don't have to put this code in an unsafe block.
scottm
scotty2012,Thanks.I have some problems in the bytes order,but i'll try to figure it out by myself.If not then ill post another question.Thank you Both!
John
Sometimes you have to mess with the Pack setting in the struct layout attributes. I think default in c# is 8 bytes, but c is 4, or something to that effect. I can't remember off the top of my head.
scottm
A: 

It can be done with unsafe code too, although it restricts the context under which your program can run, and, naturally, introduces the possibility of security flaws. The advantage is that you cast directly from an array to the structure using pointers and it's also maintenance-free if you are only going to add or remove fields from the struct. However, accessing the arrays require using the fixed-statement as the GC can still move the struct around in memory when it's contained in an object.

Here's some modified code of an unsafe struct I used for interpreting UDP packets:

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
public unsafe struct UnsafePacket
{
 int time;
 short id0;
 fixed float acc[3];
 short id1;
 fixed float mat[9];

 public UnsafePacket(byte[] rawData)
 {
  if (rawData == null)
   throw new ArgumentNullException("rawData");
  if (sizeof(byte) * rawData.Length != sizeof(UnsafePacket))
   throw new ArgumentException("rawData");

  fixed (byte* ptr = &rawData[0])
  {
   this = *(UnsafePacket*)rawPtr;
  }
 }

 public float GetAcc(int index)
 {
  if (index < 0 || index >= 3)
   throw new ArgumentOutOfRangeException("index");
  fixed (UnsafePacket* ptr = &acc)
  {
   return ptr[index];
  }
 }

 public float GetMat(int index)
 {
  if (index < 0 || index >= 9)
   throw new ArgumentOutOfRangeException("index");
  fixed (UnsafePacket* ptr = &mat)
  {
   return ptr[index];
  }
 }

            // etc. for other properties
}

For this kind of code it is extremely important to check that the length of the array perfectly matches the size of the struct, otherwise you'll open for some nasty buffer overflows. As the unsafe keyword has been applied to the whole struct, you don't need to mark each method or codeblock as separate unsafe statements.

Cecil Has a Name
A: 

Presumably, you've already got some existing C++ code that works; otherwise, you would just be doing things the "C# way" and not care about mimimicing something from C++.

That being the case, why not just continue to use your C++ code--caling into it from C#? There are three different ways to use existing C++ code from C#: a custom .NET wrapper written in C++/CLI, P/Invoke, and COM Interop; these are all extensive topics, which you can read about elsewhere.

Dan