Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Invoking C API consisting of buffer pointer parameter using C#

I am trying to invoke a C API of Nvidia Omniverse from my .netcore based application.

It is an API to upload a file. You can find the contract here.
https://docs.omniverse.nvidia.com/kit/docs/client_library/latest/_build/docs/client_library/latest/function_group__file_1gab649e472ef2238a89c4213357b1d5598.html#exhale-function-group-file-1gab649e472ef2238a89c4213357b1d5598

I don’t have enough experience with C so I am not able to invoke this API.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

My data is coming from another server as HttpContent.

I have written this code so far but my application crashes when omniClientWriteFileEx API is hit.
Approach 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;
    }
}

Approach 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;
    }
}

I am able to invoke other APIs of this library where datatypes as primitive but this one involves a buffer.

>Solution :

The docs say:

This function takes ownership of the content buffer, and frees it when it’s finished with it (which may be some time in the future).

So you can’t just give it an array, because the marshaller will unpin it after the end of the call. You need to create your own buffer, and marshal in the data yourself.

First, declare all the PInvoke as follows. Note that you are missing CharSet.Ansi declarations, and you need the callback definition.

The third value in OmniClientContent is a callback to free the buffer.

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

delegate 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
};

Then you can do

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

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

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading