Вызов C API, состоящий из параметра указателя буфера, с использованием C#

Я пытаюсь вызвать API C Nvidia Omniverse из моего приложения на базе .netcore.

Это API для загрузки файла. Договор вы можете найти здесь. https://docs.omniverse.nvidia.com/kit/docs/client_library/latest/_build/docs/client_library/latest/function_group__file_1gab649e472ef2238a89c4213357b1d5598.html#exhale-function-group-file-1gab649e472ef2238a8 9c4213357b1d5598

У меня недостаточно опыта работы с C, поэтому я не могу вызвать этот API.

Мои данные поступают с другого сервера как HttpContent.

Я уже написал этот код, но мое приложение аварийно завершает работу при обращении к API omniClientWriteFileEx. Подход 1:

public class test
{
    [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
    static extern ulong omniClientWriteFileEx(string url, OmniClientContent content, IntPtr userData, IntPtr callback, string message);

    public async Task UploadUsingCApiAsync(string url, HttpContent httpContent)
    {
        var data = await httpContent.ReadAsByteArrayAsync();
        var content = new OmniClientContent 
        { 
            Buffer = content, 
            Size = content.Length 
        };
        omniClientWriteFileEx(url, content, IntPtr.Zero, IntPtr.Zero,  "");
    }

    private struct OmniClientContent
    {
        public int Size;
        public Byte[] Buffer;
    }
}

Подход 2

public class test2
{
    [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
    static extern ulong omniClientWriteFileEx(string url, OmniClientContent content, IntPtr userData, IntPtr callback, string message);

    public async Task UploadUsingCApiAsync(string url, HttpContent httpContent)
    {
        var data = await httpContent.ReadAsByteArrayAsync();
        var content = await dssHttpContent.ReadAsByteArrayAsync();
        var buffer = Marshal.AllocHGlobal(content.Length);
        Marshal.Copy(content, 0, buffer, content.Length);
        var content = new OmniClientContent 
        { 
            Buffer = buffer, 
            Size = content.Length 
        };
        omniClientWriteFileEx(url, content, IntPtr.Zero, IntPtr.Zero,  "");
    }

    private struct OmniClientContent
    {
        public int Size;
        public IntPtr Buffer;
    }
}

Я могу вызывать другие API этой библиотеки, где типы данных являются примитивными, но этот использует буфер.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В документах говорится:

Эта функция берет на себя управление буфером контента и освобождает его, когда закончит работу с ним (что может произойти через некоторое время в будущем).

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

Сначала объявите все PInvoke следующим образом. Обратите внимание, что вам не хватает объявлений CharSet.Ansi и вам нужно определение обратного вызова.

Третье значение в OmniClientContent — это обратный вызов для освобождения буфера.

[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern ulong omniClientWriteFileEx(
    string url, OmniClientContent content, IntPtr userData,
    OmniClientWriteFileExCallback callback, string message);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void OmniClientWriteFileExCallback(
    IntPtr userData, OmniClientResult result, OmniClientWriteFileExInfo info)

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void FreeBufferCallback(IntPtr buffer);

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
class OmniClientWriteFileExInfo
{
    public string version;
    public string hash;
}

class OmniClientContent
{
    public IntPtr buffer;
    public IntPtr size;
    public FreeBufferCallback FreeBuffer = Marshal.FreeHGlobal;

    public OmniClientContent(byte[] array)
    {
        this.size = array.Length;
        this.buffer = Marshal.AllocHGlobal(this.size);
        Marshal.Copy(array, 0, this.buffer, this.size);
    }
}


enum OmniClientResult
{
     ValuesHere
};

Тогда вы можете сделать

public async Task UploadUsingCApiAsync(string url, HttpContent httpContent)
{
    var data = await httpContent.ReadAsByteArrayAsync();

    var content = new OmniClientContent(data);
    omniClientWriteFileEx(url, content, IntPtr.Zero, null,  "");
}

Большое спасибо за очень подробное описание. :) Я могу выполнить API после передачи делегата, чтобы освободить буфер.

S7H 28.02.2024 13:01

запуск этого кода в Linux приводит к ошибке сегментации при вызове функции обратного вызова. Даже если я передам ноль в качестве входных данных. Знаете, в чем может быть проблема?

S7H 12.04.2024 11:42

Похоже на несбалансированный стек. Дважды трижды проверьте все. Я пропустил Cdecl делегата. И убедитесь, что вы объявляете некоторые из них как class а не struct, чтобы получить указатели.

Charlieface 12.04.2024 11:49

Другие вопросы по теме