Hossein Narimani Rad
Hossein Narimani Rad

Reputation: 32481

`Marshal.StructureToPtr` throws exception trying to get byte array representation of a structure

I'm going to save a DataTable to *.dbf file (dBase IV). So I have a structure like this:

[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct DbfHeader
{
    [FieldOffset(0)]
    private byte versionNumber;

    [FieldOffset(1)]
    private byte yearOfLastUpdate;

    [FieldOffset(2)]
    private byte monthOfLastUpdate;

    [FieldOffset(3)]
    private byte dayOfLastUpdate;

    [FieldOffset(4)]
    private Int32 numberOfRecords;

    [FieldOffset(8)]
    private Int16 lengthOfHeader;

    [FieldOffset(10)]
    private Int16 lengthOfEachRecord;

    [FieldOffset(12)]
    private Int16 reserved1;

    [FieldOffset(14)]
    private byte incompleteTransaction;

    [FieldOffset(15)]
    private byte encryptionFlag;

    [FieldOffset(16)]
    private byte[] freeRecordThread;

    [FieldOffset(20)]
    private byte[] reserved2;

    [FieldOffset(28)]
    private byte mdxFlag;

    [FieldOffset(29)]
    private byte languageDriver;

    [FieldOffset(30)]
    private Int16 reserved3;

    public DbfHeader(int numberOfRecords, int numberOfFields, short recordLength, Encoding encoding)
    {
        // some code that initialize each field
    }

}

Also, I have a method to convert DbfHeader variables to byte[] array like this:

public static byte[] StructureToByteArray<T>(T structure)
{
    int len = Marshal.SizeOf(structure);
    byte[] result = new byte[len];
    IntPtr ptr = Marshal.AllocHGlobal(len);
    Marshal.StructureToPtr(structure, ptr, true);
    Marshal.Copy(ptr, result, 0, len);
    Marshal.FreeHGlobal(ptr);
    return result;
}

But this method is not working. At the Marshal.StructureToPtr(structure, ptr, true) line, I get this exception:

Attempted to read or write protected memory. 
This is often an indication that other memory is corrupt.

Anyone knows what is going wrong? Any help will be appreciated.

Upvotes: 0

Views: 3290

Answers (1)

Matthew Watson
Matthew Watson

Reputation: 109567

Why are you passing fDeleteOld as true to Marshal.StructureToPtr()?

You should be passing false as far as I can see.

I think you should also be calling Marshal.DestroyStructure() after copying the memory:

public static byte[] StructureToByteArray<T>(T structure)
{
    int len = Marshal.SizeOf(structure);
    byte[] result = new byte[len];
    IntPtr ptr = Marshal.AllocHGlobal(len);
    Marshal.StructureToPtr(structure, ptr, false);
    Marshal.Copy(ptr, result, 0, len);
    Marshal.DestroyStructure(ptr, typeof(T));
    Marshal.FreeHGlobal(ptr);
    return result;
}

The reason it was crashing before was because passing the fDeleteOld as true assumes that you have already called Marshal.StructureToPtr() for that IntPtr. Because you hadn't, it crashed (the memory block was not initialised in the way that StructureToPtr() was expecting).

However, you still need to clean up the memory by calling Marshal.DestroyStructure() as per my example above. This is needed to clean up data used for structures that contain references. (Your particular example struct doesn't contain references, but you could pass such a struct to your StructureToByteArray()).

Finally, note that if you did this, it wouldn't crash (this code is pointless other than to demonstrate how the fDeleteOld flag is used):

Marshal.StructureToPtr(structure, ptr, false); // First time; must be false.
Marshal.StructureToPtr(structure, ptr, true); // Second time: Now it can be true.

Upvotes: 4

Related Questions