2

I am trying to deserialize a byte array to a struct.

Here is my deserialization function:

void RawDeserialize(byte[] bytearray, object obj)
{
    int len = Marshal.SizeOf(obj);

    IntPtr i = Marshal.AllocHGlobal(len);

    Marshal.Copy(bytearray, 0, i, len);

    obj = Marshal.PtrToStructure(i, obj.GetType());

    Marshal.FreeHGlobal(i);
}

I call it with

RawDeserialize(outarr, outbuf);

Where outarr is a byte array of length 22 and outbuf is my struct which looks like this:

[StructLayout(LayoutKind.Sequential,Size =22)]
public struct ID_OUTPUT
{
    public HEADER_OUTPUT hdr; //Another struct size=8
    public byte bType;
    public byte bRunning;
    [MarshalAs(UnmanagedType.ByValTStr,SizeConst = 8)]
    public string softwareName; 
    public short softwareVersion;
} 

When I step-through debug in my deserialization function, obj is filled with correct values, but on return outbuf is filled with zeroes (or is never assigned to because I originally initialize everything to zero).

My initial thought is the object is not being passed by reference, but I assumed this should work because I found this deserialization function on another SO question (which I do not have the link for anymore).

So then I try to use the ref keyword, but then I get an error cannot convert from ref ID_OUTPUT to ref object.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
Seth Kitchen
  • 1,526
  • 19
  • 53

2 Answers2

3

You are redefining the reference of the local variable obj to something different than you passed to the method. The actual reference in the calling method doesn't change. When you change the method signature to ref the variables, it does work, but then you have a conversion problem.

Since your method is a void, the simplest thing to do is return the variable and type it appropriately (or cast it if you need to). (You can use generics on your return variable, whatever suits you best).

Required method signature:

ID_OUTPUT /* your struct return variable */ RawDeserialize(byte[] bytearray);

If the problem is solely the conversion problem, this fixes it:

ID_OUTPUT obj;
object outputObject = obj;
RawDeserialize(bytearray, ref outputObject);

obj = (ID_OUTPUT)outputObject;
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • But I want to use this deserialization method for other structs (other than ID_OUTPUT). Is that possible? – Seth Kitchen Oct 26 '15 at 14:28
  • 1
    I do recommend to use the method of ken2k, since that will be the most future proof method to use. My code is just to explain how to overcome your specific issue. – Patrick Hofman Oct 26 '15 at 14:37
  • Ok I am in the process of converting to his. By the way Marshal.SizeOf and Marshal.PtrStructure are becoming obsolete. Are there any solutions which don't use these? – Seth Kitchen Oct 26 '15 at 14:40
3

As you're dealing with multiple structure types and want an unique method that does the job, generics are a good option. This allows you to use a nice C# syntax without the need to cast your instances as object.

// For .Net 4.5 and previous versions
public static T RawDeserialize<T>(byte[] bytearray)
    where T : struct
{
    var type = typeof(T);
    int len = Marshal.SizeOf(type);

    IntPtr i = IntPtr.Zero;
    try
    {
        i = Marshal.AllocHGlobal(len);

        Marshal.Copy(bytearray, 0, i, len);

        return (T)Marshal.PtrToStructure(i, type);
    }
    finally
    {
        if (i != IntPtr.Zero)
        {
            Marshal.FreeHGlobal(i);
        }
    }
}

Usage:

ID_OUTPUT myStruct = RawDeserialize<ID_OUTPUT>(someByteArray);
ZZZZ myStruct2 = RawDeserialize<ZZZZ>(someByteArray);

For .Net 4.5.1+, you may want use the generic versions of SizeOf/PtrToStructure:

public static T RawDeserialize<T>(byte[] bytearray)
    where T : struct
{
    int len = Marshal.SizeOf<T>();

    IntPtr i = IntPtr.Zero;
    try
    {
        i = Marshal.AllocHGlobal(len);

        Marshal.Copy(bytearray, 0, i, len);

        return Marshal.PtrToStructure<T>(i);
    }
    finally
    {
        if (i != IntPtr.Zero)
        {
            Marshal.FreeHGlobal(i);
        }
    }
}
Community
  • 1
  • 1
ken2k
  • 48,145
  • 10
  • 116
  • 176