The Premise
I am continually polling custom modbus frames from an embedded device with my c# wpf application at a low interval. At the moment the bytes in the frame i receive are located at the same position every time(e.g. frame[15]+frame[16] = 0x0532 = errorcode2).
The Questions
Are there smart procedures (other than simply hardcoding the bytestorage) I could use to go from frame receiving to splitting/parsing?
What data structures would be preferrable to enable frames to be processed dynamically further down the line?
Method 1:
You can define an explicitly laid out struct in C# and marshal the data to it like is done in the code below. However, you will have problems with the data due to endian ordering if you are not running on big endian hardware (Modbus data is sent in big endian). This also won't handle variable length Modbus packets (i.e. Read Input Registers). NOTE: It is assumed that the frame has already been CRC checked so there is no need to declare and read it.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct MyPacket
{
public Byte Address; // Byte 0
public Byte FunctionCode; // Byte 1
public Byte ByteField; // Byte 2
public UInt32 UInt32Field; // Bytes 3-6
public UInt64 UInt64Field; // Byte 7-14
public UInt16 ErrorCode; // Bytes 15-16
}
public static bool TryParse<T>(byte[] array, out T packet) where T : struct
{
try
{
var size = Marshal.SizeOf(typeof(T));
var p = Marshal.AllocHGlobal(size);
Marshal.Copy(array, 0, p, size);
packet = (T)Marshal.PtrToStructure(p, typeof(T));
Marshal.FreeHGlobal(p);
return true;
}
catch
{
packet = default(T);
return false;
}
}
static int Main(string[] args)
{
// Create a sample frame
byte[] bytes = new byte[] { 0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 };
MyPacket packet;
if (TryParse<MyPacket>(bytes, out packet))
{
// Process packet here...
}
return 0;
}
Method 2:
What I think is a better solution is to define a class for each function type that you use and read each field individually as shown in the code below. NOTE: It is assumed that the frame has already been CRC checked so there is no need to declare and read it.
// VERY SIMPLE BigEndianReader class
public class BigEndianReader
{
private Stream _stream;
public BigEndianReader(Stream stream)
{
if (stream == null) throw new ArgumentNullException(nameof(stream));
if (!stream.CanRead) throw new ArgumentException(nameof(stream));
_stream = stream;
}
public byte ReadByte()
{
int b = _stream.ReadByte();
if (b < 0) throw new EndOfStreamException();
return (byte)b;
}
public UInt16 ReadUInt16()
{
UInt16 v = 0;
for (int i = 0; i < sizeof(UInt16); i++){v <<= 8;v |= ReadByte();}
return v;
}
public UInt32 ReadUInt32()
{
UInt32 v = 0;
for (int i = 0; i < sizeof(UInt32); i++){v <<= 8;v |= ReadByte();}
return v;
}
public UInt64 ReadUInt64()
{
UInt64 v = 0;
for (int i = 0; i < sizeof(UInt64); i++) { v <<= 8; v |= ReadByte(); }
return v;
}
}
class MyPacket
{
public Byte Address { get; private set; }
public Byte FunctionCode { get; private set; }
public Byte ByteField { get; private set; }
public UInt32 UInt32Field { get; private set; }
public UInt64 UInt64Field { get; private set; }
public UInt16 ErrorCode { get; private set; }
public static bool TryParse(byte[] bytes, out MyPacket result)
{
result = null;
try
{
BigEndianReader r = new BigEndianReader(new MemoryStream(bytes));
MyPacket p = new MyPacket();
p.Address = r.ReadByte();
p.FunctionCode = r.ReadByte();
p.ByteField = r.ReadByte();
p.UInt32Field = r.ReadUInt32();
p.UInt64Field = r.ReadUInt64();
p.ErrorCode = r.ReadUInt16();
result = p;
return true;
}
catch
{
return false;
}
}
}
static int Main(string[] args)
{
// Create a sample frame
byte[] bytes = new byte[] { 0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19 };
MyPacket packet;
if (MyPacket.TryParse(bytes, out packet))
{
// Process packet here...
}
//==================================================================
Console.Write("(press any key to exit)");
Console.ReadKey(true);
return 0;
}
Related
I want to store a known AES key (retrieved offline) by entering it into the application once, saving it to Cng for storage, then reference it only by name on subsequent use.
I want to save the key in the Key Storage Provider so my application won't load it into memory.
I can create (generate) a AES key (that I can retreive and create an instance of AesCng with) like this:
CngProvider keyStorageProvider = CngProvider.MicrosoftSoftwareKeyStorageProvider;
CngKeyCreationParameters keyCreationParameters = new CngKeyCreationParameters()
{
ExportPolicy = CngExportPolicies.AllowPlaintextExport,
KeyCreationOptions = CngKeyCreationOptions.OverwriteExistingKey
};
var name = "mykey";
var algo = new CngAlgorithm("AES");
var created = CngKey.Create(algo, name, keyCreationParameters);
But how can I add my already known AES symmetric key and just reference it by name the next time I run my application to run encryption/decryption using Cng?
Using CngKey.Import won't let me specify a name and I think I've tried all overloads but all yield some kind of error.
UPDATE:
This is a complete working example when creating a key.
// Calling code
byte[] key = //<from external input>;
byte[] data = new byte[] { 0xA, 0xB, 0xC, 0xD };
crypto.StoreKey("appkey", key);
var encryptedData = crypto.EncryptWithStoredKey("appkey", data);
// Implementation
public void StoreKey(string name, byte[] key)
{
CngKeyCreationParameters keyCreationParameters = new CngKeyCreationParameters()
{
KeyCreationOptions = CngKeyCreationOptions.OverwriteExistingKey
};
var algo = new CngAlgorithm("AES");
// Question: How can I import the byte[] key with name "appkey" instead of generating a new key here?
CngKey.Create(algo, name, keyCreationParameters);
}
public byte[] EncryptWithStoredKey(string name, byte[] data)
{
using (var cng = new AesCng(name))
using (var encryptor = cng.CreateEncryptor())
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
return memoryStream.ToArray();
}
}
}
It's... not nice. But it's technically doable. Reusing some of the import definitions from X509Certificate2.Import with NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG:
private static unsafe CngKey ImportPersistedAesKey(string keyName, byte[] key)
{
switch (key.Length * 8)
{
case 128:
case 192:
case 256:
break;
default:
throw new ArgumentOutOfRangeException(nameof(key));
}
byte[] blob = new byte[s_cipherKeyBlobPrefix.Length + key.Length];
Buffer.BlockCopy(s_cipherKeyBlobPrefix, 0, blob, 0, s_cipherKeyBlobPrefix.Length);
blob[12] = (byte)(12 /* sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) */ + key.Length);
blob[32] = (byte)key.Length;
Buffer.BlockCopy(key, 0, blob, s_cipherKeyBlobPrefix.Length, key.Length);
fixed (char* keyNamePtr = keyName)
fixed (byte* blobPtr = blob)
{
NativeMethods.NCrypt.NCryptBuffer nameBuf;
nameBuf.BufferType = NativeMethods.NCrypt.BufferType.PkcsName;
nameBuf.cbBuffer = (keyName.Length + 1) * 2;
nameBuf.pvBuffer = (IntPtr)keyNamePtr;
NativeMethods.NCrypt.NCryptBufferDesc bufferDesc;
bufferDesc.ulVersion = 0;
bufferDesc.cBuffers = 1;
bufferDesc.pBuffers = (IntPtr)(&nameBuf);
int ret = NativeMethods.NCrypt.NCryptOpenStorageProvider(
out SafeNCryptProviderHandle hProv,
CngProvider.MicrosoftSoftwareKeyStorageProvider.Provider,
0);
using (hProv)
{
if (ret != 0)
{
throw new Win32Exception(ret);
}
ret = NativeMethods.NCrypt.NCryptImportKey(
hProv,
IntPtr.Zero,
"CipherKeyBlob",
ref bufferDesc,
out SafeNCryptKeyHandle hKey,
(IntPtr)blobPtr,
blob.Length,
NativeMethods.NCrypt.NCryptImportFlags.NCRYPT_OVERWRITE_KEY_FLAG);
using (hKey)
{
if (ret != 0)
{
throw new Win32Exception(ret);
}
return CngKey.Open(hKey, CngKeyHandleOpenOptions.None);
}
}
}
}
private static byte[] s_cipherKeyBlobPrefix = {
// NCRYPT_KEY_BLOB_HEADER.cbSize (16)
0x10, 0x00, 0x00, 0x00,
// NCRYPT_KEY_BLOB_HEADER.dwMagic (NCRYPT_CIPHER_KEY_BLOB_MAGIC (0x52485043))
0x43, 0x50, 0x48, 0x52,
// NCRYPT_KEY_BLOB_HEADER.cbAlgName (8)
0x08, 0x00, 0x00, 0x00,
// NCRYPT_KEY_BLOB_HEADER.cbKeyData (to be determined)
0x00, 0x00, 0x00, 0x00,
// UTF16-LE "AES\0"
0x41, 0x00, 0x45, 0x00, 0x53, 0x00, 0x00, 0x00,
// BCRYPT_KEY_DATA_BLOB_HEADER.dwMagic (BCRYPT_KEY_DATA_BLOB_MAGIC (0x4D42444B))
0x4B, 0x44, 0x42, 0x4D,
// BCRYPT_KEY_DATA_BLOB_HEADER.dwVersion (1)
0x01, 0x00, 0x00, 0x00,
// BCRYPT_KEY_DATA_BLOB_HEADER.cbKeyData (to be determined)
0x00, 0x00, 0x00, 0x00
};
internal static class NativeMethods
{
internal static class NCrypt
{
[StructLayout(LayoutKind.Sequential)]
internal struct NCryptBufferDesc
{
public int ulVersion;
public int cBuffers;
public IntPtr pBuffers;
}
[StructLayout(LayoutKind.Sequential)]
internal struct NCryptBuffer
{
public int cbBuffer;
public BufferType BufferType;
public IntPtr pvBuffer;
}
internal enum BufferType
{
PkcsName = 45,
}
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern int NCryptOpenStorageProvider(
out SafeNCryptProviderHandle phProvider,
string pszProviderName,
int dwFlags);
internal enum NCryptImportFlags
{
None = 0,
NCRYPT_MACHINE_KEY_FLAG = 0x00000020,
NCRYPT_OVERWRITE_KEY_FLAG = 0x00000080,
NCRYPT_DO_NOT_FINALIZE_FLAG = 0x00000400,
}
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern int NCryptImportKey(
SafeNCryptProviderHandle hProvider,
IntPtr hImportKey,
string pszBlobType,
ref NCryptBufferDesc pParameterList,
out SafeNCryptKeyHandle phKey,
IntPtr pbData,
int cbData,
NCryptImportFlags dwFlags);
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern int NCryptFinalizeKey(SafeNCryptKeyHandle hKey, int dwFlags);
}
}
The expected flow is that you should be able to create a key and then use NCryptSetKeyProperty to set the NCRYPT_CIPHER_KEY_BLOB property for the NCRYPT_CIPHER_KEY_BLOB payload (from the .NET perspective that'd be you call CngKey.Create with creation parameters and specify it as a creation property). But it looks like CNG didn't wire that up for persisted symmetric keys, so you have to use the complicated form of NCryptImportKey directly.
I have a problem that just baffles me. I import a .wav file and read them as bytes. Then I turn them into integers that I then all divide by 2 (or some other number) in order to decrease the volume. Then I make a new .wav file into which I put the new data. The result is loud and heavy distortion over the original track.
Scroll to the Main() method for the relevant (C#-)code:
using System;
using System.IO;
namespace ConsoleApp2 {
class basic {
public static byte[] bit32(int num) { //turns int into byte array of length 4
byte[] numbyt = new byte[4] { 0x00, 0x00, 0x00, 0x00 };
int pow;
for (int k = 3; k >= 0; k--) {
pow = (int)Math.Pow(16, 2*k + 1);
numbyt[k] += (byte)(16*(num/pow));
num -= numbyt[k]*(pow/16);
numbyt[k] += (byte)(num/(pow/16));
num -= (num/(pow/16))*pow/16;
}
return numbyt;
}
public static byte[] bit16(int num) { //turns int into byte array of length 2
if (num < 0) {
num += 65535;
}
byte[] numbyt = new byte[2] { 0x00, 0x00 };
int pow;
for (int k = 1; k >= 0; k--) {
pow = (int)Math.Pow(16, 2*k + 1);
numbyt[k] += (byte)(16*(num/pow));
num -= numbyt[k]*(pow/16);
numbyt[k] += (byte)(num/(pow/16));
num -= (num/(pow/16))*pow/16;
}
return numbyt;
}
public static int bitint16(byte[] numbyt) { //turns byte array of length 2 into int
int num = 0;
num += (int)Math.Pow(16, 2)*numbyt[1];
num += numbyt[0];
return num;
}
}
class wavfile: FileStream {
public wavfile(string name, int len) : base(name, FileMode.Create) {
int samplerate = 44100;
byte[] riff = new byte[] { 0x52, 0x49, 0x46, 0x46 };
this.Write(riff, 0, 4);
byte[] chunksize;
chunksize = basic.bit32(36 + len*4);
this.Write(chunksize, 0, 4);
byte[] wavebyte = new byte[4] { 0x57, 0x41, 0x56, 0x45 };
this.Write(wavebyte, 0, 4);
byte[] fmt = new byte[] { 0x66, 0x6d, 0x74, 0x20 };
this.Write(fmt, 0, 4);
byte[] subchunk1size = new byte[] { 0x10, 0x00, 0x00, 0x00 };
this.Write(subchunk1size, 0, 4);
byte[] formchann = new byte[] { 0x01, 0x00, 0x02, 0x00 };
this.Write(formchann, 0, 4);
byte[] sampleratebyte = basic.bit32(samplerate);
this.Write(sampleratebyte, 0, 4);
byte[] byterate = basic.bit32(samplerate*4);
this.Write(byterate, 0, 4);
byte[] blockalign = new byte[] { 0x04, 0x00 };
this.Write(blockalign, 0, 2);
byte[] bits = new byte[] { 0x10, 0x00 };
this.Write(bits, 0, 2);
byte[] data = new byte[] { 0x64, 0x61, 0x74, 0x61 };
this.Write(data, 0, 4);
byte[] samplesbyte = basic.bit32(len*4);
this.Write(samplesbyte, 0, 4);
}
public void sound(int[] w, int len, wavfile wavorigin = null) {
byte[] wavbyt = new byte[len*4];
for (int t = 0; t < len*2; t++) {
byte[] wavbit16 = basic.bit16(w[t]);
wavbyt[2*t] = wavbit16[0];
wavbyt[2*t + 1] = wavbit16[1];
}
this.Write(wavbyt, 0, len*4);
System.Media.SoundPlayer player = new System.Media.SoundPlayer();
player.SoundLocation = this.Name;
while (true) {
player.Play();
Console.WriteLine("repeat?");
if (Console.ReadLine() == "no") {
break;
}
}
}
}
class Program {
static void Main() {
int[] song = new int[45000*2];
byte[] songbyt = File.ReadAllBytes("name.wav"); //use your stereo, 16bits per sample wav-file
for (int t = 0; t < 45000*2; t++) {
byte[] songbytsamp = new byte[2] { songbyt[44 + 2*t], songbyt[44 + 2*t + 1] }; //I skip the header
song[t] = basic.bitint16(songbytsamp)/2; //I divide by 2 here, remove the "/2" to hear the normal sound again
//song[t] *= 2;
}
wavfile wav = new wavfile("test.wav", 45000); //constructor class that writes the header of a .wav file
wav.sound(song, 45000); //method that writes the data from "song" into the .wav file
}
}
}
The problem is not the rounding down that happens when you divide an odd number by 2; you can uncomment the line that says song[t] *= 2; and hear for yourself that all of the distortion has completely disappeared again.
I must be making a small stupid mistake somewhere, but I cannot find it. I just want to make the sound data quieter to avoid distortion when I add more sounds to it.
Well, I knew it would be something stupid, and I was right. I forgot to account for the fact that negative numbers are written in signed 16 bit language as the numbers above 2^15, and when you divide by 2, you push them into (very large) positive values. I altered my code to substract 2^16 from any number that's above 2^15 before dividing by 2. I have to thank this person though: How to reduce volume of wav stream?
If this means that my question was a duplicate, then go ahead and delete it, but I'm letting it stay for now, because someone else might find it helpful.
Using Math.Pow to do bit and byte operations is a really bad idea. That function takes double values as inputs and returns a double. It also does exponentiation (not a trivial operation). Using traditional bit shift and mask operations is clearer, much faster and less likely to introduce noise (because of the inaccuracy of doubles).
As you noticed, you really want to work with unsigned quantities (like uint/UInt32 and ushort/UInt16). Sign extension trips up everyone when doing this sort of work.
This is not a full answer to your question, but it does present a way to do the byte operations that is arguably better.
First, create a small struct to hold a combination of a bit-mask and a shift quantity:
public struct MaskAndShift {
public uint Mask {get; set;}
public int Shift {get; set;}
}
Then I create two arrays of these structs for describing what should be done to extract individual bytes from a uint or a ushort. I put them both in a static class named Worker:
public static class Worker {
public static MaskAndShift[] Mask32 = new MaskAndShift[] {
new MaskAndShift {Mask = 0xFF000000, Shift = 24},
new MaskAndShift {Mask = 0x00FF0000, Shift = 16},
new MaskAndShift {Mask = 0x0000FF00, Shift = 8},
new MaskAndShift {Mask = 0x000000FF, Shift = 0},
};
public static MaskAndShift[] Mask16 = new MaskAndShift[] {
new MaskAndShift {Mask = 0x0000FF00, Shift = 8},
new MaskAndShift {Mask = 0x000000FF, Shift = 0},
};
}
Looking at the first entry in the first array, it says "to extract the first byte from a uint, mask that uint with 0xFF000000 and shift the result 24 bits to the right". If you have endian-ness issues, you can simply re-order the entries in the array.
Then I created this static function (in the Worker class) to convert a uint / UInt32 to an array of four bytes:
public static byte[] UintToByteArray (uint input) {
var bytes = new byte[4];
int i = 0;
foreach (var maskPair in Mask32) {
var masked = input & maskPair.Mask;
if (maskPair.Shift != 0) {
masked >>= maskPair.Shift;
}
bytes[i++] = (byte) masked;
}
return bytes;
}
The code to do the same operation for a 16 bit ushort (aka UInt16) looks nearly the same (there's probably an opportunity for some refactoring here):
public static byte[] UShortToByteArray (ushort input) {
var bytes = new byte[2];
int i = 0;
foreach (var maskPair in Mask16) {
var masked = input & maskPair.Mask;
if (maskPair.Shift != 0) {
masked >>= maskPair.Shift;
}
bytes[i++] = (byte) masked;
}
return bytes;
}
The reverse operation is much simpler (however, if you have endian-ness issues, you'll need to write the code). Here I just take the entries of the array, add them into a value and shift the result:
public static uint ByteArrayToUint (byte[] bytes) {
uint result = 0;
//note that the first time through, result is zero, so shifting is a noop
foreach (var b in bytes){
result <<= 8;
result += b;
}
return result;
}
Doing this for the 16 bit version ends up being effectively the same code, so...
public static ushort ByteArrayToUshort (byte[] bytes) {
return (ushort) ByteArrayToUint(bytes);
}
Bit-twiddling never works the first time. So I wrote some test code:
public static void Main(){
//pick a nice obvious pattern
uint bit32Test = (((0xF1u * 0x100u) + 0xE2u) * 0x100u + 0xD3u) * 0x100u + 0xC4u;
Console.WriteLine("Start");
Console.WriteLine("Input 32 Value: " + bit32Test.ToString("X"));
var bytes32 = Worker.UintToByteArray(bit32Test);
foreach (var b in bytes32){
Console.WriteLine(b.ToString("X"));
}
Console.WriteLine();
ushort bit16Test = (ushort)((0xB5u * 0x100u) + 0xA6u);
Console.WriteLine("Input 16 Value: " + bit16Test.ToString("X"));
var bytes16 = Worker.UShortToByteArray(bit16Test);
foreach (var b in bytes16){
Console.WriteLine(b.ToString("X"));
}
Console.WriteLine("\r\nNow the reverse");
uint reconstitued32 = Worker.ByteArrayToUint(bytes32);
Console.WriteLine("Reconstituted 32: " + reconstitued32.ToString("X"));
ushort reconstitued16 = Worker.ByteArrayToUshort(bytes16);
Console.WriteLine("Reconstituted 16: " + reconstitued16.ToString("X"));
}
The output from that test code looks like:
Start
Input 32 Value: F1E2D3C4
F1
E2
D3
C4
Input 16 Value: B5A6
B5
A6
Now the reverse
Reconstituted 32: F1E2D3C4
Reconstituted 16: B5A6
Also note that I do everything in hexadecimal - it makes everything so much easier to read and to understand.
I have little bit problem with USB communication. I can connect to USB device but I cannot write any data to USB device.
This is my code to write data to USB device:
public byte[] GetUID = { 0xFF, 0xCA, 0x00, 0x00, 0x00 };
public byte[] SCardConnect = { 0x68, 0x92, 0x01, 0x00, 0x03, 0x01, 0x00, 0x00 };
public bool WriteBulk(byte[] str)
{
//ErrorCode ec = ErrorCode.None;
if (IsConnected == false)
return false;
else Console.WriteLine("connected");
/*endpointWriter = MyUsbDevice.OpenEndpointWriter(WriteEndpointID.Ep02, EndpointType.Bulk);
int bytesWritten = length;
ec = endpointWriter.Write(protocol, 5000, out bytesWritten);
if (ec != ErrorCode.None) return false;
else return true;*/
UsbSetupPacket packet = new UsbSetupPacket((byte)UsbRequestType.TypeVendor, 0x04, 0, 0, (short)str.Length);
int lengthTransfered;
return MyUsbDevice.ControlTransfer(ref packet, str, str.Length, out lengthTransfered);
}
public bool checkUID()
{
return WriteBulk(GetUID);
}
public bool checkSCardConnect()
{
return WriteBulk(SCardConnect);
}
When I use checkUID() or checkSCardConnect(), it always returns false. Is there any other procedure to write using USB Communication?
How do I convert a structure that contains an array to a byte array in C#?
There was a question here about a struct without array.
But if the struct contains an array like this:
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;
}
It results with an access violation exception when converting the struct in a byte:
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;
}
My goal is to send a message in bytes in a message queue with MSMQ.
Here the complete code that compiles and reproduce the problem.
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);
*/
}
}
}
The problem lies with wrong assumption about how structure is represented in C#
// 8 + (numDi*4) bytes
[Serializable]
public struct MyPacket
{
public uint ProtocolIdentifier;
public uint NumDi;
public DiObject[] Di;
}
The assumption that size of public DiObject[] Di member is numDi * 4 is not true. In place of this field there is a pointer to the array of structures. Array is a class in .NET and is not included in place in structure declaration.
To solve this problem one can use fixed arrays. I understand that the idea behind the design is to get variable length array and it is presented in next code listing.
This code does not raise AccessViolationException during executin:
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);
}
}
}
Code below provides efficient conversion to byte array and from byte array for MyPacket struct with variable internal array size. Implementation avoids casts and bounds checks by using unsafe pointer arithmetic.
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);
}
}
}
C# Question:
I need to be able to create array of sets of bytes with a variable assigned to each set of bytes return that set of bytes to go through a for each loop function until the list is finished for example:
public static bytes[] OBJECTS()
{
return new bytes[3]
{
public static byte[] object1 = new byte[] { 0xB7, 0x79, 0xA0, 0x91 };
public static byte[] object2 = new byte[] { 0x4C, 0x80, 0xEB, 0x0E };
public static byte[] object3 = new byte[] { 0x5D, 0x0A, 0xAC, 0x8F };
};
}
EXAMPLE
I need to be able to return every value in the array to the for loop to perform functions with every set of bytes. Sorry for the confusion.
for (int i = 0; i < 3; ++i)
{
1st Loop Return Object 1
2st Loop Return Object 2
3rd Loop Return Object 3
}
This code most closely resembles the code in your question, but this is legal C#. How close is this to what you want?
void Main()
{
for (int i = 0; i < 3; ++i)
{
byte[] selected = OBJECTS()[i];
/* do something with `selected` */
}
}
public static byte[][] OBJECTS()
{
return new byte[][]
{
new byte[] { 0xB7, 0x79, 0xA0, 0x91 },
new byte[] { 0x4C, 0x80, 0xEB, 0x0E },
new byte[] { 0x5D, 0x0A, 0xAC, 0x8F },
};
}
This would be a better way to get each sub-array:
foreach (byte[] selected in OBJECTS())
{
/* do something with `selected` */
}