Преобразование заголовков C в C # - ByValArray и ByValTStr для фиксированного массива Char внутри структуры - PullRequest
0 голосов
/ 13 февраля 2019

У меня есть структура, определенная в C как:

typedef struct {
    char struct_id[4];
    int struct_version;
    int keepAliveInterval;
    ……
} MQTTClient_connectOptions

Я создаю соответствующую структуру в C # следующим образом:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MQTTClient_connectOptions {
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
    public string struct_id;
    public int struct_version;
    public int keepAliveInterval;
    ……
}

Эта структура C # - это то, что генерируется P/ Invoke Interop Assistant, и это фрагмент кода C #, который рекомендуется несколькими публикациями в моем поиске Google.

Тот же DLL / исходный код, который определяет структуру C, также определяет функцию:

DLLExport  int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options);

Что я определил в своем коде C # как:

[DllImport("PahoMqttC", EntryPoint = "MQTTClient_connect", CharSet = CharSet.Ansi)]
public static extern int MQTTClient_connect(IntPtr handle, ref MQTTClient_connectOptions options);

В своем коде C # я могу установить

MQTTClient_connectOptions.struct_id = "MQTC"

, и когда я проверяю объект во время отладки, я вижу эти4 символа в этом поле.Тем не менее, когда я использую эту структуру для вызова MQTTClient_connect (), «MQTC» усекается до «MQT».

Когда я шагаю по коду, как только я шаг в MQTTClient_connect, поле struct_id изменяется с «MQTC» на «MQT \ 0» в инспекторе объектов C #, и MQTTClient_connect завершается ошибкой, потому что struct_id не соответствует ожидаемому.

Если я вместо этого определяю структуру в C #, как это:

 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] struct_id;

и установите его значение следующим образом:

struct_id = Encoding.ASCII.GetBytes("MQTC");

тогда все работает правильно ???

Моя цель - понять Marshaling и P / Invoke ипреобразование заголовков C / C ++ в код C #, и поэтому мне бы очень хотелось знать:

1 - Почему использование «byte []» работает при использовании «string», вызывает изменение значения struct_id при I войти в подпрограмму MQTTClient_connect ()?

2 - Есть ли способ определить структуру C # с помощью «строки», которая упростит остальную часть моего кода C #?

Спасибо!

1 Ответ

0 голосов
/ 13 февраля 2019

Причина этого в том, что маршаллер p / invoke должен решить, является ли поле char[4] текстовым или двоичным (т. Е. Байтами).И здесь действует соглашение, согласно которому string маршаллируется как UnmanagedType.ByValTStr как текст.В этом случае он может иметь переменную длину, которая определяется нулевым терминатором, который должен присутствовать.Это соглашение, которое соответствует типичному использованию массивов фиксированной длины char для хранения C-строк.Строки Си заканчиваются нулем.

В действительности, я подозреваю, что ваши данные на самом деле не текстовые.Я подозреваю, что поле будет лучше объявлено в C как unsigned char struct_id[4], чтобы указать, что оно содержит 4 байта.Все это немного субъективно, и, конечно, мы не принимаем во внимание все обоснования дизайна библиотеки.Возможно, есть веская причина объявить его массивом char, который я не вижу.

Независимо от того, что ваш код C # не может использовать string для этого поля.A byte[] с SizeConst = 4 является правильным способом упорядочить это.Некоторые вспомогательные методы для вашего типа записи могут помочь преобразовать байтовый массив в строку.Однако мне интересно, сколько разных идентификаторов вы встретите.Возможно, вам просто нужно объявить несколько констант байтового массива, и вы можете использовать их вместо строковых литералов.

...