Click here to Skip to main content
16,008,010 members
Articles / Programming Languages / C#
Tip/Trick

How to make a callback to C# from C/C++ code

Rate me:
Please Sign up or sign in to vote.
4.96/5 (46 votes)
26 Jun 2013CPOL1 min read 235.3K   89   41
This post shows how to make a callback to C# from C/C++
Almost everyone knows how to make a call to a function in an unmanaged DLL. However, sometimes we wish that we could call C# code from C/C++ code.
Imagine a scenario wherein we have a C# application which has a native C DLL called Engine.dll. There is a function entry named “DoWork” in this DLL that we need to call. Calling DoWork in the engine is as easy as making the following declaration in the C# code:
C#
[DllImport("Engine.dll")]
public static extern void DoWork(); 
…and then using it like any other static C# method in our C# application.
 
This will work just fine. However, let’s assume DoWork is a long-running task and we want to show a progress or so in the C# app in order to keep our user(s) updated. To make this happen, we need to…
  1. Define an unmanaged delegate in the C# code like –
    C#
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    delegate void ProgressCallback(int value);
  2. Define callback signature in the C code –
    C++
    typedef void (__stdcall * ProgressCallback)(int);
  3. Change DoWork signature in C code to accept ProgressCallback address:
    C++
    DLL void DoWork(ProgressCallback progressCallback)
    Note: DLL is…
    C++
    #define DLL __declspec(dllexport)
  4. Inside the C# code, we need to create a delegate of type of the unmanaged delegate –
    C#
    ProgressCallback callback =
        (value) =>
        {
            Console.WriteLine("Progress = {0}", value);
        };
  5. Then for calling DoWork, we need to do it like this –
    C#
    DoWork(callback);
Here is a sample source code for a simple application. This code snippet includes a second scenario wherein we have a function in C code called ProcessFile that needs to get back to the C# in order to obtain a file path for further processing - in this case, printing its contents to the console.
 
Engine.dll/Main.h
C++
#include "Windows.h"

#ifdef __cplusplus
extern "C"
{
#endif
 
    #define DLL __declspec(dllexport)
    typedef void (__stdcall * ProgressCallback)(int);
    typedef char* (__stdcall * GetFilePathCallback)(char* filter);
 
    DLL void DoWork(ProgressCallback progressCallback);
    DLL void ProcessFile(GetFilePathCallback getPath);
 
#ifdef __cplusplus
}
#endif
 
Engine.dll/Main.c
C++
#include "Main.h"
#include <stdio.h>

DLL void DoWork(ProgressCallback progressCallback)
{
    int counter = 0;
 
    for(; counter<=100; counter++)
    {
        // do the work...

        if (progressCallback)
        {
            // send progress update
            progressCallback(counter);
        }
    }
}
 
DLL void ProcessFile(GetFilePathCallback getPath)
{
 
    if (getPath)
    {
        // get file path...
        char* path = getPath("Text Files|*.txt");
        // open the file for reading
        FILE *file = fopen(path, "r");
        // read buffer
        char line[1024];
 
        // print file info to the screen
        printf("File path: %s\n", path ? path : "N/A");
        printf("File content:\n");
 
        while(fgets(line, 1024, file) != NULL)
        {
            printf("%s", line);
        }
 
        // close the file
        fclose(file);
    }
}
 
TestApp.exe/Program.cs
C#
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
 
class Program
{
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    delegate void ProgressCallback(int value);
 
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    delegate string GetFilePathCallback(string filter);
 
    [DllImport("Engine.dll")]
    public static extern void DoWork([MarshalAs(UnmanagedType.FunctionPtr)] ProgressCallback callbackPointer);
 
    [DllImport("Engine.dll")]
    public static extern void ProcessFile([MarshalAs(UnmanagedType.FunctionPtr)] GetFilePathCallback callbackPointer);
 
    [STAThread]
    static void Main(string[] args)
    {
        // define a progress callback delegate
        ProgressCallback callback =
            (value) =>
            {
                Console.WriteLine("Progress = {0}", value);
            };
 
        Console.WriteLine("Press any key to run DoWork....");
        Console.ReadKey(true);
        // call DoWork in C code
        DoWork(callback);
 
        Console.WriteLine();
        Console.WriteLine("Press any key to run ProcessFile....");
        Console.ReadKey(true);
 
        // define a get file path callback delegate
        GetFilePathCallback getPath =
            (filter) =>
            {
                string path = default(string);
 
                OpenFileDialog ofd =
                    new OpenFileDialog()
                {
                    Filter = filter
                };
 
                if (ofd.ShowDialog() == DialogResult.OK)
                {
                    path = ofd.FileName;
                }
 
                return path;
            };
 
        // call ProcessFile in C code
        ProcessFile(getPath);
    }
}
 
