聲明:網絡上類似的中文博客大有存在,本人知識水平有限,業余愛好,也是為了備份收藏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