3

I need to pass an array of C# strings into C code

Example C code

void print_string_array(const char** str_array, int length){
    for (int i = 0; i < length; ++i) {
        printf("Sting[%l] = %s\n", i, str_array[i]);
    }
}

C# that I have tried (This did not work)

string foo[] = {"testing", "one", "two", "three"};  
print_string_array(foo, foo.Length);

[DllImport(my_C_dll, CharSet = CharSet.Ansi)]
private static extern void print_string_array([In][MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr)] string[] sa, int length);

Fails with a System.AccessViolationException System.AccessViolationException : Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

I have also tried (This also did not work)

string[] foo = {"testing", "one", "two", "three"};  
IntPtr[] s_array = new IntPtr[foo.Length];

for(int i = 0; i < foo.Length; ++i) 
{
    s_array[i] = Marshal.StringToCoTaskMemAnsi(foo[i])
}
print_string_array( s_array, s_array.Length);

[DllImport(my_C_dll, CharSet = CharSet.Ansi)]
private static extern void print_string_array(IntPtr[] sa, int length);

This also fails with a

System.AccessViolationException
System.AccessViolationException : Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Any one know how to pass an array of strings from c# to C?


Update: Added error messages per suggestion from David Heffernan. Change size_t to int in C code since it did not affect what I was trying to do. Still get the same errors.

2 Answers 2

10

You can simply declare your function in C# like this:

[DllImport(my_C_dll, CallingConvention=CallingConvention.Cdecl)]
static extern void print_string_array([In] string[] str_array, IntPtr length);

As it is written, your C++ code would appear to use the cdecl calling convention. So you may need to make the C# declaration match. I suspect that is the main problem you are facing.

Note also that size_t, which you use for the length parameter is 32 bits wide in a 32 bit process, and 64 bits wide in a 64 bit process. So the correct C# type is IntPtr. Personally I'd declare it to be int in both the C++ and C# code.


One final word of advice. When you encounter failures, include the error messages in your questions. I suspect your first code failed with this error:

A call to PInvoke function 'MyApp!MyApp.Program::print_string_array' has unbalanced the stack.

And if you had included that in the question it would have been a great help.

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

1 Comment

My original code still fails; however, I started a clean test project and the code worked the first try using this method. Thanks.
1

Your second attempt is quite close. Try the following:

string[] foo = {"testing", "one", "two", "three"};  
IntPtr[] s_array = new IntPtr[foo.Length];

for(int i = 0; i < foo.Length; ++i) 
{
    s_array[i] = Marshal.StringToHGlobalAnsi(foo[i])
}
GCHandle gH = GCHandle.Alloc(s_array, GCHandleType.Pinned);
print_string_array( gH.AddrOfPinnedObject(), s_array.Length);
gH.Free();
for(int i = 0; i < foo.Length; ++i) 
{
    Marshal.FreeHGlobal(s_array[i])
}

[DllImport(my_C_dll, CharSet = CharSet.Ansi)]
private static extern int print_string_array(IntPtr sa, int length);

1 Comment

This is actually the same as the code in the question. Arrays are already pinned by the marshaller.

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.