title: C# marshalling large struct to c DLL
body: <p>I'm trying to access functions exported by some DLL (for accessing some specific hardware). The access to the functions using DLLImport and simple datatypes is working. But to some functions I need to pass some quite large structures. To overcome size limitations of automated marshalling I figured out to use IntPtr.</p>
  
  <pre><code>typedef struct
  {
      double value[MemDepth];
      double offset[MemDepth];
      unsigned long start;
      BOOL active;
  } RegisterData;
  
  typedef struct 
  {
      RegisterData a,b,c,d,e,f,g,h;
      BOOL active;
      BOOL test;
  } Register;
  </code></pre>
  
  <p>And this is how I tried to put them in C#:</p>
  
  <pre><code>[StructLayout(LayoutKind.Sequential)]
  public struct RegisterData
  {
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = defines.MemDepth)]
      public double[] value;
  
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = defines.MemDepth)]
      public double[] offset;
  
      UInt32 start;
  
      [MarshalAs(UnmanagedType.U1)]
      bool active;
  }
  
  [StructLayout(LayoutKind.Sequential)]
  public struct Register
  {
      RegisterData a,b,c,d,e,f,g,h;
      [MarshalAs(UnmanagedType.U1)]
      bool active;
      [MarshalAs(UnmanagedType.U1)]
      bool test;
  }
  </code></pre>
  
  <p>The function I'm trying to call looks the following in the DLL:</p>
  
  <pre><code>long ResetControl(Register* control);
  </code></pre>
  
  <p>Which I translated into:</p>
  
  <pre><code>[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
  public static extern Int32 Reset(IntPtr control);
  </code></pre>
  
  <p>I'm then trying to call the function like this:</p>
  
  <pre><code>public Int32 lib_Reset(ref Register control)
  {
      int size = System.Runtime.InteropServices.Marshal.SizeOf(control);
  
      //IntPtr ptr = System.Runtime.InteropServices.Marshal.FreeHGlobal(size);
      IntPtr ptr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(size);
  
      System.Console.WriteLine("requested size of: " + size); // shows correct
  
      System.Runtime.InteropServices.Marshal.StructureToPtr(control, ptr, false);
  
      System.Console.WriteLine("mem done");
  
      Int32 ret = Reset(ptr);
  
      System.Console.WriteLine("called reset");
  
      System.Runtime.InteropServices.Marshal.PtrToStructure(ptr, control);
      System.Console.WriteLine("transfered back");
  
      System.Runtime.InteropServices.Marshal.FreeCoTaskMem(ptr);
      //System.Runtime.InteropServices.Marshal.FreeHGlobal(ptr);
  
      System.Console.WriteLine("freeed mem");
      ptr = IntPtr.Zero;
  
      return ret;
  }
  </code></pre>
  
  <p>This results in " A heap has been corrupted." when calling the DLLs Reset function. I already tried different ways but non worked.</p>
  
  <p><strong>EDIT:</strong></p>
  
  <p>I now got it working by cleaning and rebuilding the project using the IntPtr approach. It does not work directly using "ref". When using "ref" I always get:</p>
  
  <pre><code> Cannot marshal 'parameter #1': Internal limitation: structure is too complex or too large.
  </code></pre>
  
  <p>I now do the following to convert from structure to pointer and back:</p>
  
  <pre><code>public Int32 lib_Reset(ref Register control)
  {
      int size = Marshal.SizeOf(control);
      IntPtr ptr = Marshal.FreeHGlobal(size);
      Marshal.StructureToPtr(control, ptr, false);
      Int32 ret = Reset(ptr);
      control = (Register)Marshal.PtrToStructure(ptr, typeof(control));
      Marshal.FreeHGlobal(ptr);
      ptr = IntPtr.Zero;
      return ret;
  }
  </code></pre>
  
  <p>That way I can call Reset but it seems the functions does not do anything.</p>
  
  <pre><code>Register reg = new Register();
  
  reg.active = true;
  System.Console.WriteLine(reg.active);   // .active = true
  lib_Reset(ref reg);         // should set .active = false
  System.Console.WriteLine(reg.active);   // .active = true
  </code></pre>
  
  <p>The function Reset should initialize the Register structure. Thereby it sets active to false. But in the code shown above the value of active does not change. I tested the same with a integer value. It also did not change.</p>
  
  <p>I hope anyone here can point me to the right direction in accessing the data.</p>
  
  <p>Thank you,
  Tobias</p>