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 control

  • having generic method taking pointer using gchandle.alloc , passing basic getinfo takes intptr: works great, needs special handling enums because regrettably aren't blittable (yes, know getinfo<[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[][].

  1. when use structs such int, long, ..., can use marshal.sizeof size , new intptr(&gchandle.alloc(...)).
  2. when use enums can use .getenumunderlyingtype() original type in it, , value it's field named value__ reflection , use getvalue on enum object , receive it.
  3. 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:

  1. to make array out of valuetype such int, long , etc. need similar string's delivery method.
  2. to make array out of string need multi-allocations , bit of dirty work. (the code quite native)

Comments

Popular posts from this blog

c++ - QTextObjectInterface with Qml TextEdit (QQuickTextEdit) -

javascript - angular ng-required radio button not toggling required off in firefox 33, OK in chrome -

xcode - Swift Playground - Files are not readable -