Click here to Skip to main content
15,891,253 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 228.4K   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

 
QuestionIF the c++ project is a vc++ clr library how would it work Pin
Member 1616904823-Dec-23 6:48
Member 1616904823-Dec-23 6:48 
QuestionError in MS VS 2019 debug mode Pin
Jerry_Mouse6-May-21 4:17
Jerry_Mouse6-May-21 4:17 
Questionwaiting for delegate for returning the values from c++ Pin
Kerito Nation3-Mar-21 1:39
Kerito Nation3-Mar-21 1:39 
QuestionCan we call delegates from c++ whenever needed? Pin
Member 1468675226-Jul-20 18:59
Member 1468675226-Jul-20 18:59 
Questionhow to make DoWork call a non blocking. Pin
Member 1474271015-Feb-20 4:16
Member 1474271015-Feb-20 4:16 
QuestionCan this be done in reverse? Pin
Andrew Truckle7-May-19 21:18
professionalAndrew Truckle7-May-19 21:18 
QuestionOne simplification Pin
Michael Geary13-Feb-17 17:49
Michael Geary13-Feb-17 17:49 
QuestionArrays in Callbacks Pin
Ben99317-Jul-16 7:55
Ben99317-Jul-16 7:55 
QuestionCallback invoked in a separate thread Pin
Radu Tomy17-Feb-16 23:44
Radu Tomy17-Feb-16 23:44 
AnswerRe: Callback invoked in a separate thread Pin
Tecfield18-Feb-16 0:55
Tecfield18-Feb-16 0:55 
GeneralRe: Callback invoked in a separate thread Pin
Radu Tomy18-Feb-16 1:17
Radu Tomy18-Feb-16 1:17 
GeneralRe: Callback invoked in a separate thread Pin
Tecfield18-Feb-16 6:58
Tecfield18-Feb-16 6:58 
QuestionInconsistent accessibility Pin
Member 1144213210-Feb-15 5:48
Member 1144213210-Feb-15 5:48 
AnswerRe: Inconsistent accessibility Pin
Tecfield6-Mar-15 2:48
Tecfield6-Mar-15 2:48 
QuestionWorks Great! Pin
Tamok16-Dec-14 7:17
Tamok16-Dec-14 7:17 
GeneralThanks. Very informative and clear Pin
IssaharNoam11-Oct-14 8:28
IssaharNoam11-Oct-14 8:28 
QuestionExcellent and informative Pin
Master_Clinician31-Dec-13 14:27
Master_Clinician31-Dec-13 14:27 
GeneralMy vote of 5 Pin
2374124-Jun-13 2:49
2374124-Jun-13 2:49 
Generalnice Pin
colttme21-Jun-13 19:10
colttme21-Jun-13 19:10 
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 
Sorry Dear DaedalusAlpha I missed your question and sorry for belated answer.
Generally this was just a tip to give you the idea about how to do it on your own Smile | :)
Regarding your question, it goes back to MS UI App Architecture. When a UI object created, all updates to that control should be made from the very same thread they were created on. Any update from other threads needs to go through Control.Invoke channel. What it does is that it transfers the call to the thread on which control was created. If you don't do that, you may receive a runtime exception (and may not, it will be kind of random). That link you sent is not complete please check on this one for more details: http://msdn.microsoft.com/en-us/library/ms171728.aspx[^]
Maybe I, Maybe U, can make a change to the world!

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 

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.