I'm interfacing with a C API that has a few functions of the following form:
int get_info(/* stuff */, size_t in_size, void* value, size_t *out_size);
This is a well-known C idiom to return a bunch of data of different types from a single function: the in_size
parameter contains the size of the buffer passed into value
, and the actual number of bytes written out is written through the out_size
pointer, the caller can then read back its data in the value
buffer.
I'm trying to call these kinds of functions from C# nicely (i.e. without making an overload for each different type and having to call them individually, which is butt-ugly and introduces a lot of duplicate code). So I naturally tried something like this:
[DllImport(DllName, EntryPoint = "get_info")]
int GetInfo<T>(/* stuff */, UIntPtr inSize, out T value, out UIntPtr outSize);
To my amazement, this compiled and worked perfectly in Mono. Unfortunately, my hopes were quickly slashed by VS not wanting to compile it, and, indeed, generic methods are forbidden as DllImport targets. But this is basically what I am looking for. I tried a few things:
dynamically generating an appropriate PInvoke target based on a generic type using reflection: absolutely atrocious, and very error-prone (also loses all of the benefits offered by automatic marshaling)
having one overload by type (
GetInfoInt
,GetInfoStr
, ..): quickly gets out of controlhaving a generic method taking a pointer using
GCHandle.Alloc
and passing that into a basicGetInfo
which takes anIntPtr
: works great, but needs special handling for enums because they regrettably aren't blittable (yes, I know I could simplyGetInfo<[underlying enum type]>
and cast to the enum, but that kind of defeats the purpose because you can't invoke a generic method with a runtime-determined type without reflection) and strings also need special code
So I eventually thought that maybe having exactly three overloads, being GetInfoBlittable<T>
, GetInfoEnum<T>
, and GetInfoStr
would be the best compromise between code duplication and reflection voodoo. But, is there a better way? Is there a nicer way to get as close as possible to the first GetInfo<T>
snippet? Ideally without needing to switch over types. Thanks for your help!
FWIW, in total I would need to make this work with int
, long
, uint
, ulong
, string
, string[]
, UIntPtr
, UIntPtr[]
, enum types, blittable structs, and possibly string[][]
.
int
,long
, ..., you can useMarshal.SizeOf
to get the size andnew IntPtr(&GCHandle.Alloc(...))
..GetEnumUnderlyingType()
to get the original type in it, and to get the value get it's field namedvalue__
by reflection and useGetValue
on the enum object and you will receive it.I made a test, so you could understand it:
Which outputs:
NOTE: You will need to enable unsafe code at your project's properties to test it.
To make arrays I will give you hints:
ValueType
such as int, long and etc. You need to do something similar to string's delivery method.string
you will need to do multi-allocations and a bit of dirty work. (The code will look quite native)