PInvoke, передача данных вперед и назад

#c# #c #pinvoke #bytearray #dllimport

#c# #c #pinvoke #bytearray #dllimport

Вопрос:

Я пытаюсь использовать P / Invoke для вызова функции C из C #.

 [DllImport(PATH)]
public static extern int PQunescapeByteaWrapper(
    ref byte[] src,
    ref byte[] dst);
 

Соответствующая функция C выглядит следующим образом:

 extern DECLSPEC int PQunescapeByteaWrapper(unsigned char* src, unsigned char* dst)
{
size_t dst_len;
dst = PQunescapeBytea(src, amp;dst_len);
return ((int)dst_len);
}
 

И вызов C#:

 PQfun.PQunescapeByteaWrapper(ref EscapedMessage, ref UnescapedMessage);
 

Отладка в C Я вижу, что «src» передается правильно, а также вычисляется «летнее время», но когда я возвращаюсь к C #, массив байтов [] «dst» содержит не значение массива символов без знака «dst», а исходное значение перед C P/ Вызвать!!
Как я могу передать вычисленное значение?

С уважением, Стефан

Ответ №1:

Вы используете сигнатуру метода C , и реализация неверна. Вы присваиваете параметру новый адрес. Вы должны использовать указатель на указатель, например

 extern DECLSPEC int PQunescapeByteaWrapper(unsigned char* src, unsigned char** dst)
{
    size_t dst_len;
    *dst = PQunescapeBytea(src, amp;dst_len);
    return ((int)dst_len);
}
 

Кстати, у вас здесь нет утечки памяти? вы намеревались перезаписать значения в существующем массиве, на который ссылается dst, или вы намеревались создать новый массив и назначить его dst?

Комментарии:

1. Я намеревался перезаписать существующий массив. Он ref byte[] dst уже выделен в моем коде на C #. Как вы думаете, где утечка памяти?

2. @luckymanStefanc — Вот почему я задал свой вопрос 😉 возможно, вы хотели выделить летнее время в своем методе C и вернуть его в C #, поскольку именно это делает реализация вашего метода C . Если вы хотите скопировать результат PUnescapeByteA в ваш предварительно выделенный буфер dst, вы должны это сделать. Смотрите Пример другого ответа для этого. Если вы хотите выделить новый массив на c и вернуть его на C #, см. Мой ответ

Ответ №2:

Я думаю, вам следует написать это так и выделить dst буфер снаружи в вашем коде на C #.

 int PQunescapeByteaWrapper(unsigned char* src, unsigned char* dst, size_t maxlen)
{
   size_t dst_len = 0;
   unsgigned char* tmp = PQunescapeBytea( src, amp;dst_len );
   memcpy( dst, tmp, min( maxlen, dst_len ));
}
 

Комментарии:

1. Спасибо, теперь это работает. Чтобы понять, в чем разница, если я использую временный массив, как вы предложили?

Ответ №3:

Насколько я вижу, есть несколько проблем.

1) C # использует __stdcall для вызова функций C. Поэтому вы должны добавить атрибут [DllImport(PATH, CallingConvention=CallingConvention .Cdecl)] или укажите атрибут __stdcall для вашей функции C.

2) Если вам нужно передать массив, вам не нужно ключевое слово «ref».

 [DllImport(PATH)]
public static extern int MyFunction(byte[] src);

extern DECLSPEC int __stdcall MyFunction(unsigned char* src);
 

3) Вы не можете использовать в C # массив, выделенный из C .
Вам нужно скопировать его в управляемую память (массив C #).
Для этого вы можете выполнить две функции, например.
Тот, который подсчитывает, сколько символов вам нужно для вашего нового массива.
Еще один, который выполняет преобразование в целевом массиве.

Итак, вы можете сделать что-то вроде этого:

 public static byte[] MyConvert(byte[] myArray)
{
    // Function defined in your C/C   dll that compute how much space you need.
    int count = CountRequiredChars(myArray);

    byte[] myNewArray = new byte[count];

    // Function defined in your C/C   dll that writes into myNewArray the output.
    PerformMyConversion(myArray, myNewArray);

    return myNewArray;
}
 

PerformMyConversion не должен возвращать новый массив, он должен копировать содержимое преобразования в выходной параметр.