Calling managed code from unmanaged

This article will look at calling a managed C # code (.Net) from an unmanaged C code.
Once at work they gave a project, or rather not even the project itself, but only part of it. The project itself consisted of two parts: a functional written in C (unmanaged code) and an interface part written in C # (managed code). My task was to write an interface and associate it with functionality.

Later in this article, managed code will be called the top level, unmanaged - the bottom.

As you know, the P / Invoke (Platform Invoke) mechanism is used to access the lower level from the top in C #. To do this, the lower level is wrapped in Dll (that is, all functions of the lower level are made exported) and called from above using the DllImport attribute. For those who are unfamiliar with this mechanism -msdn.microsoft.com/en-us/library/aa288468 (v = vs.71) .aspx

During the course of the assignment, I had a problem - at the lower level was a callback function, which should notify the upper level of successful or unsuccessful completion functions. To solve the problem, it was necessary either to call the upper level from the lower level or to come up with some mechanism to find out the moment of calling the upper level function (for example, using an event). A search on the Internet for the topic of invoking managed code from unmanaged did not bear fruit. Nevertheless, it was decided to try to get by with little blood and not reinvent the wheel.

To simplify understanding, a new solution was created, including two projects: a low-level project (CallC) and a high-level project (SharpForCall).

So, we have an empty C # project (Console Application) and someone wrote a C project (I initially had of course only h-file, we take the project right away for simplicity). The type of project in C is Dll, which naturally should lie next to our executable received in C #. The project has a * .cpp file with the following contents:

/** Обратный уведомляющий вызов */
typedef 
VOID (__stdcall * pResultCallBack)(
	int nStatus
	);
__declspec(dllexport)
int FuncC(pResultCallBack pfResult, PVOID pContext)
{
	//
	// здесь что-то делаем
	//
	/* перед выходом из функции уведомляем высший уровень(C#) о результате */
	pfResult(1);
	return 1;
}


Once again I will explain the meaning of what needs to be done. The exported function here (FuncC) will be imported on the C # side, which, suppose, is called when a user presses a button (let's not forget that the main task is to interface the interface with the functional). This function (imported on the C # side) will naturally call the FuncC function in the given * .cpp file (see above), which after execution should report the result of execution back to C # by calling the pResultCallBack function. On the top-level side, the pResultCallBack function (in our case, FuncCSharp, see below) will analyze the result of the FuncC function and, depending on the value passed to it, perform certain actions (for example, when returning a status code indicating a failed call, you can repeat the call, etc. d.).

Let's get down to implementation.

First, go to the C-shnoy project settings in Configuration Properties-> General-> Output Directory and write the path to the folder with the C # project executable.

image

Secondly, do not forget to go into the Project Dependencies of the C # project and check the box next to the C-project.

image

Next, we create the Import.cs class, in which we describe the imported function using the P / Invoke mechanism.

using System;
using System.Runtime.InteropServices;// не забываем подключить
namespace SharpForCall
{
    class Import
    {
        [DllImport("CallC.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int FuncC(
            [MarshalAs(UnmanagedType.FunctionPtr)] ResultCallBack pfResult,
            IntPtr pContext);
    }
}


We replace the PVOID with IntPtr, and the pointer to the pResultCallBack function with the ResultCallBack delegate, which is described in the Program.cs file as follows:

using System;
namespace SharpForCall
{
    public delegate void ResultCallBack(Int32 nStatus);
    class Program
    {
        static void Main(string[] args)
        {
            ResultCallBack result = new ResultCallBack(FuncCSharp);
            IntPtr pContext = IntPtr.Zero;
            /* Вызов функции из Dll */
            int nRes = Import.FuncC(result, pContext);
        }
        /** Функция анализа обратного вызова.
         *  Вызывается из unmanaged кода.
         */
        public static void FuncCSharp(Int32 nStatus)
        {
            if ( 1 == nStatus )
            {
                //бла бла бла
            }
            if ( 2 == nStatus )
            {
                //бла бла бла
            }
            // и так далее
            return ;
        }
    }
}


Now, by launching the program and going through it in steps (in order to enter unmanaged code you need to set the checkbox in the project properties -> Debug-> Enable unmanaged code debugging), we will see that first the upper level calls the lower level, passing it to (the lower level ) the delegate, and the lower one - at the end of the FuncC function execution, calls the upper one (FuncCSharp function) using the same “delegate” and passes it the result of the function (in this case, “1”). Next, the function analyzes the received status code and returns control to the lower level, from where control is transferred to the upper level. If, when executed, the program issues an exit with the following contents:

image

then we add on the C side in the definition of the __stdcall callback.

If this does not help, then on the C # side in the Import class, you need to add CallingConvention = CallingConvention.Cdecl when calling the DllImport attribute. This is all necessary in order to return the stack to its original state.

Now everything works. We just accomplished what many consider impossible - we called the managed code from unmanaged.

PS: I think someone will come in handy. Waiting for your comments ...

Also popular now: