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.
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.
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);
[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
};
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, "");
}