[翻譯]:怎樣從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\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);
    }
}

以下附上本人編譯的代碼,和原文有點出入,主要是因為本人習慣用.NET 2.0,還有一些是為了編譯期間順利通過編譯器。

代碼使用Visual Studio 2010+VC6.0編寫

下載地址:1.怎樣從C_C++代碼中對C#進行回調.rar

            2.怎樣從C_Cpp代碼中對CSharp進行回調2.rar


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM