PInvoke char * в C DLL обрабатывается как String в С#. Ошибка с нулевыми символами

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

int my_Funct(char* input, char* output);

Я должен назвать это из приложения С#. Я делаю это следующим образом:

...DllImport stuff...
public static extern int my_Funct(string input, string output);

Строка ввода прекрасно передается в DLL (у меня есть видимое доказательство этого). Выход, который функция заполняет, хотя и ошибается. У меня есть гекса данных, например:

3F-D9-00-01

Но, к сожалению, все, что после двух нулей разрезается, и только первые два байта приходят в мое приложение С#. Это случается, потому что (я думаю) он обрабатывается как нулевой символ и принимает его как конец строки.

Любая идея, как я могу избавиться от нее? Я пытался указать его как IntPtr вместо строки, но я не знаю, что с ним делать потом. Я попытался сделать следующее:

 byte[] b1 = new byte[2];
 Marshal.Copy(output,b1,0,2);

2 обычно должна быть длиной массива байтов. Но я получаю всевозможные ошибки: например, "Запрошенный диапазон простирается за конец массива". или "Попытка чтения или записи защищенной памяти..."

Я ценю любую помощь.

Ответ 1

Ваш маршаллинг выходной строки неверен. Использование string в объявлении p/invoke целесообразно при передаче данных от управляемого к собственному. Но вы не можете использовать это, когда данные перемещаются в другом направлении. Вместо этого вам нужно использовать StringBuilder. Как это:

[DllImport(...)]
public static extern int my_Funct(string input, StringBuilder output);

Затем выделите память для вывода:

StringBuilder output = new StringBuilder(256);
//256 is the capacity in characters - only you know how large a buffer is needed

И тогда вы можете вызвать функцию.

int retval = my_Funct(inputStr, output);
string outputStr = output.ToString();

С другой стороны, если эти параметры содержат нулевые символы, вы не можете использовать маршал как строку. Это потому, что маршаллер не будет ничего маршалировать после нуля. Вместо этого вам нужно упорядочить его как массив байтов.

public static extern int my_Funct(
    [In] byte[] input, 
    [Out] byte[] output
);

Это соответствует вашей декларации C

Затем, предполагая кодировку ANSI, вы конвертируете входную строку в байтовый массив следующим образом:

byte[] input = Encoding.Default.GetBytes(inputString);

Если вы хотите использовать другую кодировку, очевидно, как это сделать.

И для вывода вам нужно выделить массив. Предполагая, что это та же длина, что и для ввода, вы сделаете это:

byte[] output = new byte[input.Length];

И как-то ваша функция C узнала длину массивов. Я оставлю это вам!

Затем вы можете вызвать функцию

int retval = my_Funct(input, output);

А затем, чтобы преобразовать выходной массив обратно в строку С#, вы снова используете класс Encoding.

string outputString = Encoding.Default.GetString(output);