Initialization of a struct in C#? - c#

Some code I'm modifying makes extensive use of structs to communicate with some factory equipment by loading them to or from byte arrays.
Here's a made-up ultra-simplified example of such a struct.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct K_FOO
{
public byte a_byte; // 1 byte
public BYTE3_TYPE b3; // 3 bytes
public int num; // happens to be 4 bytes
}
BYTE3_TYPE looks like this...
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class BYTE3_TYPE
{
[System.Runtime.InteropServices.MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] L = new byte[3];
}
If I just do a
K_FOO origFoo = new K_FOO();
the int and the byte are initialized to 0's, I assume because they are native types, but the byte array b3 is *un*initialized - all nulls. I have to explicitly load it, for example,
BYTE3_TYPE b3 = new BYTE3_TYPE();
origFoo.b3 = b3;
... and I couldn't think of any alternative because structs don't take parameterless constructors but the real structs are huge.
But I noticed something interesting. We have routines to copy these structs to and from byte arrays. For example . . .
public static T ByteArrayToStructure<T>(byte[] buffer) where T : struct
{
int length = buffer.Length;
IntPtr ptr = Marshal.AllocHGlobal(length); // allocate (length) bytes
Marshal.Copy(buffer, 0, ptr, length); // copies into UNmanaged space
T result = (T)Marshal.PtrToStructure(ptr, typeof(T));
Marshal.FreeHGlobal(ptr);
return result;
}
...and if I call it...
K_FOO retFoo = ByteArrayToStructure<K_FOO>(buffer.bytes);
... the resulting struct is returned fully initialized, the bytes in the byte array have all had their space allocated so they could be loaded, apparently in the PtrToStructure() call. This implies .Net "knows" how to initialize such a struct. So is there some way to get .Net to do that for me so I can avoid writing hundreds of lines of explicit initialization code? Thanks in advance!

If you make your BYTE3_TYPE a struct instead of a class, the default constructor (calling K_FOO origFoo = new K_FOO();) will initialize the entire thing to zero correctly.
This is also likely the correct approach if you're trying to match existing specifications and pass this to custom hardware.

Related

Can't copy a fixed-size byte array from a struct to another array in a struct in C#

