Providing Managed Methods as Callback for Unmanaged

Providing Managed Methods as Callback for Unmanaged

In a P/Invoke interaction, the initiative must come from the managed code’s side. The process starts in managed mode and makes calls to the unmanaged functions. However, the exchange can’t always go in only one direction; that model would be too simplistic to be usable.

Many unmanaged methods require callback functions, and the managed code must have the means to provide those functions. Thus, it’s necessary to have a way to pass a managed method pointer to an unmanaged function, permitting the unmanaged function to call the managed method. The managed method in question might be simply a P/Invoke thunk of another unmanaged method, but that changes nothing—it’s still a managed method.

The way to pass managed methods as callbacks to unmanaged functions involves the use of delegates. The delegates are marshaled by P/Invoke thunks as unmanaged function pointers, which makes them suitable for the task.

Let’s look at a sample to review the way delegates are used for callback specifications. You can find this sample, Callback.il, on the companion CD. The sample implements a simple program that sorts 15 integer values in ascending order, employing the well-known C function qsort, called through P/Invoke. The difference between the P/Invoke calls you’ve encountered so far and this one is that qsort requires a callback function, which performs the comparison of two elements of the array being sorted, thus defining the sorting order.

Let the sample speak for itself:

// Necessary preliminaries .assembly extern mscorlib {} .assembly callback { } .module callback.exe // Here is the unsorted data we want to sort. With minor  // improvements, I could get the data from a file or the console, // but it would not serve any illustrative purpose. // Feel free to try modifying the code to include this feature. .class private value explicit sealed SixtyBytes { .pack .size 60 } .field public static valuetype SixtyBytes DataToSort at D_0001 .data D_0001 = {int32(10), int32(32), int32(-1), int32(567),    int32(3), int32(18), int32(1), int32(-51), int32(789), int32(2345),    int32(-43), int32(788), int32(-345), int32(345), int32(0)} // To show that the sorting really happens, I am going to print // out data before and after the sorting. Rather than using the // [mscorlib]System.Console::WriteLine, I will use P/Invoke   // to call the C function int printf(char* Format,...). .method public hidebysig static pinvokeimpl("msvcrt.dll" ansi cdecl)     vararg int32 printf(stringpreservesig {} // And this is a managed printing method, invoking printf. .method public static void printInt32(void* pBuff, int32 N) {    .locals init(int32 i, void* pb)    // i = 1;    ldc.i4.1    stloc.0    // pb = pBuff;    ldarg.0    stloc.1 Next:   // if(i > N) goto Return;    ldloc.0    ldarg.1    bgt Return    // printf("%2.2d : %d\n",i,*(int*)pb);    ldstr "%2.2d : %d\n"    ldloc.0    ldloc.1    ldind.i4    call vararg int32 printf(string,...,int32,int32)    pop    // i += 1;    ldloc.0    ldc.i4.1    add    stloc.0    // pb += 4; In C, this would be an illegal operation because     // pb is void*, and the absolute pointer increment could not be     // calculated. In IL, however, the pointer increment is always    // absolute.    ldloc.1    ldc.i4.4    add    stloc.1    br Next  Return:    ret} // I can't pass the managed method pointer to the unmanaged function, // and even the ldftn instruction will not help me. // This delegate will serve as an appropriate vehicle. .class public sealed CompareDelegate        extends [mscorlib]System.MulticastDelegate {    .method public hidebysig specialname             instance void  .ctor(object Object,                                 native unsigned int MethodPtr)                                  runtime managed {}    // Note the modopt modifier of the Invoke signature -it's very    // important. Without it, the calling convention of the callback    // function is marshaled as stdcall (callee cleans the stack).     // But qsort expects the callback function to have the cdecl    // calling convention (caller clears the stack). If we supply the    // callback with the stdcall calling convention, qsort blows     // the stack away and causes a memory access violation. You are    // welcome to comment out the modopt line and see what happens.    // Note also that the modopt modifier is placed on the delegate's    // Invoke signature, not on the signature of the delegated method.    .method public hidebysig virtual instance int32        modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl)              Invoke(void*, void*) runtime managed {}    // Well, I don't really need asynchronous invocation here,     // but, you know, dura lex sed lex.    .method public hidebysig newslot virtual             instance class [mscorlib]System.IAsyncResult                BeginInvoke(object,                           class [mscorlib]System.AsyncCallback,                           objectruntime managed {}    .method public hidebysig newslot virtual instance            void  EndInvoke(class [mscorlib]System.IAsyncResult)                  runtime managed {} } // The hero of the occasion: the qsort function. .method public hidebysig static pinvokeimpl("msvcrt.dll" ansi cdecl)     void qsort(void*,int32,int32,class CompareDelegate) preservesig {} // This is the comparison method I'm going to offer as  // a callback to qsort. What can be simpler than comparing // two integers? .method public static int32 compInt32(void* arg1,void* arg2)  {    // return(*arg1 - *arg2);    ldarg.0    ldind.i4    ldarg.1    ldind.i4    sub    ret} // And now, let's put this show on the road. .method public static void Exec() {    .entrypoint    .locals init(class CompareDelegate)    // Print the unsorted values.    ldstr "Before Sorting:\n"    call vararg int32 printf(string)    pop    ldsflda valuetype SixtyBytes DataToSort     ldc.i4 15    call void printInt32(void*, int32)    // Create the delegate.     // Null object ref indicates the global method.    ldnull    ldftn int32 compInt32(void*,void*)    newobj instance void        CompareDelegate::.ctor(object,native unsigned int)    stloc.0    // Invoke qsort.    ldsflda valuetype SixtyBytes DataToSort // Pointer to data    ldc.i4 15   // Number of items to sort    ldc.i4 4   // Size of an individual item    ldloc.0   // Callback function pointer    call void qsort(void*,int32,int32,class CompareDelegate)    // Print the sorted values.    ldstr "After Sorting:\n"    call vararg int32 printf(string)    pop    ldsflda valuetype SixtyBytes DataToSort     ldc.i4 15    call void printInt32(void*, int32)    ret }



Inside Microsoft. NET IL Assembler
Inside Microsoft .NET IL Assembler
ISBN: 0735615470
EAN: 2147483647
Year: 2005
Pages: 147
Authors: SERGE LIDIN

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net