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 {
')
NativeCode.cpp file
int __stdcall FuncInterface( __in int nStatus) { nStatus = 5; return 1; } int CreateInterface( __inout PSInterface pInterface) {
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:
PS: this method is only an alternative substitute for calling unmanaged code using P / Invoke.