• [翻译]:怎样从C/C++代码中对C#进行回调


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

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

  • 相关阅读:
    密钥学习
    MAP的计算方法(简单总结)
    模型量化技术(入门级理解,不涉及复杂公式和深入的原理)
    实现java非阻塞http请求的两种方式
    PIP安装软件报错:“ERROR: Could not install packages due to an EnvironmentError: HTTPSConnectionPool(host='files.pythonhosted.org', port=443)”
    Fiddler弱网测试
    Fiddler断点应用
    Fiddler基本介绍
    Fiddler安装及证书配置教程(Windows)
    URL统一资源定位符
  • 原文地址:https://www.cnblogs.com/passedbylove/p/6082220.html
Copyright © 2020-2023  润新知