Структура PInvoke с вложенным массивом структур

Я пытаюсь PInvoke метод, который имеет параметр структуры с вложенным указателем массива структур. Объявление c выглядит следующим образом:

duckdb_state duckdb_query(duckdb_connection connection, const char *query, duckdb_result *out_result);

typedef struct {
    void *data;
    bool *nullmask;
    duckdb_type type;
    char *name;
} duckdb_column;

typedef struct {
    idx_t column_count;
    idx_t row_count;
    duckdb_column *columns;
    char *error_message;
} duckdb_result;

Я объявил их на C# следующим образом:

[DllImport("duckdb.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_query")]
public static extern DuckdbState DuckdbQuery(IntPtr connection, string query, out DuckdbResult result);

    [StructLayout(LayoutKind.Sequential)]
    public struct DuckdbColumn
    {
        IntPtr data;
        bool nullmask;  
        DuckdbType type;
        string name;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DuckdbResult
    {
        public long column_count;
        public long row_count;

        public IntPtr columns;
        public string error_message;
    }

Но когда я пытаюсь выполнить запрос и прочитать столбцы, я не получаю никаких значимых данных:

result = DuckdbQuery(connection, "SELECT * FROM integers", out queryResult);

DuckdbColumn[] columns = new DuckdbColumn[queryResult.column_count];
var queryResultColumns = queryResult.columns;
            
var columnPointer = Marshal.ReadIntPtr(queryResultColumns);
var ptrToStructure = (DuckdbColumn)Marshal.PtrToStructure(columnPointer, typeof(DuckdbColumn));

данные столбца

Как мне изменить объявления PInvoke, чтобы я мог читать столбцы после выполнения запроса?

Пример кода c находится по адресу: пример DuckDB c

Обновление 1

Я могу получить имена столбцов с помощью следующего кода:

for (int i = 0; i < queryResult.column_count; i++)
{
    var column = (DuckdbColumn)Marshal.PtrToStructure(queryResult.columns + 8 + (Marshal.SizeOf<DuckdbColumn>() + 8) * i, typeof(DuckdbColumn));
    columns[i] = column;
}

но в поле type по-прежнему написано DUCKDB_TYPE_INVALID

введите здесь описание изображения введите здесь описание изображения

Обновление 2

Как предложил Дэвид в своем ответе, я изменил bool nullmask; на IntPtr nullmask;, и теперь я могу читать информацию столбца следующим образом:

for (int i = 0; i < queryResult.column_count; i++)
{
    var column = (DuckdbColumn)Marshal.PtrToStructure(queryResult.columns + Marshal.SizeOf<DuckdbColumn>() * i, typeof(DuckdbColumn));
    columns[i] = column;
}

person Giorgi    schedule 13.10.2020    source источник
comment
Это вряд ли будет действительным указателем. Мне кажется код ошибки. С намеком type_invalid, что вам нужно интерпретировать это как ошибку.   -  person Hans Passant    schedule 13.10.2020
comment
Количество возвращаемых байтов равно количеству столбцов * количеству строк. Итак, все, что вам нужно сделать, это взять ptrToStructure и маршалировать в массив байтов. Затем перечислите массив байтов.   -  person jdweng    schedule 13.10.2020
comment
@HansPassant Я не знаю об этом указателе, но запрос выполняется успешно, и я возвращаю значения. Единственное, что не работает, это получение типа и имени столбца.   -  person Giorgi    schedule 13.10.2020
comment
@jdweng Я могу получить данные, используя другие функции: github .com/cwida/duckdb/blob/master/src/include/duckdb.h#L160 Единственное, что не работает, — это получение типа и имени столбца.   -  person Giorgi    schedule 13.10.2020
comment
Создайте перечисление С# для TYPE. Используйте перечисление github в ссылке вверху вашего вопроса и конвертируйте в С#. Размер перечисления по умолчанию в С# составляет 32 бита (должен быть того же размера, что и С++). С++ для строк заканчивается символом '\0', а размер равен одному байту. В С# символ — это класс. Поэтому измените тип со строки на byte[].   -  person jdweng    schedule 13.10.2020
comment
@jdweng У меня уже есть это перечисление. У DuckdbResult также есть char*, который я без проблем получаю в строке, когда я выдаю неправильный запрос   -  person Giorgi    schedule 13.10.2020
comment
@jdwengЯ могу поделиться демонстрационным проектом, если вам интересно   -  person Giorgi    schedule 13.10.2020
comment
Не понимаю вопроса. Недопустимый тип равен нулю, поэтому, возможно, размер неправильный и не равен 32 битам. Если тип имеет неправильный размер, тогда имя будет неправильным. Также я подозреваю, что когда строка работает, ее нет в опубликованной структуре. Вероятно, у вас есть LPRSTR, который преобразует byte[] в строку. Итак, вам нужно посмотреть на следующее: docs.microsoft.com/en-us/dotnet/framework/interop/   -  person jdweng    schedule 13.10.2020
comment
@jdweng Работающая строка находится в структуре DuckdbResult, которую я опубликовал.   -  person Giorgi    schedule 13.10.2020
comment
Сначала вам нужно заставить тип работать, потому что структура последовательная. Вы можете изменить размер перечисления следующим образом: enum DuckdbType: short   -  person jdweng    schedule 13.10.2020
comment
Давайте продолжим обсуждение в чате.   -  person Giorgi    schedule 13.10.2020
comment
Поделитесь демонстрационным проектом, будет проще.   -  person Simon Mourier    schedule 13.10.2020
comment
@SimonMourier Хотите продолжить в чате выше?   -  person Giorgi    schedule 13.10.2020
comment
@jdweng Я обновил свой вопрос своим прогрессом.   -  person Giorgi    schedule 13.10.2020
comment
@HansPassant Я обновил вопрос своим прогрессом   -  person Giorgi    schedule 13.10.2020
comment
Интересно!!! Имя начинается с 8-го байта каждой строки. Это означает, что dataptr (4 байта) + nullmask + type составляет 8 байтов (а не 12 байтов).   -  person jdweng    schedule 14.10.2020
comment
Ваш перевод char* полей какstring правильный   -  person David Heffernan    schedule 14.10.2020


Ответы (1)


Вы неправильно перевели это поле

bool *nullmask

Это не bool, это указатель. Объявите это как

IntPtr nullmask;

Могут быть и другие ошибки, потому что мы не можем видеть все переводы. Кроме того, +8 в арифметике указателя доступа к массиву выглядит подозрительно.

person David Heffernan    schedule 14.10.2020
comment
Спасибо, это сработало! Как получить логическое значение из IntPtr? - person Giorgi; 14.10.2020
comment
Поэтому я думаю, что нативный код — это C++, потому что bool — это тип C++. В C++ для Windows bool — это один байт. Это означает, что вы используете bool nullmask = Marshal.ReadByte(col.nullmask) <> 0. Я думаю! - person David Heffernan; 14.10.2020
comment
Да, вот код C++: github. com/cwida/duckdb/blob/master/src/main/duckdb-c.cpp#L129 - person Giorgi; 14.10.2020