声明:网络上类似的中文博客大有存在,本人知识水平有限,业余爱好,也是为了备份收藏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#代码中做以下声明一样简单。

  1. [DllImport("Engine.dll")]
  2. public static extern void DoWork();

这段代码运行将非常良好,然而,让我再假设一下DoWork是一个连续性运行的任务,为了保证我们的用户端可被更新,
我们希望显示一个进度。想要实现这一点,我们需要做出以下几步:

1.在C#代码中定义一个类似的非托管代码委托

  1. [UnmanagedFunctionPointer(CallingConvention.StdCall)]
  2. public delegate void ProgressCallback(int value);

2.在C代码中定义一个回调签名

  1. typedef void (__stdcall * ProgressCallback)(int);

3.在C代码中更改DoWork的签名以便接收ProgressCallback的地址

  1. DLL void DoWork(ProgressCallback progressCallback)

注意:DLL宏的声明是这样的

  1. #define DLL __declspec(dllexport)

4.在C#代码中,我们需要创建一个非托管委托类型的委托

  1. ProgressCallback callback =
  2. (value) =>
  3. {
  4. Console.WriteLine("Progress = {0}", value);
  5. };

5.然后为了调用DoWork,我们需要这样做

  1. DoWork(callback);

这里有一个简单应用程序的示例源码.这个代码段包含其他代码套方案,其中有一个名为ProcessFile函数的C代码需要回调到C#,以便获得文件
路径进行用于进一步处理 - 当前情形下将打印文件的内容到控制台。

Engine.dll/Main.h

  1. #include "Windows.h"
  2.  
  3. #ifdef __cplusplus
  4. extern "C"
  5. {
  6. #endif
  7.  
  8. #define DLL __declspec(dllexport)
  9. typedef void (__stdcall * ProgressCallback)(int);
  10. typedef char* (__stdcall * GetFilePathCallback)(char* filter);
  11.  
  12. DLL void DoWork(ProgressCallback progressCallback);
  13. DLL void ProcessFile(GetFilePathCallback getPath);
  14.  
  15. #ifdef __cplusplus
  16. }
  17. #endif

Engine.dll/Main.c

  1. #include "Main.h"
  2. #include <stdio.h>
  3.  
  4. DLL void DoWork(ProgressCallback progressCallback)
  5. {
  6. int counter = ;
  7.  
  8. for(; counter<=; counter++)
  9. {
  10. // do the work...
  11.  
  12. if (progressCallback)
  13. {
  14. // send progress update
  15. progressCallback(counter);
  16. }
  17. }
  18. }
  19.  
  20. DLL void ProcessFile(GetFilePathCallback getPath)
  21. {
  22.  
  23. if (getPath)
  24. {
  25. // get file path...
  26. char* path = getPath("Text Files|*.txt");
  27. // open the file for reading
  28. FILE *file = fopen(path, "r");
  29. // read buffer
  30. char line[];
  31.  
  32. // print file info to the screen
  33. printf("File path: %s\n", path ? path : "N/A");
  34. printf("File content:\n");
  35.  
  36. while(fgets(line, , file) != NULL)
  37. {
  38. printf("%s", line);
  39. }
  40.  
  41. // close the file
  42. fclose(file);
  43. }
  44. }

TestApp.exe/Program.cs

  1. using System;
  2. using System.Runtime.InteropServices;
  3. using System.Windows.Forms;
  4.  
  5. class Program
  6. {
  7. [UnmanagedFunctionPointer(CallingConvention.StdCall)]
  8. delegate void ProgressCallback(int value);
  9.  
  10. [UnmanagedFunctionPointer(CallingConvention.StdCall)]
  11. delegate string GetFilePathCallback(string filter);
  12.  
  13. [DllImport("Engine.dll")]
  14. public static extern void DoWork([MarshalAs(UnmanagedType.FunctionPtr)] ProgressCallback callbackPointer);
  15.  
  16. [DllImport("Engine.dll")]
  17. public static extern void ProcessFile([MarshalAs(UnmanagedType.FunctionPtr)] GetFilePathCallback callbackPointer);
  18.  
  19. [STAThread]
  20. static void Main(string[] args)
  21. {
  22. // define a progress callback delegate
  23. ProgressCallback callback =
  24. (value) =>
  25. {
  26. Console.WriteLine("Progress = {0}", value);
  27. };
  28.  
  29. Console.WriteLine("Press any key to run DoWork....");
  30. Console.ReadKey(true);
  31. // call DoWork in C code
  32. DoWork(callback);
  33.  
  34. Console.WriteLine();
  35. Console.WriteLine("Press any key to run ProcessFile....");
  36. Console.ReadKey(true);
  37.  
  38. // define a get file path callback delegate
  39. GetFilePathCallback getPath =
  40. (filter) =>
  41. {
  42. string path = default(string);
  43.  
  44. OpenFileDialog ofd =
  45. new OpenFileDialog()
  46. {
  47. Filter = filter
  48. };
  49.  
  50. if (ofd.ShowDialog() == DialogResult.OK)
  51. {
  52. path = ofd.FileName;
  53. }
  54.  
  55. return path;
  56. };
  57.  
  58. // call ProcessFile in C code
  59. ProcessFile(getPath);
  60. }
  61. }

