Click here to Skip to main content
Click here to Skip to main content

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

By , 11 Feb 2012
 
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:
[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 –
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    delegate void ProgressCallback(int value);
  2. Define callback signature in the C code –
    typedef void (__stdcall * ProgressCallback)(int);
  3. Change DoWork signature in C code to accept ProgressCallback address:
    DLL void DoWork(long progressCallbackAddress)
    Note: DLL is…
    #define DLL __declspec(dllexport)
  4. Inside the DoWork code, convert this address to its respective function pointer –
    ProgressCallback progressCallback = (ProgressCallback)progressCallbackAddress;
    And then use it as a normal function. For instance…
    progressCallback(10);
  5. Inside the C# code, we need to create a delegate of type of the unmanaged delegate –
    ProgressCallback callback =
        (value) =>
        {
            Console.WriteLine("Progress = {0}", value);
        };
  6. Then for calling DoWork, we need to do it like this –
    DoWork(Marshal.GetFunctionPointerForDelegate(callback).ToInt64());
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
#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(long progressCallbackAddress);
    DLL void ProcessFile(long getFilePathPointer);
 
#ifdef __cplusplus
}
#endif
 
Engine.dll/Main.c
#include "Main.h"
#include <stdio.h>

DLL void DoWork(long progressCallbackAddress)
{
    // get access to ProgressCallback function pointer
    ProgressCallback progressCallback = (ProgressCallback)progressCallbackAddress;
    int counter = 0;
 
    for(; counter<=100; counter++)
    {
        // do the work...

        if (progressCallback)
        {
            // send progress update
            progressCallback(counter);
        }
    }
}
 
DLL void ProcessFile(long getFilePathPointer)
{
    // get access to GetFilePathCallback function pointer
    GetFilePathCallback getPath = (GetFilePathCallback)getFilePathPointer;
 
    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
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 :)

License

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

About the Author

Tecfield
Software Developer (Senior)
United States United States
Member
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.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionThreaded callbacks [modified]memberDaedalusAlpha4 Feb '13 - 2:34 
GeneralMy vote of 5memberTheMahakhef16 Jan '13 - 1:58 
GeneralMy vote of 5membermYashodhar19 Jul '12 - 18:01 
QuestionMoving pointer from C/C++ dll to C# callbackmemberkobbi_k4 Jul '12 - 2:33 
GeneralRe: Moving pointer from C/C++ dll to C# callbackmemberTecfield6 Jul '12 - 6:04 
AnswerRe: Moving pointer from C/C++ dll to C# callbackmemberTecfield6 Jul '12 - 6:10 
GeneralMy vote of 5memberShmuel Zang20 Jun '12 - 10:12 
GeneralMy vote of 5memberjuan_tabares00130 May '12 - 20:23 
QuestionCongratulations, so easy and simplememberMiguel Correia Lima7 May '12 - 7:34 
GeneralReason for my vote of 5 nice articlemembersan123pune26 Feb '12 - 8:39 
GeneralNice...memberjaffar Ramnad13 Feb '12 - 21:00 
GeneralReason for my vote of 5 good explanation, straight forward! ...memberEmiel Duivenvoorden31 Jan '12 - 5:30 
GeneralReason for my vote of 5 ...Once upon a time I needed such fu...memberkosmoh24 Jan '12 - 20:12 
General[UnmanagedFunctionPointer(CallingConvention.StdCall)] de...memberSelvin23 Jan '12 - 22:49 
GeneralRe: Thanks for the update :) I modified it to reflect your sugge...memberTecfield24 Jan '12 - 3:04 
QuestionNice !memberraananv31 Jan '12 - 11:27 
AnswerRe: Nice !memberTecfield31 Jan '12 - 16:19 

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130513.1 | Last Updated 11 Feb 2012
Article Copyright 2012 by Tecfield
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid