⬆️ ⬇️

Call unmanaged code from managed without P / Invoke

We continue to marshal. This time, the method of calling C-functions from C # without using P / Invoke ([DllImport]) will be considered. And if to be completely accurate, then [DllImport] will still have to be used, but only once. Essentially, this article is an article about marshaling delegates to pointers to functions and back.

So let's get started.



The first thing to start with is to remember that when we refuse something (in this case [DllImport]), we replace it with something else. So it is here. Rejecting P / Invoke (in C #) and export from Dll (in C), you have to declare a structure with pointers to a function (in C) and a structure with delegates (in C #).

Suppose you have a header file and an implementation file with a code of the following form (no matter where they come from or how they are received):



NativeCode.h file



/* ,    */ typedef int (__stdcall * pFuncInterface)( __in int nStatus ); typedef struct _Interface { //         pFuncInterface m_pfInterface; //      DWORD m_dwData; } SInterface, *PSInterface; /*    */ __declspec(dllexport) int CreateInterface( __inout PSInterface pInterface ); 


')

NativeCode.cpp file



 int __stdcall FuncInterface( __in int nStatus) { nStatus = 5; return 1; } /*   */ int CreateInterface( __inout PSInterface pInterface) { //       pInterface->m_pfInterface = FuncInterface; // -  pInterface->m_dwData = 5; return pInterface->m_dwData; } 




Note 1 : in this example, “ __stdcall ” is already being added by the reader to restore the stack state.

Note 2 : the number of function pointers declared as structure fields ( SInterface ) can be different, that is, the structure can contain an arbitrarily different number of function pointers.



As you can see from the example, we have a pointer ( pFuncInterface ) to the function ( FuncInterface ), which takes an int and returns an int , a structure with two fields ( SInterface ), one of which is a pointer to the function, and a function to fill the structure by fields ( CreateInterface ) .



Our task is to write interaction code with this structure in C #. The first thing that comes to mind is the export of all functions on the C side and calling them through [DllImport] on the C # side, and leave only data fields in the structure (exclude function pointers). But I don’t want to change the code (besides, if it’s not yours at all, it’s most likely not possible to change it simply).



In this case, proceed as follows.



As usual, we create a Win32 project (Console Application) and indicate that it will compile into a Dll, and also create a C # project - Console Application. In the Dll we add two of our source files and the function of filling the structure by fields is made exported (this is the only function for which you need to call P / Invoke). So, we have a Dll with only one exported function CreateInterface () .



The disadvantage of this method is that on the C # side you have to declare exactly the same structure, in which function pointers are replaced by delegates (you have to declare a delegate for each function), that is, on the C # side, in essence, duplication of the * .h code of the file ( in this case, NativeCode.h ). However, by doing this, as well as [DllImport] for CreateInterface () , we get:



 using System; using System.Runtime.InteropServices; namespace SharpCode { /****************************************************/ //    *.h  public delegate int pInterface(int nStatus); [StructLayout(LayoutKind.Sequential, Pack = 1)] struct SInterface { public pInterface m_pfpInterface; public UInt32 m_dwData; } /****************************************************/ class Program { private static SInterface stInterface = new SInterface(); [DllImport("NativeDll", CallingConvention = CallingConvention.Cdecl)] public static extern int CreateInterface( ref SInterface pInterface ); static void Main(string[] args) { CreateInterface(ref stInterface); //         //  unmanaged code   P/Invoke int nRes = stInterface.m_pfpInterface(1); Console.WriteLine("Result = {0}", nRes); } } } 




It now remains to add in two places " __stdcall " and voila, everything works. By calling CreateInterface () from the Dll, the SInterface structure is filled with function pointers (in this case, one), which on the C # side are marshaled to delegates. Now you can use the received delegate to call a function from unmanaged code directly, bypassing the P / Invoke mechanism.



The result of the program, as expected:



image



PS: this method is only an alternative substitute for calling unmanaged code using P / Invoke.

Source: https://habr.com/ru/post/131209/



All Articles