title: How to marshal a pointer to array of structs which contains an unsigned char array?
body: <p>I'm sorry if this sounds too specific, but I need to get this done exactly this way and I'm stuck at it for days. In my real scenario, I have a <code>MyStruct **ppObject</code> which address must be passed to a third-party dll so that it will point to an array of structs. Afterwards, I must p/Invoke this same pointer to array, but I'm having problems with the contents of the struct. Here's a MCVE:</p>
  
  <p><strong>Unmanaged.h:</strong></p>
  
  <pre><code>#ifndef _NATIVELIB_H_
  #define _NATIVELIB_H_
  
  #ifndef MCVE
  #define MCVE
  #endif
  
  struct PlcVarValue
  {
      unsigned char   byData[8];
  };
  
  #ifdef __cplusplus
  extern "C" {
  #endif
      MCVE __declspec(dllexport) void FillArray(void);
      MCVE __declspec(dllexport) void Clear(void);
      MCVE __declspec(dllexport) PlcVarValue** GetValues(void);
  
  #ifdef __cplusplus
  }
  #endif
  
  #endif // _NATIVELIB_H_
  </code></pre>
  
  <p><strong>Unmanaged.cpp</strong></p>
  
  <pre><code>#include "stdafx.h"
  #include "Unmanaged.h"
  #include &lt;iostream&gt;
  
  PlcVarValue** values;
  
  MCVE __declspec(dllexport) void FillArray(void) {
      values = (PlcVarValue**)malloc(sizeof(PlcVarValue*) * 5);
  
      for (int i = 0; i &lt; 5; i++) 
      {
          values[i] = new PlcVarValue();
          *values[i]-&gt;byData = i;
      }
  }
  
  MCVE __declspec(dllexport) void Clear(void) {
      delete *values;
      free(values);
  }
  
  MCVE __declspec(dllexport) PlcVarValue** GetValues(void) {
      return values;
  }
  </code></pre>
  
  <p><strong>PlcVarValue.cs</strong></p>
  
  <pre><code>using System.Runtime.InteropServices;
  
  namespace Managed
  {
      [StructLayout(LayoutKind.Sequential)]
      public class PlcVarValue
      {
          [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
          public byte[] Data;
      }
  }
  </code></pre>
  
  <p><strong>ManagedClass.cs</strong></p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Runtime.InteropServices;
  
  namespace Managed
  {
      public class ManagedClass
      {
          public ManagedClass()
          {
              FillArray();
          }
  
          [DllImport("Unmanaged.dll", CallingConvention = CallingConvention.Cdecl)]
          private static extern void FillArray();
  
          [DllImport("Unmanaged.dll", CallingConvention = CallingConvention.Cdecl)]
          private static extern IntPtr GetValues();
  
          [DllImport("Unmanaged.dll", CallingConvention = CallingConvention.Cdecl)]
          private static extern void Clear();
  
          public List&lt;PlcVarValue&gt; GetList()
          {
              int size = 5;
              var list = new List&lt;PlcVarValue&gt;();
              IntPtr ptr = GetValues();
  
              var gch = GCHandle.Alloc(ptr, GCHandleType.Pinned);
  
              try
              {
                  int memSize = Marshal.SizeOf(typeof(PlcVarValue));
                  for (int i = 0; i &lt; size; i++)
                  {
                      //I know this is wrong, it would work for a PlcVarValue* but I need it to work with a PlcVarValue**
                      list.Add((PlcVarValue)Marshal.PtrToStructure(new IntPtr(ptr.ToInt32() + memSize * i), typeof(PlcVarValue)));
                  }
              }
              finally
              {
                  gch.Free();
              }
  
              return list;
          }
  
          public void FreeMemory()
          {
              Clear();
          }
      }
  }
  </code></pre>
  
  <p><strong>Program.cs</strong></p>
  
  <pre><code>using Managed;
  using System;
  
  namespace Test
  {
      class Program
      {
          static void Main(string[] args)
          {
              var managed = new ManagedClass();
              var list = managed.GetList();
              foreach(var value in list)
                  Console.WriteLine(BitConverter.ToInt32(value.Data, 0));
  
              managed.FreeMemory();
              Console.ReadKey();
          }
      }
  }
  </code></pre>
  
  <p>I hope that code is self-explanatory, I'll provide more information if needed. My problem is that the byte array (which I'm trying to marshal from the aforementioned unsigned char array) in the <code>Program</code> class prints different random data every time I run the program but when I check the C++ side with the debugger (before marshalling takes place), everything is fine. Like I mentioned in a comment in the code above, I believe the problem lies in this line in the <code>ManagedClass</code> class: </p>
  
  <p><code>list.Add((PlcVarValue)Marshal.PtrToStructure(new IntPtr(ptr.ToInt32() + memSize * i), typeof(PlcVarValue)));</code></p>
  
  <p>I know for a fact that this works fine with <code>MyStruct *pObject</code> where <code>*pObject</code> is an array of the same struct, but in this case I need a pointer to a pointer since what the third-party dll requires is actually a <code>MyStruct ***pppObject</code> (beats me, but that's what I have to deal with). I have tried to copy data from <code>ppObject</code> to a single pointer but, even though it worked, I was not satisfied with the result as it had some undesired side-effects in the real application. Also, should my usage of GCHandle be wrong, I'd gladly accept advice on how to fix it, but that's not the main focus of this question.</p>