以下附上本人编译的代码,和原文有点出入,主要是因为本人习惯用.NET 2.0,还有一些是为了编译期间顺利通过编译器。

代码使用Visual Studio 2010+VC6.0编写

下载地址:1.怎样从C_C++代码中对C#进行回调.rar

2.怎样从C_Cpp代码中对CSharp进行回调2.rar

[翻译]:怎样从C/C++代码中对C#进行回调的更多相关文章

  1. 检查.net代码中占用高内存函数(翻译)

    哈哈,昨天没事做,在CodeProject瞎逛,偶然看到这篇文章,居然读得懂,于是就翻译了一下,当练习英语,同时增强对文章的理解,发现再次翻译对于文章的一些细节问题又有更好的理解.下面是翻译内容,虽然 ...

  2. 在后台代码中动态生成pivot项并设置EventTrigger和Action的绑定

    最近在做今日头条WP的过程中,遇到需要动态生成Pivot项的问题.第一个版本是把几个频道写死在xaml里了,事件绑定也写在xaml里,每个频道绑定一个ObservableCollection<A ...

  3. 要心中有“数”——C语言初学者代码中的常见错误与瑕疵(8)

    在 C语言初学者代码中的常见错误与瑕疵(7) 中,我给出的重构代码中存在BUG.这个BUG是在飞鸟_Asuka网友指出“是不是时间复杂度比较大”,并说他“第一眼看到我就想把它当成一个数学问题来做”之后 ...

  4. 【Win 10 应用开发】在代码中加载文本资源

    记得前一次,老周给大伙,不,小伙伴们介绍了如何填写 .resw 文件,并且在 XAML 中使用 x:Uid 标记来加载.也顺便给大伙儿分析了运行时是如何解析 .resw 文件的. 本来说好了,后续老周 ...

  5. Salesforce 自定义标签在代码中的应用

    自定义标签简介 Salesforce 中自定义标签(Custom Label)的作用是存储一般性的文本,可以用于 Apex.Visualforce 页面.Lightning 组件等地方,用于显示提示信 ...

  6. [翻译] 如何在 ASP.Net Core 中使用 Consul 来存储配置

    [翻译] 如何在 ASP.Net Core 中使用 Consul 来存储配置 原文: USING CONSUL FOR STORING THE CONFIGURATION IN ASP.NET COR ...

  7. 18.翻译系列:EF 6 Code-First 中的Seed Data(种子数据或原始测试数据)【EF 6 Code-First系列】

    原文链接:https://www.entityframeworktutorial.net/code-first/seed-database-in-code-first.aspx EF 6 Code-F ...

  8. 11.翻译系列:在EF 6中配置一对零或者一对一的关系【EF 6 Code-First系列】

    原文链接:https://www.entityframeworktutorial.net/code-first/configure-one-to-one-relationship-in-code-fi ...

  9. 【翻译】TCP backlog在Linux中的工作原理

    原文How TCP backlog works in Linux水平有限,难免有错,欢迎指出!以下为翻译: 当应用程序通过系统调用listen将一个套接字(socket)置为LISTEN状态时,需要为 ...

随机推荐

  1. java后台调用url无协议

    url格式不正确,可能有"www.baidu.com"    "这个不能有 // 下载pdf public void downpdf(String URL, String ...

  2. Openstack Day1简介及虚拟环境搭建

    本文章仅作为作者本人存档记忆!恕不详细展开内容!   openstack kilo版本重要组件(module)简介 ======================================= Ke ...

  3. 基于纹理的图片检索及demo(未启动)

    基于纹理的图片检索及demo(未启动)

  4. Windows程序设再读笔记01-起步

    1.从程序员角度看,统一的界面意味着编程人员可以使用windows自带的例程来构建许多的功能,例如菜单,对话框等.只用几行代码就可以实现很多复杂的功能.但是这同时也增加了一些限制,使得做出一个个性化的 ...

  5. for..in遍历,枚举器

    #pragma mark ------------for循环遍历集合中的元素------ //创建一个数组,包含5个字符串对象,倒序取出数组中的所有元素,并存储到另一可变数组中 NSArray *ar ...

  6. andriod前端传来经度 纬度 坐标 来查询数据库坐标周围500M内的类数据

    @Transient public static List<Article> queryByPosition(PositionInfo pinfo){ //System.out.print ...

  7. JAVAC 命令详解(转)

    本文来自:http://www.cnblogs.com/JeffChen/archive/2008/01/16/1041783.html 结构 javac [ options ] [ sourcefi ...

  8. C++:名字查找先于类型检查

    Sub-Title: Name Hiding. "In C++, there is no overloading across scopes - derived class scopes a ...

  9. 一个C#语法高亮插件

    语法高亮对程序员阅读代码来说有着不小的帮助,虽然VisualStudio本身支持C#语法高亮,但也只是对关键字.类名.字符串等少数元素加了标记,而我们代码中主题:变量.函数.属性.事件等都没有进行高亮 ...

  10. javaScript 正则表达式匹配日期

    // yyyyMMddhhmmss var pattern = /^(?:(?!0000)[0-9]{4}(?:(?:0[1-9]|1[0-2])(?:0[1-9]|1[0-9]|2[0-8])|(? ...