声明:网络上类似的中文博客大有存在,本人知识水平有限,业余爱好,也是为了备份收藏How to make a callback to C# from C/C++ code
本着共享知识的初衷,翻译一份给大家参考,为了便于阅读不至于拗口,没有按照原文直译,不到之处或者翻译有误,还望勿喷,敬请指评。
几乎每个人都知道怎样调用一个非托管DLL中的函数,然而有时候我们希望能从C/C++代码中调用C#代码。
想象一个场景,其中有一个名为Engine.dll的本机C语言编写DLL的C#应用程序。在DLL中有一个名为“DoWork”
的函数入口点,我需要对它进行调用。在Engine.dll中调用"DoWork"就像在C#代码中做以下声明一样简单。
[DllImport("Engine.dll")] public static extern void DoWork();
这段代码运行将非常良好,然而,让我再假设一下DoWork是一个连续性运行的任务,为了保证我们的用户端可被更新,
我们希望显示一个进度。想要实现这一点,我们需要做出以下几步:
1.在C#代码中定义一个类似的非托管代码委托
[UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate void ProgressCallback(int value);
2.在C代码中定义一个回调签名
typedef void (__stdcall * ProgressCallback)(int);
3.在C代码中更改DoWork的签名以便接收ProgressCallback的地址
DLL void DoWork(ProgressCallback progressCallback)
注意:DLL宏的声明是这样的
#define DLL __declspec(dllexport)
4.在C#代码中,我们需要创建一个非托管委托类型的委托
ProgressCallback callback = (value) => { Console.WriteLine("Progress = {0}", value); };
5.然后为了调用DoWork,我们需要这样做
DoWork(callback);
这里有一个简单应用程序的示例源码.这个代码段包含其他代码套方案,其中有一个名为ProcessFile函数的C代码需要回调到C#,以便获得文件
路径进行用于进一步处理 - 当前情形下将打印文件的内容到控制台。
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 ", path ? path : "N/A"); printf("File content: "); 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); } }
以下附上本人编译的代码,和原文有点出入,主要是因为本人习惯用.NET 2.0,还有一些是为了编译期间顺利通过编译器。
代码使用Visual Studio 2010+VC6.0编写
下载地址:1.怎样从C_C++代码中对C#进行回调.rar