I have a structure which I want to send to a TCP client throught TCP protocol so I want to assign or copy this struct data to byte array:
struct StartReadXML
{
public int CmdID;//3
public char[] CmdName;//ReadXML
public char[] Description;//Other data
};
here am assigning data to struct data members as below :
StartReadXML startXML=new StartReadXML();
startXML.CmdID = 3;
startXML.CmdName = "sreedhar".ToCharArray();
startXML.Description = "Kumar".ToCharArray();
Now, I want it to be assigned to a byte array. Which am doing using marshalling as below:
int sizestartXML = Marshal.SizeOf(startXML);//Get size of struct data
byte[] startXML_buf = new byte[sizestartXML];//byte array & its size
IntPtr ptr = Marshal.AllocHGlobal(sizestartXML);//pointer to byte array
Marshal.StructureToPtr(startXML, ptr, true);
Marshal.Copy(ptr, startXML_buf, 0, sizestartXML);
Marshal.FreeHGlobal(ptr);
//Sending struct data packet
stm.Write(startXML_buf, 0, startXML_buf.Length);//Modified
But, it fails at Structuretoptr conversion method. Please help in transferring the struct data as bytes for which am using above steps.
Thanks in advance Smile | :) !!
You cannot call StructureToPtr on arrays of variable size.
What this boils down to is that unless you know the size of CmdName and declare it - if it would be for example, 20 chars in size, like so:
public fixed char[] CmdName[20];
You will be greeted with an exception from the Marshal saying that your structure is either non-blittable or no meaningful size can be obtained.
This is a requirement the CLR imposes, and you can not work around.
An alternative method would be to use the Convert class or a serializer to convert the members of your struct manually, but unless you know the size of those arrays up front, you won't be able to use StructureToPtr - the same goes for the string type, as I'm assuming that's what your char array will contain.
Consider using a MemoryStream and writing values to the stream, and sending the contents of the stream using stream.ToArray() instead.
Considering that you can't simply convert to binary a struct, use for example:
class StartReadXML
{
public int CmdID;//3
public string CmdName;//ReadXML
public string Description;//Other data
}
Then:
var srx = new StartReadXML();
srx.CmdID = 3;
srx.CmdName = "sreedhar";
srx.Description = "Kumar";
// Example of how to Write to byte[] buffer
byte[] buffer;
using (var ms = new MemoryStream())
{
using (var bw = new BinaryWriter(ms, Encoding.UTF8))
{
bw.Write(srx.CmdID);
bw.Write(srx.CmdName);
bw.Write(srx.Description);
}
buffer = ms.ToArray();
}
// Example of how to Read from byte[] buffer
var srx2 = new StartReadXML();
using (var ms = new MemoryStream(buffer))
{
using (var br = new BinaryReader(ms, Encoding.UTF8))
{
srx2.CmdID = br.ReadInt32();
srx2.CmdName = br.ReadString();
srx2.Description = br.ReadString();
}
}
Note that here I'm working with a "variable length" packet: the length of CmdName and Description aren't fixed (and BinaryWriter/BinaryReader handle this by prepending the length of the string).
The opposite is when you have a packet of fixed length, with fixed-length strings. That requires a totally different handling.
Related
I am trying to use the marshalling thing correctly to serialize 4 bytes into a string.
When reading about SizeConst, it states
Indicates the number of elements in the fixed-length array or the number of characters (not bytes) in a string to import.
So I assumed that to read four bytes as string I require the following struct for marshalling
struct Data
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
public string string4;
}
However it appears for some reason that I need SizeConst to set to 5 to get the 4 bytes.
Here is some example
public void Test_Marshal()
{
var file = #"D:\foo.bin" ;
var chars = new char[] {'W', 'H', 'A','T', '?'};
File.WriteAllBytes(file, chars.ToList().Select( c => Convert.ToByte(c)).ToArray());
var fileAsStream = new FileStream(file, FileMode.Open);
int count = Marshal.SizeOf(typeof(Data));
byte[] readBuffer = new byte[count];
using (var reader = new BinaryReader(fileAsStream))
{
readBuffer = reader.ReadBytes(count);
}
GCHandle handle = GCHandle.Alloc(readBuffer, GCHandleType.Pinned);
var aStruct = (Data)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(Data));
handle.Free();
System.Diagnostics.Debug.Assert(aStruct.string4 == "WHAT");
}
What I do: I write "WHAT?" in bytes into a file.
I then read the file and marshal the data into a struct with a member set to contain a fixed array of 4.
But I only get WHA, which are three chars and I do not understand why. If it would be C/C++ I would assume I need some null-termination but I am unaware of this condition in C#
What is happening here?
What is the most efficient way to combine multiple variables of different Types into a single byte-array?
Take the following example data:
short a = 500;
byte b = 10;
byte[] c = new byte[4];
How could I combine these three variables into one byte array without wasting to much time and memory?
Think of it like this (Pseudocode):
var combinedArray = new byte[] { a, b, c };
I thought of different ways, including unsafe code, converting them to byte[] using BitConverter and using Linq's Concat.
I need an array in the end, not just an IEnumerable, because I need to send this data via udp.
Are there any methods I did not think of?
Use the BinaryWriter combined with a MemoryStream.
var buffer = new MemoryStream();
var writer = new BinaryWriter(buffer);
writer.Write(a);
writer.Write(b);
writer.Write(c);
writer.Close();
byte[] bytes = buffer.ToArray();
But do note that there is no padding or alignment. The array c will start at an odd offset.
You will also have to verify the Big Endian / Little Endian contract with your client.
I am writing my custom serializer for my class which should serialize a class object to a byte array. Below is my sample code:
[StructLayout(LayoutKind.Sequential)]
public class ATTRIBUTE_STR
{
public uint atType;
public uint atLen;
public byte[] atData = new byte[3];
public byte[] serialize()
{
byte[] temp = new byte[Marshal.SizeOf(this)];
byte[] buffer = BitConverter.GetBytes(atType);
Array.Copy(buffer, 0, temp, 0, buffer.Length);
buffer = null;
buffer = BitConverter.GetBytes(atLen);
Array.Copy(buffer, 0, temp, 4, buffer.Length);
Array.Copy(this.atData, 0, temp, 8, this.atData.Length);
return temp;
}
};
However, the byte array atData because of how its stored in memory with byte alignment and all, it's not getting correctly into the temp byte array. Since the byte arrays have even byte alignment.
How can I serialize this object accounting for the alignment of members in memory?
EDIT: I know there are other options like Marshalling or ProtocolBuffers, but I would like using my custom serializer.
Your sizeof call is probably not returning what you think it is, but there hardly seems much point worrying about Sizeof if you're going to stick with hard-coded offsets for the output.]
So you might as well do something like this:
byte[] temp = new byte[8+atLen.Length];
BitConverter.GetBytes(atType).CopyTo(temp,0);
BitConverter.GetBytes(atLen).CopyTo(temp,4);
this.AtData.CopyTo(temp,8);
return temp;
Update: Sizeof returns the amount of unmanaged space necessary to store your object - but bear in mind that your atData array contents are not stored within your object, they are in an array object which is referred to by your object. So Marshal.Sizeof is not going to give you an answer which includes the size of your array - indeed, if you ask Marshal.Sizeof to give you Sizeof(atData), you'll get an exception.
Okay the basic idea what I'm trying to do is, converting byte array to something like short or int etc. etc.
A simple example might be:
unsafe
{
fixed (byte* byteArray = new byte[5] { 255, 255, 255, 126, 34 })
{
short shortSingle = *(short*)byteArray;
MessageBox.Show((shortSingle).ToString()); // works fine output is -1
}
}
Okay, so what I'm really trying to do is, make an extension to Stream class; extended read and write methods. I need help at the following code:
unsafe public static T Read<T>(this Stream stream)
{
int bytesToRead = sizeof(T); // ERROR: Cannot take the address of, get the size of, or declare a pointer to a managed type ('T')
byte[] buffer = new byte[bytesToRead];
if (bytesToRead != stream.Read(buffer, 0, bytesToRead))
{
throw new Exception();
}
fixed (byte* byteArray = buffer)
{
T typeSingle = *(T*)byteArray; // ERROR: Cannot take the address of, get the size of, or declare a pointer to a managed type ('T')
return typeSingle;
}
}
unsafe public static T[] Read<T>(this Stream stream, int count)
{
// haven't figured out it yet. This is where I read and return T arrays
}
I feel like I have to use pointers for speed because I will be working on writting and reading data from streams like NetworkStream classes. Thanks for your help!
EDIT:
And while I try to figure out how may I return T arrays, I've faced with this problem:
unsafe
{
fixed (byte* byteArray = new byte[5] { 0, 0, 255, 255, 34 })
{
short* shortArray = (short*)byteArray;
MessageBox.Show((shortArray[0]).ToString()); // works fine output is 0
MessageBox.Show((shortArray[1]).ToString()); // works fine output is -1
short[] managedShortArray = new short[2];
managedShortArray = shortArray; // The problem is, How may I convert pointer to a managed short array? ERROR: Cannot implicitly convert type 'short*' to 'short[]'
}
}
THE SUMMARY:
I have to convert from byte array to given type of T
OR
to given type of T array with given length
You can't make this function generic because of pointer restrictions in C#. Any of the following types may be a pointer type:
sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool.
Any enum type.
Any pointer type.
Any user-defined struct type that contains fields of unmanaged types only.
But you can't set a restriction on T where T <can be pointer type>. where T : struct is very close, but not enough, because user-defined structs can contain fields of reference types.
There is a workaround - System.Runtime.InteropServices.Marshal.PtrToStructure() (it simply throws an exception if it is unable to work with specified object type), but it would also kill any achieved performance improvements.
I think the only way to do this is to create non-generic functions for all desired types.
Edit: unmanaged constraint added to C# 7.3.
Jumping in a bit late on this one, but with C# 7.3 comes the addition of the unmanaged type constraint.
With the unmanaged type constraint, you can use generic pointers (T*) among other things, provided the type passed in is unmanaged.
I tested the generic method you provided, and it does work now. Additionally, you can extend it to return an array like so:
public static unsafe T[] ReadAsArray<T>(this Stream stream) where T : unmanaged
{
var length = stream.Length;
var returnArray = new T[length];
for (var i = 0; i < length; i++)
{
int bytesToRead = sizeof(T); // no longer throws error
byte[] buffer = new byte[bytesToRead];
if (bytesToRead != stream.Read(buffer, 0, bytesToRead))
{
throw new Exception();
}
fixed (byte* byteArray = buffer)
{
T typeSingle = *(T*)byteArray; // no longer throws error
returnArray[i] = typeSingle;
}
}
return returnArray;
}
You can call it with the following code, which will print the contents of a file:
using (var sourceStream = File.Open(filename, FileMode.Open))
{
var byteArray = sourceStream.ReadAsArray<byte>();
Console.Write(new string(byteArray.Select(b => (char)b).ToArray()));
}
I have a TCP socket connection where I get byte array of data which I have to decrypt and then format, so I am looking for a simple way to read this resulted byte array that I can easyly walk thru the elements exporting it as I need to a type or variable etc, to explain it better see the below example:
Let's say we have the byte array (packets are little-endian, this is just hypotetical example):
01 02 00 03 74 00 74 00 69 00
I want to put the first 2 bytes into an int, the next 2 bytes into another int and rest as an string.
So it would look like this translated:
int first = 258;
int seconnd = 3;
string rest = "tti"
I am not a java person, but on java there was something interesting I noticed somewhere of people reading packets directly like this:
messageSize = readH(); // for 2 bytes
key = readH(); // for 2 bytes
message = readS(); // read as string
And I was wondering if there is similar pre-made feature in c# or any other resource that may help me better doing this task ?
You can use BinaryReader like this:
using (var reader = new BinaryReader(stream))
{
var first = reader.ReadUInt16();
var second = reader.ReadUInt16();
var stringBytes = reader.ReadBytes(6);
var str = Encoding.Unicode.GetString(stringBytes);
}
However, this will only work if little-endian is used.
The example you posted is big-endian.
I assume that you implement both writer and sender in C#, so you are good to go with BinaryReader and BinaryWriter, they both use little-endian, so they will understand each other.
[Edit]
Another approach you might want to consider is using struct.
For example in your case :
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MyStruct
{
public ushort First;
public ushort Second;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string MyString;
}
The code would look like this
var myStruct = new MyStruct { First = 1, Second = 2, MyString = "asd" };
var bytes = StructToBytes(myStruct);
var myStruct1 = BytesToStruct<MyStruct>(bytes);
And two utility methods:
public static T BytesToStruct<T>(byte[] bytes) where T : struct
{
AssertUtilities.ArgumentNotNull(bytes, "bytes");
var structSize = Marshal.SizeOf(typeof(T));
var pointer = IntPtr.Zero;
try
{
pointer = Marshal.AllocHGlobal(structSize);
Marshal.Copy(bytes, 0, pointer, structSize);
return (T)Marshal.PtrToStructure(pointer, typeof(T));
}
finally
{
if (pointer != IntPtr.Zero)
Marshal.FreeHGlobal(pointer);
}
}
public static byte[] StructToBytes<T>(T structObject) where T : struct
{
var structSize = Marshal.SizeOf(typeof(T));
var bytes = new byte[structSize];
var pointer = IntPtr.Zero;
try
{
pointer = Marshal.AllocHGlobal(structSize);
Marshal.StructureToPtr(structObject, pointer, true);
Marshal.Copy(pointer, bytes, 0, structSize);
return bytes;
}
finally
{
if (pointer != IntPtr.Zero)
Marshal.FreeHGlobal(pointer);
}
}
For simple examples, you could do something like:
static class StreamHelpers
{
public static int ReadH(this Stream stream)
{
int x = stream.ReadByte(), y = stream.ReadByte();
if (x < 0 || y < 0) throw new EndOfStreamException();
return (y << 8) | x;
}
...
}
which gives you access to:
int i = stream.ReadH();
etc. However, personally I would be writing a custom blahReader (for your blah), with an internal byte[] buffer and tracking cursor. This allows must more efficient reading in most cases (even compared to a BufferedStream).
As a side-note, your string looks to be UTF-16 and read to the EOF; reading to EOF is a pain as it doesn't really allow you to encode 2 strings. I would (instead) be adding (first) the length of the string, and I would be using UTF-8 unless there is a good chance of large character points. Then reading a string is a case of:
read the length
decode that much via Encoding.*
As a final point - if you are mostly writing small integers, there are alternative encoding styles for integers that might be useful (allowing single-byte for small values, but still allowing full int ranges to be expressed).