interop - Generic PInvoke in C# -
i'm interfacing c api has few functions of following form:
int get_info(/* stuff */, size_t in_size, void* value, size_t *out_size);
this well-known c idiom return bunch of data of different types single function: in_size
parameter contains size of buffer passed value
, , actual number of bytes written out written through out_size
pointer, caller can read data in value
buffer.
i'm trying call these kinds of functions c# nicely (i.e. without making overload each different type , having call them individually, butt-ugly , introduces lot of duplicate code). naturally tried this:
[dllimport(dllname, entrypoint = "get_info")] int getinfo<t>(/* stuff */, uintptr insize, out t value, out uintptr outsize);
to amazement, compiled , worked in mono. unfortunately, hopes slashed vs not wanting compile it, and, indeed, generic methods forbidden dllimport targets. looking for. tried few things:
dynamically generating appropriate pinvoke target based on generic type using reflection: absolutely atrocious, , error-prone (also loses of benefits offered automatic marshaling)
having 1 overload type (
getinfoint
,getinfostr
, ..): gets out of controlhaving generic method taking pointer using
gchandle.alloc
, passing basicgetinfo
takesintptr
: works great, needs special handling enums because regrettably aren't blittable (yes, knowgetinfo<[underlying enum type]>
, cast enum, kind of defeats purpose because can't invoke generic method runtime-determined type without reflection) , strings need special code
so thought maybe having 3 overloads, being getinfoblittable<t>
, getinfoenum<t>
, , getinfostr
best compromise between code duplication , reflection voodoo. but, there better way? there nicer way close possible first getinfo<t>
snippet? ideally without needing switch on types. help!
fwiw, in total need make work int
, long
, uint
, ulong
, string
, string[]
, uintptr
, uintptr[]
, enum types, blittable structs, , possibly string[][]
.
- when use structs such
int
,long
, ..., can usemarshal.sizeof
size ,new intptr(&gchandle.alloc(...))
. - when use enums can use
.getenumunderlyingtype()
original type in it, , value it's field namedvalue__
reflection , usegetvalue
on enum object , receive it. - when use string can make array out of , give it's pointer.
i made test, understand it:
internal class program { public unsafe static int getinfo(intptr t,uintptr size) { if(size.touint32( ) == 4) console.writeline( *( int* )t.topointer( ) ); else //is our string? console.writeline( new string( ( char* )t.topointer( ) ) ); return 1; } public static unsafe int managedgetinfo<t>(t t) { if (t.gettype().isenum) { var handle = gchandle.alloc( t.gettype( ).getfield( "value__" ).getvalue( t ), gchandletype.pinned ); var result = getinfo( handle.addrofpinnedobject( ), new uintptr( (uint)marshal.sizeof( t.gettype().getenumunderlyingtype() ) ) ); handle.free( ); return result; } else if (t.gettype().isvaluetype) { var handle = gchandle.alloc( t, gchandletype.pinned ); var result = getinfo( handle.addrofpinnedobject( ), new uintptr( ( uint )marshal.sizeof( t ) ) ); handle.free( ); return result; } else if (t string) { var str = t string; var arr = ( str + "\0" ).toarray( ); fixed (char *ptr = &arr[0]) { return getinfo( new intptr( ptr ), new uintptr( ( uint )( arr.length * marshal.sizeof( typeof(char) ) ) ) ); } } return -1; } enum { x,y,z } private static void main( ) { string str = "1234"; int = 1234; a = a.y; console.writeline( "should print: " + str ); managedgetinfo( str ); console.writeline( "should print: " + ); managedgetinfo( ); console.writeline( "should print: " + ( int )a ); managedgetinfo( ); } }
which outputs:
should print: 1234 1234 should print: 1234 1234 should print: 1 1
note: need enable unsafe code @ project's properties test it.
to make arrays give hints:
- to make array out of
valuetype
such int, long , etc. need similar string's delivery method. - to make array out of
string
need multi-allocations , bit of dirty work. (the code quite native)
Comments
Post a Comment