Маршал массив строк из кода C # в C с помощью p / invoke

Мне нужно передать массив строк C # в код C

Пример кода C

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 #, который я пробовал (это не сработало)

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);

Сбой с System.AccessViolationException
System.AccessViolationException: попытка чтения или записи в защищенную память. Это часто указывает на то, что другая память повреждена.

Я тоже пытался (это тоже не сработало)

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);

Это также терпит неудачу с System.AccessViolationException
System.AccessViolationException: попытка чтения или записи в защищенную память. Это часто указывает на то, что другая память повреждена.

Кто-нибудь знает, как передать массив строк из C # в C?

Редактировать:
Добавлены сообщения об ошибках по предложению от Дэвида Хеффернама.
Измените size_t на int в коде C, поскольку это не влияет на то, что я пытался сделать.
Все равно получите те же ошибки.

2

Решение

Вы можете просто объявить свою функцию в C # следующим образом:

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

Как написано, ваш код C ++, по-видимому, использует cdecl Соглашение о вызовах. Поэтому вам может потребоваться сделать объявление C # соответствующим. Я подозреваю, что это главная проблема, с которой вы сталкиваетесь.

Обратите внимание, что size_t, который вы используете для length Параметр имеет ширину 32 бита в 32-битном процессе и ширину 64 бита в 64-битном процессе. Таким образом, правильный тип C # IntPtr, Лично я бы объявил это int в коде C ++ и C #.


Одно заключительное слово совета. При возникновении сбоев включайте сообщения об ошибках в свои вопросы. Я подозреваю, что ваш первый код не удался с этой ошибкой:

Вызов функции PInvoke ‘MyApp! MyApp.Program :: print_string_array’ разбалансирует стек.

И если бы вы включили это в вопрос, это было бы очень полезно.

4

Другие решения

Ваша вторая попытка довольно близка. Попробуйте следующее:

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);
2

Почему «print_string_array (IntPtr sa, int length);» вместо «print_string_array (IntPtr [] sa, int length);» ?
А что print_string_array смотрит на C-сторону?
print_string_array (char ** sa, int length); ?

0
По вопросам рекламы [email protected]