Mecanik
Mecanik

Reputation: 1049

Sharing chained structure with memory mapped file from C++ to C#

Following my other question here, I have been able to share string from C++ to C# thanks to this community.

However, I need to go one level above and I need to share chained structures from C++ to C# using memory mapping.

In an example scenario:

My C++ structures:

struct STRUCT_2
{
    char Name[260];
};

struct STRUCT_1
{
    void Init()
    {
        this->Count = 0;
        this->Index = 0;
    }

    DWORD Count;
    DWORD Index;
    STRUCT_2 Table[256];
};

And me trying to "transfer" it to C#:

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public unsafe struct STRUCT_2
{
   [FieldOffset(0)]
   public fixed char Name[260];
}

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public unsafe struct STRUCT_1
{
   void Init()
   {
     this.Count = 0;
     this.Index = 0;
   }
   [FieldOffset(0)]
   public uint Count;
   [FieldOffset(0)]
   public uint Index;
   [FieldOffset(100)]
   [MarshalAs(UnmanagedType.LPStruct, SizeConst = 256)]
   public STRUCT_2 Table;
}

This works partially, basically I can see the values from Count and Index, however I cannot see values or even get them in STRUCT_2.

I have tried to change:

public STRUCT_2[] Table;

But then the compiler tells me:

"The specified Type must be a struct containing no references."

So my question would be, how can you read structures within structures using MemoryMappedFile in C# ?

Advice, thoughts or examples are very welcomed.

Update:

Complete testable code in C#:

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
        public unsafe struct STRUCT_2
        {
            [FieldOffset(0)]
            public fixed byte Name[260];
            // Fix thanks to Ben Voigt
        }

        [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
        public unsafe struct STRUCT_1
        {
            void Init()
            {
                this.Count = 0;
                this.Index = 0;
            }
            [FieldOffset(0)]
            public uint Count;
            [FieldOffset(0)]
            public uint Index;
            [FieldOffset(100)]
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
            public STRUCT_2[] Table;
        }

        static void Main(string[] args)
        {
            MemoryMappedFileSecurity CustomSecurity = new MemoryMappedFileSecurity();
            CustomSecurity.AddAccessRule(new System.Security.AccessControl.AccessRule<MemoryMappedFileRights>("everyone", MemoryMappedFileRights.FullControl, System.Security.AccessControl.AccessControlType.Allow));

            var mappedFile = MemoryMappedFile.CreateOrOpen("Local\\STRUCT_MAPPING", 1024, MemoryMappedFileAccess.ReadWriteExecute, MemoryMappedFileOptions.None, CustomSecurity, System.IO.HandleInheritability.Inheritable);

            using (var accessor = mappedFile.CreateViewAccessor())
            {
                STRUCT_1 data;

                accessor.Read<STRUCT_1>(0, out data); // ERROR !
                //The specified Type must be a struct containing no references.

                Console.WriteLine(data.Count);
                Console.WriteLine(data.Index);
            }
        }

Upvotes: 2

Views: 2527

Answers (1)

Jake
Jake

Reputation: 139

Check this out.

Tested on Visual Studio 2017, Windows 7 x64.

Write and Read data works find.

It's also good study for me.

Take time on this.

Godspeed!

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct STRUCT_2
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)]
        public  byte[]  Name;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 4)]
    public struct STRUCT_1
    {
        void Init()
        {
            this.Count = 0;
            this.Index = 0;
        }
        public uint Count;
        public uint Index;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] //! array size of 10.
        public STRUCT_2 [] Table;
    }

    static void test3()
    {

        MemoryMappedFileSecurity CustomSecurity = new MemoryMappedFileSecurity();
        CustomSecurity.AddAccessRule(new System.Security.AccessControl.AccessRule<MemoryMappedFileRights>
            ( "everyone"
            , MemoryMappedFileRights.FullControl
            , System.Security.AccessControl.AccessControlType.Allow));

        using (var mappedFile = MemoryMappedFile.CreateOrOpen("Local\\STRUCT_MAPPING"
                                                    , 10 * 1024
                                                    , MemoryMappedFileAccess.ReadWriteExecute
                                                    , MemoryMappedFileOptions.None
                                                    , CustomSecurity
                                                    , System.IO.HandleInheritability.Inheritable))
        { 
            using (var accessor = mappedFile.CreateViewAccessor())
            {
                //! test setting.
                int table_count = 5;

                //! write data.
                STRUCT_1 write_data;
                write_data.Index = 1;
                write_data.Count = 2;
                write_data.Table = new STRUCT_2[10];

                for (int i = 0; i < 10; i++)
                {
                    write_data.Table[i].Name = new byte[260];
                    write_data.Table[i].Name[0] = (byte)i;
                }

                //! ----------------------------
                // Get size of struct
                int size = Marshal.SizeOf(typeof(STRUCT_1));
                byte[] data = new byte[size];

                // Initialize unmanaged memory.
                IntPtr p = Marshal.AllocHGlobal(size);

                // Copy struct to unmanaged memory.
                Marshal.StructureToPtr(write_data, p, false);

                // Copy from unmanaged memory to byte array.
                Marshal.Copy(p, data, 0, size);

                // Write to memory mapped file.
                accessor.WriteArray<byte>(0, data, 0, data.Length);

                // Free unmanaged memory.
                Marshal.FreeHGlobal(p);
                p = IntPtr.Zero;

                //! ----------------------------------------------
                STRUCT_1 read_data;
                size = Marshal.SizeOf(typeof(STRUCT_1));
                data = new byte[size];

                // Initialize unmanaged memory.
                p = Marshal.AllocHGlobal(size);


                // Read from memory mapped file.
                accessor.ReadArray<byte>(0, data, 0, data.Length);

                // Copy from byte array to unmanaged memory.
                Marshal.Copy(data, 0, p, size);

                // Copy unmanaged memory to struct.
                read_data = (STRUCT_1)Marshal.PtrToStructure(p, typeof(STRUCT_1));

                // Free unmanaged memory.
                Marshal.FreeHGlobal(p);
                p = IntPtr.Zero;


                Console.WriteLine(read_data.Index);
                Console.WriteLine(read_data.Count);
            }
        }
    }

Upvotes: 3

Related Questions