title: StringBuilder Field in a Structure Cannot Be Marshaled Properly
body: <p>I've struggled with at p/invoke problem for some time now. Keep getting "Attempted to read or write protected memory." all the time, and I suspect my marshaling is a bit off.</p>
  
  <p>Here is the c signature:</p>
  
  <pre><code>long ft_show(                             /* show file-attributes        */
      const struct ft_admission *admis,     /* I: transfer admission       */
      const struct ft_shwpar    *par,       /* IO: parameter-list          */
      struct ft_fileinfo        *info,      /* O: requested information    */
      struct ft_err             *errorinfo, /* O: error information        */
      void                      *options    /* I: options                  */
      );
  </code></pre>
  
  <p>and here are the related structures (in c)</p>
  
  <pre><code>struct ft_admission
  {
      char    *remsys;
      char    *remadmis;
      char    *remaccount;
      char    *rempasswd;
  };
  
  struct ft_shwpar
  {
      int                shwparvers;
      char               *fn;
      char               *mgmtpasswd;
      char               *fud;
      int                fudlen;
  };
  
  struct ft_fileinfo
  {
      int             ftshowivers;
      char            fn[INFO_FN_LEN];
      enum ft_ftype   filetype;
      enum ft_charset charset;
      enum ft_rform   recordform;
      long            recsize;
      enum ft_available availability;
      int             access;
      char            account[ACC_LEN];
      long            size;
      long            maxsize;
      char            legalqual[LQ_LEN];
      char            cre_user[USER_LEN];
      long            cre_date;
      char            mod_user[USER_LEN];
      long            mod_date;
      char            rea_user[USER_LEN];
      long            rea_date;
      char            atm_user[USER_LEN];
      long            atm_date;
      long long       fsize;
      long long       fmaxsize;
  };
  
  struct ft_err
  {
      long    main;
      long    detail;
      long    additional;
  };
  
  struct ft_options
  {                                                                       
      int ftoptsvers;
      int ftapivers;
  };
  </code></pre>
  
  <p>I've tried to create c# structs and calls like this:</p>
  
  <pre><code>[DllImport("ftapi.dll", EntryPoint = "ft_showdir")]
  private static extern long ft_showdir(ft_admission admis, ref ft_shwpar par, ft_fileinfo[] buf, int bufsize, ref ft_err errorinfo, ft_options options);
  
      public struct ft_admission
      {
          public String remsys;
          public String remadmis;
          public String remaccount;
          public String rempasswd;
      };
      public struct ft_shwpar
      {
          public int shwparvers;
          public String fn;
          public String mgmtpasswd;
          public IntPtr fud;
          public int fudlen;
      };
      [StructLayout(LayoutKind.Sequential, Size = 1464, CharSet = CharSet.Ansi), Serializable]
      public struct ft_fileinfo
      {
          public int ftshowivers;
          [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
          public String fn;
          public ft_ftype filetype;
          public ft_charset charset;
          public ft_rform recordform;
          public long recsize;
          public ft_available availability;
          public int access;
          [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
          public String account;
          public long size;
          public long maxsize;
          [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 81)]
          public String legalqual;
          [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 68)]
          public String cre_user;
          public long cre_date;
          [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 68)]
          public String mod_user;
          public long mod_date;
          [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 68)]
          public String rea_user;
          public long rea_date;
          [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 68)]
          public String atm_user;
          public long atm_date;
          public long fsize;
          public long fmaxsize;
      };
      public struct ft_err
      {
          public long main;
          public long detail;
          public long additional;
      }
      public struct ft_options
      {
          public int ftoptsvers;
          public int ftapivers;
      };
  </code></pre>
  
  <p>I've already used the ft_err and ft_options structs successfully in another call, so I think those are marshaled correctly.</p>
  
  <p>I think the problem is the ft_fileinfo structure. I want to use StringBuilder in there, but there is a bug in the framework preventing correct marshaling of StringBuilder inside structs. I've tried the workarounds described here: <a href="http://support.microsoft.com/kb/327109" rel="nofollow">http://support.microsoft.com/kb/327109</a>
  but no success.</p>
  
  <p>Here's the code snippet provided in the documentation of the api I try to access (some code has been removed as it's only used for printing info to the screen):</p>
  
  <pre><code>/* sample3.c     File management requests                    */
  /*************************************************************/
  static const char Sccsid[] = "@(#)sample3.c   2.33 2009/03/25";
  /*************************************************************
  
    Program call: sample3 &lt;directory&gt;
  
   *************************************************************/
  /*   Include Files                                           */
  /*************************************************************/
  #include &lt;stdio.h&gt;       /* printf()                         */
  #include &lt;stdlib.h&gt;      /* exit()                           */
  #include &lt;string.h&gt;      /* strcat(), strcpy()               */
  #include &lt;ftapi.h&gt;
  
  /*************************************************************/
  /*                Local Constants and Macros                 */
  /*************************************************************/
  #define BUFSIZE 200      /* number of ft_fileinfo structures */
                           /* in output buffer                 */
  
  /*************************************************************/
  /*               Local Functions Declarations                */
  /*************************************************************/
  static void error_print(struct ft_err *, char *);
  static void printinfo(struct ft_fileinfo *info); 
  
  int main (int argc, char *argv[])
  {
      int i;                     /* Some local counter         */
      int count;                 /* Number of files            */
      char remotesys[201];       /* Address of remote systems  */
      char remoteadm[68];        /* Transfer admission         */
      char fud[STAT_FUD_LEN];    /* Further details            */
      struct ft_err errorinfo;   /* Error information          */
      struct ft_admission admis; /* Admission parameters       */
      struct ft_shwpar par;      /* Show directory parameters  */
      struct ft_options opt;     /* Options                    */
      struct ft_fileinfo buf[BUFSIZE]; /* Output buffer        */
  
      /* Check program arguments                               */
      if (argc != 2)
      {
          printf("Call: %s &lt;directory&gt;\n", argv[0]);
          exit(EXIT_FAILURE);
      }
  
      /* Initialize ft_transpar structure                      */
      memset(&amp;par, 0, sizeof(par));
      /* Set version of the parameter list                     */
      par.shwparvers  = FT_SPARV2;
      /* Set the name of the remote directory                  */
      par.fn = argv[1];
      /* Set buffer and buffer length for the further details  */
      par.fud = fud;               
      par.fudlen = sizeof(fud);      
      /* Set version of the ft_fileinfo structure in the       */
      /* output buffer. The version in the first structure     */
      /* is valid for all structtures in the output buffer.    */
      buf[0].ftshowivers = FT_SHOWIV2;
  
      /* Get the name/address of the remote system and the     */
      /* transfer admission                                    */
      printf("Enter name of remote system: ");
      scanf("%s", remotesys);
      printf("Input transfer admission to remote system: ");
      scanf("%s", remoteadm);
      admis.remsys     = remotesys;
      admis.remadmis   = remoteadm;
      admis.remaccount = NULL;
      admis.rempasswd  = NULL;
  
      /* Prepare the options structure                         */
      opt.ftoptsvers = FT_OPTSV1;
      opt.ftapivers = FT_APIV2; 
  
      /* Read the contents of the remote directory             */
      count = ft_showdir(&amp;admis, &amp;par, buf, BUFSIZE, &amp;errorinfo,
                         &amp;opt);
      if (count == -1) 
      {   
          /* Error                                             */
          error_print(&amp;errorinfo, par.fud); 
      }
  
      /* Display result of request                             */
      printf("There are %d entries in remote directory %s\n",
             count, argv[1]);
  
      /* If the output range was not large enough for all      */
      /* the information, only the data in the buffer is       */
      /* displayed                                             */
      if (count &gt; BUFSIZE)
          count = BUFSIZE;
      for (i = 0; i &lt; count; i++)
           printinfo(&amp;buf[i]);
      return(0);
  }
  </code></pre>
  
  <p>Can someone help me achieve the same in C#? And even explain why it have to be that way, so we all can learn something from this :)</p>
  
  <p>Thanks!</p>