title: Can I marshal a C-struct with a 2d array without using "unsafe"?
body: <p>I have a C DLL that I'm writing a C# interop class for.</p>
  
  <p>In the C DLL, one of the key methods fills a 2d structure; the structure is allocated and freed by helper methods, like so:</p>
  
  <pre><code>// Simple Struct Definition -- Plain Old Data
  typedef struct MyPodStruct_s
  {
      double a;
      double b;
  } MyPodStruct;
  
  typedef struct My2dArray_s
  {
      MyPodStruct** arr; // allocated by Init2d;
                         // array of arrays.
                         // usage: arr[i][j] for i&lt;n,j&lt;m
      int n;
      int m;
  } My2dArray;
  
  void Init2d(My2dArray* s, int n, int m);
  void Free2d(My2dArray* s);
  
  // fill according to additional work elsewhere in the code:
  void Fill2dResult(My2dArray* result);
  </code></pre>
  
  <p>Simply marshaling <code>My2dArray.arr</code> as a pointer to a pointer looks like an issue. <strong>Is there any way I can marshal this for C#, so that I <em>don't</em> need the C# code to be unsafe?</strong><br>
  (I'd strongly prefer to avoid modifying my C API if possible, or at least keeping the changes minimal, but this is an option if it's the only way.)</p>
  
  <p>Here's the unsafe C# code I have presently (simplified slightly from the real thing). It works fine and does what I want, but requires unsafe usage:</p>
  
  <pre><code>class FooInterop
  {
      public struct MyPodStruct // Plain Old Data
      {
          public double a;
          public double b;
      };
  
      [StructLayout(LayoutKind.Sequential)]
      private unsafe struct unmanaged2d
      {
          public MyPodStruct** arr;
          public int n;
          public int m;
      };
  
      [DllImport("Foo.DLL", EntryPoint = "Init2d", CallingConvention = CallingConvention.Cdecl)]
      private static extern void unsafe_Init2d(ref FooInterop.unmanaged2d, int n, int m);
      [DllImport("Foo.DLL", EntryPoint = "Free2d", CallingConvention = CallingConvention.Cdecl)]
      private static extern void unsafe_Free2d(ref FooInterop.unmanaged2d);
      [DllImport("Foo.DLL", EntryPoint = "Fill2dResult", CallingConvention = CallingConvention.Cdecl)]
      private static extern void unsafe_Fill2dResult(ref FooInterop.unmanaged2d);
  
      public static FooInterop.MyPodStruct[,] Fill2dResult()
      {
          unmanaged2d unsafeRes = new unmanaged2d();
          FooInterop.MyPodStruct[,] res;
  
          unsafe_Init2d(ref unsafeRes, n, m); // I have n, m from elsewhere
          unsafe_Fill2dResult(ref unsafeRes );
          res = new FooInterop.MyPodStruct[n,m];
  
          for (int i=0; i&lt;n; ++i)
          {
              for (int j=0; j&lt;m; ++j)
              {
                  unsafe
                  {
                      res[i, j] = unsafeRes.arr[i][j];
                  }
              }
          }
  
          unsafe_Free2d(ref unsafeRes );
  
          return res;
      }
  }
  </code></pre>