Search code examples
c#c++stringmarshallingdllimport

Marshal char[][LENGTH] in c#


In a C# project I have difficulties to connect a C++ function coming from an external library. I guess this is a problem of string marshalling.

In C++ there is the following function:

int compute(const char names[][LENGTH_1], char values[][LENGTH_2], const int n);

The goal is to provide:

  • an read-only array containing "n" strings of LENGTH_1 characters
  • an writable array containing "n" strings of LENGTH_2 characters

The function "compute" will write in the array "values" according to what is specified in "names".

In tried in C# two different ways to connect the function

Method 1

[DllImport("abcd.dll", EntryPoint="compute", CharSet=CharSet.Ansi)]
internal static extern int Compute(StringBuilder [] names, StringBuilder [] values, int n);

I call it this way:

var names = new StringBuilder[number];
var descriptions = new StringBuilder[number];
for (int i = 0; i < number; i++) {
  names[i] = new StringBuilder(LENGTH_1);
  descriptions[i] = new StringBuilder(LENGTH_2);
}
var error = Compute(names, descriptions, number);

Method 2

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=4)]
internal struct StringName
{
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst=64)] // LENGTH_1 = 64
  public string msg;
}

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=4)]
internal struct StringValue
{
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)] // LENGTH_2 = 128
  public string msg;
}

[DllImport("abcd.dll", EntryPoint="compute", CharSet=CharSet.Ansi)]
internal static extern int Compute(StringName[] names, ref StringValue[] values, int number);

I call it this way:

var names = new StringNames[number];
var values = new StringValue[number];
var error = Compute(names, ref values, number);

Result

It crashes with no exceptions, the program is blocked on the function "Compute". I'm still unsure if the problem comes from the strings or from the external library.


Solution

  • As found by David, the keyword "ref" was wrong. Here is the changes with method 2.

    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
    internal struct StringName
    {
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst=LENGTH_1)]
      public string msg;
    }
    
    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
    internal struct StringValue
    {
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst=LENGTH_2)]
      public string msg;
    }
    
    [DllImport("abcd.dll", EntryPoint="compute", CharSet=CharSet.Ansi)]
    internal static extern int Compute([In] StringName[] names, [In, Out] StringValue[] values, int number);
    

    I call it this way:

    var names = new StringNames[number];
    // ... here the initialization of "names" ... //
    var values = new StringValue[number];
    var error = Compute(names, values, number);
    // ... here the processing of "values" ... //