Marshalling Complicated Structures using PInvoke

This article talks about marshaling structures using PInvoke which has a pointer to an array of another structure as a member.

When calling native functions in a managed application, a frequent question that comes to mind is how to marshal a nested complicated structure.  Recently I came across a scenario where we have to marshal a structure which contains a pointer to an array of another structure as a member. 

When doing PInvoke, it’s essential to know what is the layout of the native structure and how it can be marshaled.  During marshaling, one of the most important steps is converting unmanaged types to managed types.  You can use the below link for conversion table to match basic data types.

Here is an example of C struct definitions that need to be marshaled and a function  to marshal them

// NativeDLL.cpp : Defines the exported functions for the DLL application.


#include "stdafx.h"

#include <strsafe.h>

#include <stdio.h>



#define _EXPORTS_API __declspec(dllexport)


#define HASHSIZE            151


typedef struct CELL


    char                *name;                       

    char                *label;

    char                *fmt;

    double              amount;

    unsigned int         precision;

    unsigned int         flags;

    unsigned int         rank;




typedef struct CELLTABLE


    CELL              *table[HASHSIZE];

    void              *userData;                // reserved for future use





extern "C" _EXPORTS_API void NativeFunction(CELLTABLE * pFinance)


      char  buffer[1024];

      for ( int ix = 0 ; pFinance->table[ix] != NULL ; ix++ )


            CELL * pCell = pFinance->table[ix] ;

StringCbPrintf(buffer, sizeof(buffer), "%d:\n%f\n%s\n%u\n%u\n%u\n",ix, pCell->amount, pCell->name, pCell->precision, pCell->flags, pCell->rank);





Note: Char set of native application is set to Multi-Byte Character Set.

Managed Structure equivalent to Native Structure:

//MyManagedApp.cs file


using System;

using System.Collections.Generic;

using System.Text;

using System.Runtime.InteropServices;


namespace MyManagedAPP


    public class MyClass


        private const int HASHSIZE = 151;

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]

        public class CELL


            public string name;

            public string label;

            public string fmt;

            public double amount;

            public UInt32 precision;

            public UInt32 flags;

            public UInt32 rank;





        public class CELLTABLE


            [MarshalAs(UnmanagedType.ByValArray, SizeConst = HASHSIZE)]

            public IntPtr[] table;

            public IntPtr userData;






        public static extern void NativeFunction(IntPtr finance);


        public void Test()


            CELL[] cells = new CELL[HASHSIZE];

            cells[0] = new CELL();

            cells[0].amount = 123.45;

            cells[0].name = "First Record";

            cells[0].precision = 50;

            cells[0].flags = 0;

            cells[0].rank = 0;


            cells[1] = new CELL();

            cells[1].amount = 9876.54;

            cells[1].name = "Second Record";

            cells[1].precision = 3;

            cells[1].flags = 0;

            cells[1].rank = 1;


            cells[2] = new CELL();

            cells[2].amount = 24680.1357;

            cells[2].name = "Third Record";

            cells[2].precision = 2;

            cells[2].flags = 255;

            cells[2].rank = 4;


            CELLTABLE ct = new CELLTABLE();


            ct.table = new IntPtr[HASHSIZE];

            for (int ix = 0; ix < HASHSIZE && cells[ix] != null; ix++)


                int nSizeCell = Marshal.SizeOf(cells[ix]);

                ct.table[ix] = Marshal.AllocHGlobal(nSizeCell);

                Marshal.StructureToPtr(cells[ix], ct.table[ix], false);



            int nSizeTable = Marshal.SizeOf(ct);

            IntPtr pCellTable = Marshal.AllocHGlobal(nSizeTable);

            Marshal.StructureToPtr(ct, pCellTable, false);




        static void Main()


            MyClass md = new MyClass();







Note: Marshaling the structure in C#, it’s essential to set CharSet to Ansi if the C++ application expects an Ansi type string.  If it’s not set to Ansi, output can be unexpected (Mostly first character of string passed). If you don't explicitly set the CharSet property, then its default is CharSet.Ansi. If the native application is built with UNICODE defined, the CharSet should be set to CharSet.Unicode.


Jyoti Patel

Developer Support VC++ and C# 

Comments (3)
  1. lavanya says:

    Good article.

    I have a double pointer in my structure like CELL **pcell.Please help me how to declare in c#

    Thanks in advance

  2. Nickolay Kondratyev says:

    If I am not mistaken:

    "IntPtr pCellTable = Marshal.AllocHGlobal(nSizeTable);"

    will cause memory to be leaked since memory allocated by 'Marshal.AllocHGlobal' is unmanaged and CLR will not clean it up.  'Marshal.FreeHGlobal();' must be called.

  3. @Nickolay , agreed  I would suggest you to free up the memory using FreeHGlobal. How ever in my experience with clients & discussions there is consensus around , when allocating memory with (Marshal.AllocHGlobal), and the size of the process heap grows, it will never be released completely so that .NET can use it again.  Be careful not to grow your process heap out of bounds and avoid having your .NET applications run out of memory space.

Comments are closed.

Skip to main content