问题
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?
Edit: Added error messages per suggestion from David Heffernam. Change size_t to int in C code since it did not affect what I was trying to do. Still get the same errors.
回答1:
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.
回答2:
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);
回答3:
Why the "print_string_array(IntPtr sa, int length);" instead of "print_string_array(IntPtr[] sa, int length);" ? And what does print_string_array look at the C-side? print_string_array(char** sa, int length); ?
来源:https://stackoverflow.com/questions/13317931/marshal-an-array-of-strings-from-c-sharp-to-c-code-using-p-invoke