0

I'm trying to pass a writable (pre-allocated) string array from C# to a C++ dll. It fails with "Access violation writing location".

C++:

int StringArrayTest(size_t numberOfStrings, char **valueOut, size_t maxStringLength) {
    for (unsigned int i = 0; i < numberOfStrings; i++) {
        auto str = std::to_string(i); //Create simple string
        strncpy(valueOut[i], str.c_str(), maxStringLength); //Copy to output
    } 
    return 0;
}

C#:

[DllImport("MyDLL", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 
static extern int StringArrayTest(ulong arraySize, [MarshalAs(UnmanagedType.LPArray)]StringBuilder[] valuesOut, ulong maxStringLength);

public string[] GetTestStrings(ulong arraySize, ulong maxStringLength) {
    var stringBuilder = new StringBuilder[(int)arraySize];
    for (var i = 0; i < (int)arraySize; i++) {
        stringBuilder[i] = new StringBuilder((int)maxStringLength);
    }
    var result = StringArrayTest(arraySize, stringBuilder, maxStringLength);
    var returnValues = new string[arraySize];
    for (var i = 0; i < (int)arraySize; i++) {
        returnValues[i] = result.ToString();
    }
    return returnValues;
}

Note that doing this with a single string (char * signature and passing single StringBuilder) works as intended.

2
  • I don't think the marshaler will marshal everything for you. You are going to need to find an alternative approach Commented Nov 1, 2019 at 15:26
  • If you want to alter the original array you have to pass a pointer to the array char*** valueOut. The caller probabably needs to pass a reference (ref string[] valuesOut). Moreover I don't think ulong maps to size_t use uint. Commented Nov 25, 2019 at 15:11

1 Answer 1

1

C++

extern "C" {
  __declspec(dllexport) int StringArrayTest(size_t numberOfStrings, char*** ptrValueOut, size_t maxStringLength)
  {
    char** valueOut = *ptrValueOut;
    for (unsigned int i = 0; i < numberOfStrings; i++) {
      auto str = std::to_string(i); //Create simple string
      strncpy(valueOut[i], str.c_str(), maxStringLength); //Copy to output
    }
    return 0;
  }
}

Changed the declaration from ... char** valueOut, ... to ... char*** ptrValueOut, ...

C#:

[DllImport("StringArrayTest", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern int StringArrayTest(int arraySize, ref IntPtr valuesOut, int maxStringLength);

public static string[] GetTestStrings(int arraySize, int maxStringLength)
{
    var buffer = Marshal.AllocCoTaskMem(IntPtr.Size * arraySize);
    var strings = new IntPtr[arraySize];
    for (int i = 0; i < strings.Length; i++)
        strings[i] = Marshal.AllocCoTaskMem(maxStringLength);
    Marshal.Copy(strings, 0, buffer, strings.Length);            

    StringArrayTest(arraySize, ref buffer, maxStringLength);

    var returnValues = new string[arraySize];
    for (var i = 0; i < arraySize; i++)
    {
        returnValues[i] = Marshal.PtrToStringAnsi(strings[i]);
        Marshal.FreeCoTaskMem(strings[i]);
    }
    Marshal.FreeCoTaskMem(buffer);
    return returnValues;
}

The code is intendet to illustrate the procedure and probably not free of errors. Memory gets allocated via Marshal methods. The pointer to the memory is than passed as a reference to the StringArrayTest function.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks! One small fix, you can change the first line to var buffer = Marshal.AllocCoTaskMem(IntPtr.Size * arraySize); and the strings array to long[arraySize] (as well as casting the result of the next AllocCoTaskMem to long) to make it work on 32 and 64 bit.
Stupid me! There's no need to cast IntPtr to int since Marshal.Copy has a method overloading which takes an IntPtr[]. @TylerLewis You're not exactly right though. Because sizeof(long) is always 8 even on 32bit builds. But it doesn't matter with the updated solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.