Enjoy it Smile

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
United States United States
I got my BS in Software Engineering from Iran, worked there for 4.5 years mainly in industrial automation field. Then I moved to Australia. In Australia, I had a great chance to work at some big companies. Since 2009 I have been living in the States. I received my MS in Information Systems from Illinois State University. Currently, I am a Senior Software Development Engineer.

Comments and Discussions

 
Questiondeclare parameter as ProgressCallback instead of long? Pin
Oleg Vazhnev20-Jun-13 4:24
Oleg Vazhnev20-Jun-13 4:24 
AnswerRe: declare parameter as ProgressCallback instead of long? Pin
Tecfield21-Jun-13 3:42
Tecfield21-Jun-13 3:42 
QuestionThreaded callbacks Pin
DaedalusAlpha4-Feb-13 2:34
DaedalusAlpha4-Feb-13 2:34 
AnswerRe: Threaded callbacks Pin
Tecfield18-Jul-13 19:10
Tecfield18-Jul-13 19:10 
GeneralMy vote of 5 Pin
TheMahakhef16-Jan-13 1:58
TheMahakhef16-Jan-13 1:58 
GeneralMy vote of 5 Pin
mYashodhar19-Jul-12 18:01
mYashodhar19-Jul-12 18:01 
QuestionMoving pointer from C/C++ dll to C# callback Pin
kobbi_k4-Jul-12 2:33
kobbi_k4-Jul-12 2:33 
GeneralRe: Moving pointer from C/C++ dll to C# callback Pin
Tecfield6-Jul-12 6:04
Tecfield6-Jul-12 6:04 
That should be pretty straightforward. You need to define the very same structure in your C/C++ code and C# - the order of the items is very important. The trick is that you need to make sure that both versions have the same padding. If you are unfamiliar with padding, then check on that on the internet because otherwise you will receive unexpected results. Long story short, C/C++ pad structures in order to increase performance. In order to comply with C/C++ structures, C# has some attributes to make you able to apply the very same padding to you structures in case you need to communicate with unmanaged world. Once you got the padding right in both sides, you should be able to send any structures back and forth between C# and C/C++ dll with no problem.
Let me know if you needed more clarification on this.
Maybe I, Maybe U, can make a change to the world!

GeneralRe: Moving pointer from C/C++ dll to C# callback Pin
Oleg Vazhnev15-May-13 0:27
Oleg Vazhnev15-May-13 0:27 
GeneralRe: Moving pointer from C/C++ dll to C# callback Pin
Tecfield15-May-13 2:26
Tecfield15-May-13 2:26 
AnswerRe: Moving pointer from C/C++ dll to C# callback Pin
Tecfield6-Jul-12 6:10
Tecfield6-Jul-12 6:10 
GeneralMy vote of 5 Pin
Shmuel Zang20-Jun-12 10:12
Shmuel Zang20-Jun-12 10:12 
GeneralMy vote of 5 Pin
juan_tabares00130-May-12 20:23
juan_tabares00130-May-12 20:23 
QuestionCongratulations, so easy and simple Pin
Miguel Correia Lima7-May-12 7:34
professionalMiguel Correia Lima7-May-12 7:34 
GeneralReason for my vote of 5 nice article Pin
san123pune26-Feb-12 8:39
san123pune26-Feb-12 8:39 
GeneralNice... Pin
Jaffar Ramnad13-Feb-12 21:00
Jaffar Ramnad13-Feb-12 21:00 
GeneralReason for my vote of 5 good explanation, straight forward! ... Pin
Emiel Duivenvoorden31-Jan-12 5:30
Emiel Duivenvoorden31-Jan-12 5:30 
GeneralReason for my vote of 5 ...Once upon a time I needed such fu... Pin
kosmoh24-Jan-12 20:12
kosmoh24-Jan-12 20:12 
General[UnmanagedFunctionPointer(CallingConvention.StdCall)] de... Pin
Selvin23-Jan-12 22:49
Selvin23-Jan-12 22:49 
GeneralRe: Thanks for the update :) I modified it to reflect your sugge... Pin
Tecfield24-Jan-12 3:04
Tecfield24-Jan-12 3:04 
QuestionNice ! Pin
raananv31-Jan-12 11:27
raananv31-Jan-12 11:27 
AnswerRe: Nice ! Pin
Tecfield31-Jan-12 16:19
Tecfield31-Jan-12 16:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.