Using the NetworkInfo2SecurityParameter function I am trying to copy the fixed-size buffer unkRandom from from LdnNetworkInfo struct held in NetworkInfo struct to the SecurityParameter struct's buffer.
Basically I am making a method to convert these two types, and I want to copy these two arrays to another. This code features all structs relevant.
The error is happening at the Buffer.MemoryCopy() function. The error is "You cannot use fixed size buffers contained in unfixed expressions. Try using the 'fixed' statement"
unsafe void NetworkInfo2SecurityParameter(NetworkInfo info, out SecurityParameter output)
{
output = new SecurityParameter();
output.sessionId = info.networkId.sessionId;
Buffer.MemoryCopy(output.unkRandom, info.ldn.unkRandom, 16, 16);
}
struct SecurityParameter {
public unsafe fixed byte unkRandom[16];// = new byte[16];
public SessionId sessionId;
};
struct NetworkInfo : /sf::/LargeData {
public NetworkId networkId;
public CommonNetworkInfo common;
public LdnNetworkInfo ldn;
};
struct LdnNetworkInfo {
public unsafe fixed byte unkRandom[16];// = new byte[16];
public ushort securityMode;
public byte stationAcceptPolicy;
public unsafe fixed byte _unk1[3];// = new byte[3];
public byte nodeCountMax;
public byte nodeCount;
//TODO non primitive array,,
private unsafe fixed byte _nodes[sizeof(NodeInfo)*NodeCountMax]; //Needs to be fixed array, and needs to be casted to NodeInfo span, so thats why its size is this
public unsafe fixed Span<NodeInfo> nodes => MemoryMarshal.Cast<byte, NodeInfo>(MemoryMarshal.CreateSpan(ref _nodes[0], 128));
public ushort _unk2;
public ushort advertiseDataSize;
public unsafe fixed byte advertiseData[AdvertiseDataSizeMax];// = new byte[AdvertiseDataSizeMax];
public unsafe fixed byte _unk3[148];// = new byte[148];
};
struct is assignable, like any primitive. Undoubtedly faster than Buffer.MemoryCopy() would be:
public unsafe struct FixedSizeBufferWrapper
{
public unsafe fixed byte unkRandom[16];
}
unsafe
{
fixed (byte* firstStruct = somewhere.securityParameter, otherStruct = somewhereElse.ldnNetworkInfo )
{
//one assignment blits all contents
*((FixedSizeBufferWrapper*)firstStruct.unkRandom) =
*((FixedSizeBufferWrapper*)otherStruct.unkRandom);
}
}
We cast buffers in each of your original structs to the wrapper pointer type and dereference each pointer SO THAT we can assign one to the other; assigning fixed buffers directly is not possible.
We have to cast outside the fixed statement because (at least in my version of C#) we aren't allowed to cast inside fixed(...) and we cannot assign anything other than a primitive type (usually byte[]) as the buffer type. The wrapper struct exists purely for this casting / assignment.
Fixed it with this approach: I created a new variable called ret, which I did copy the data to, and then I assigned ret to output.
unsafe void NetworkInfo2SecurityParameter(NetworkInfo info, out SecurityParameter output)
{
var ret = new SecurityParameter();
output.sessionId = info.networkId.sessionId;
Buffer.MemoryCopy(ret.unkRandom, info.ldn.unkRandom, 16, 16);
output = ret;
}

Can I get a Span<byte> that references a struct member field of non-byte type?

Say I have a struct like the following:
struct MyStruct {
Guid g;
}
Is it possible to get a Span<byte> that references the bytes of the struct?
struct MyStruct {
Guid g;
public void Foo() {
Span<byte> bytes = ???
}
}
Such that bytes would be a Span<byte> of length 16 that would allow reading and writing the individual bytes of the Guid field directly.
I can do something similar with unsafe code, but it seems that this should now be possible with safe code via span, but I can't figure out how to produce the span.
Edit: Clarify that I want a Span that points to the actual storage location of the Guid. Meaning new Span<byte>(g.ToByteArray)) is not what I'm looking for. That will allocate a new array, copy the bytes to the array, and create a Span referencing the newly allocated array. Modifying bytes via such a span will not modify the Guid.
You can force the Guid to an array
struct MyStruct {
public Guid g;
}
...
var s = new MyStruct();
var span = new Span<byte>(s.g.ToByteArray());
span[2] = 4;
Note : The above will not modify the original struct. However, as you pointed out, you can do this with unsafe
var s = new MyStruct();
var span = new Span<byte>(&s , Marshal.SizeOf(s));
// woah it just become mutable
span[2] = 4;
Try this in .Net Core 2.1 or above:
Span<MyStruct> valSpan = MemoryMarshal.CreateSpan(ref mystruct, 1);
Span<byte> span = MemoryMarshal.AsBytes(valSpan);
But use with caution, you should not contain any pointer or reference type in your struct, since GC can move reference type. Actually, it will do runtime check and throw an exception if you do that.
Look at the constructors of Span class, the only ways to create a Span is providing a managed array or a pointer.
But Guid is made up with 1 int, 2 shorts and 8 bytes, not a byte array.
So it's impossible (at least not now).

Unmarshaling a structure containing a variable-size array of a structure

I'm trying to unmarshall an array of variable length of a structure nested inside another structure as in the following code:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CardInfoRequest
{
public ulong CardId;
public byte AppListLength;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct)]
public CardApp[] AppList;
}
[Serializable()]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CardApp
{
public ulong CardAppId;
public byte SomeInformation;
}
And I am unmarshalling it by doing:
var lDataPointer = Marshal.AllocHGlobal(pSize);
Marshal.Copy(pData, 0, lDataPointer, pSize);
var lResult = Marshal.PtrToStructure(lDataPointer, typeof(CardInfoRequest));
Marshal.FreeHGlobal(lDataPointer);
Where pData is a byte array containing the marshalled structure and pSize is its size in runtime (18 when the array has one item, 27 when the array has two items and so forth...).
However, no matter the size in bytes of the stream, whenever I unmarshall it I am always getting AppList.Length == 1.
Can I unmarshall it correctly? Should I do it byte by byte?
Thanks in advance!
The pinvoke marshaller has no idea what size array it needs to create. A fixed-size buffer cannot work either. You have to do it in two steps, first unmarshal the first 2 members, you now know the array size from AppListLength. Create the array, then unmarshal the array elements one by one in a loop.
So roughly (untested)
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct CardInfoRequestHeader
{
public ulong CardId;
public byte AppListLength;
}
public struct CardInfoRequest
{
public CardInfoRequestHeader Header;
public CardApp[] AppList;
}
...
var req = new CardInfoRequest();
req.Header = (CardInfoRequestHeader)Marshal.PtrToStructure(pData, typeof(CardInfoRequestHeader));
req.AppList = new CardApp(req.Header.AppListLength);
pData += Marshal.SizeOf(CardInfoRequestHeader);
for (int ix = 0; ix < req.AppList.Length; ++ix) {
req.AppList = (CardInfo)Marshal.PtrToStructure(pData, typeof(CardInfo));
pData += Marshal.SizeOf(CardInfo);
}
Beware the ulong and Pack = 1 are Red Flags, unmanaged data rarely looks like that. The code snippet does make the hard assumption that Pack=1 is accurate.

How to get the Updated Size of Structure?Size of the Structure not getting Updated When I add values to dynamic array Fields

This is related to my previous question, and thought will make this as a sparate question as it will make more sense.
I have created my Struct as :
public struct Smb_Parameters
{
public byte WordCount;
public ushort[] Words;
}
Without Assigning any Values when I try to get the size of the Struct it returns 4-Bytes:
Smb_Parameters smbParameter = new Smb_Parameters();
int len1 = Marshal.SizeOf(smbParameter );
MessageBox.Show(len1.ToString());
But When I assign the Values to structure fields :
Smb_Parameters smbParameter = new Smb_Parameters();
string myString= "String ll be converted to byte";
smbParameter.WordCount=0x00;
smbParameter .Words=Encoding.ASCII.GetBytes(myString);
int len1 = Marshal.SizeOf(smbParameter );
MessageBox.Show(len1.ToString());
Still now It shows the Length as 4-Bytes but I need the updated size.
If you wish to get the size as if it was an unmanaged type, you'd need to supply some information about its fields (e.g., the length of the array). Without it, the array length would not be taken into account.
e.g.,
public struct Smb_Parameters1
{
public byte WordCount; //1 byte
public ushort[] Words; //4 bytes (a "pointer")
}
Marshal.SizeOf(typeof(Smb_Parameters1)); //8 (with padding)
//I don't see how you get 4 unless you are on a 16-bit system maybe
[StructLayout(LayoutKind.Sequential)]
public struct Smb_Parameters2
{
public byte WordCount; //1 byte
[MarshalAs(UnmanagedType.ByValArray, SizeConst=10)]
public ushort[] Words; //20 bytes (2 * 10 bytes)
}
Marshal.SizeOf(typeof(Smb_Parameters2)); //22 (with padding)
Note that these are sizes are fixed (as if it was declared in a C/C++ program). The size reported by SizeOf() will only use these and not take into account what size array you store in Words.
Smb_Parameters1 s1 = new Smb_Parameters1() { Words = new ushort[] { 0, 1, 2 } };
Smb_Parameters2 s2 = new Smb_Parameters2() { Words = new ushort[] { 0, 1, 2 } };
Marshal.SizeOf(s1); //8 bytes
Marshal.SizeOf(s2); //22 bytes
The Words field is an array, and arrays are reference types. The structure doesn't actually contains the array items, it only contains a reference to the array, which is stored somewhere else (typically on the heap). So SizeOf always returns the same size, since the size of a reference doesn't depend on the size of the object pointed by that reference.
Marshal.SizeOf() returns a fixed size of an class/struct. The length does never depend on the contents of your object passed.
You could calculate the size with Marshal.SizeOf(typeof(byte))+Marshal.SizeOf(typeof(ushort))*yourarraylength
Another way would be to use the BinaryFormatter class to serialize your struct into binary form. It returns you a byte array. If you take the length of it you know its serialized size. Note that the result of BinaryFormatter cannot not be easily read by a non-.net language since its format is native to .net only.

converting between struct and byte array

his question is about converting between a struct and a byte array. Many solutions are based around GCHandle.Alloc() and Marshal.StructureToPtr(). The problem is these calls generate garbage. For example, under Windows CE 6 R3 about 400 bytes of garbarge is made with a small structure. If the code below could be made to work the solution could be considered cleaner. It appears the sizeof() happens too late in the compile to work.
public struct Data
{
public double a;
public int b;
public double c;
}
[StructLayout(LayoutKind.Explicit)]
public unsafe struct DataWrapper
{
private static readonly int val = sizeof(Data);
[FieldOffset(0)]
public fixed byte Arr[val]; // "fixed" is to embed array instead of ref
[FieldOffset(0)]
public Data; // based on a C++ union
}
You can in fact use the constant value 1 as the size of the fixed array in DataWrapper. There is no bounds checking when accessing an unsafe fixed array nor is there any mechanism to request its length at runtime. The presence of the Data member at offset 0 is enough to ensure that you can actually read and write sizeof(Data) bytes to/from DataWrapper.Arr.
With that said, a more straight forward solution would be to cast a Data* pointer to byte* as follows:
unsafe {
Data d = new Data();
d.a = 125.5;
d.b = 0x7FEEDDEE;
d.c = 130.0;
byte* bytes = (byte*)&d;
for (int i = 0; i < sizeof(Data); i++) {
Console.WriteLine(i + ": " + bytes[i]);
}
}

Categories