How to make a callback to C# from C/C++ code - CodeProject
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:
[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 progress or so in the C# app in order to keep our user(s) updated. To make this happen, we need to…
- Define an unmanaged delegate in the C# code like –
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate void ProgressCallback(int value);
- Define callback signature in the C code –
typedef void (__stdcall * ProgressCallback)(int);
- Change
DoWork
signature in C code to acceptProgressCallback
address:
DLL void DoWork(ProgressCallback progressCallback)
Note: DLL is…
#define DLL __declspec(dllexport)
- Inside the C# code, we need to create a delegate of type of the unmanaged delegate –
ProgressCallback callback =
(value) =>
{
Console.WriteLine("Progress = {0}", value);
};
- Then for calling
DoWork
, we need to do it like this –
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
#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
#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
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);
}
}
网友评论