C#的Native Callback

有时候需要C#调用C/C++库,如果是异步调用必然涉及回调函数,本文记录一下C#如何调用C++以及如何处理Native的回调。首先生成Windows下的动态库 DLL可以参考: 《演练:创建和使用自己的动态链接库 (C++)》 ,由于是在Windows环境,所以没有做关于平台区分的宏定义。

Windows下生成动态库DLL,也就是导出操作:

 1#pragma once
 2
 3typedef int(__stdcall* func_notice)(int, int);
 4
 5extern "C" {
 6	__declspec(dllexport) void __stdcall my_print();
 7
 8	__declspec(dllexport) int __stdcall calc_add(int a, int b);
 9
10	__declspec(dllexport) void __stdcall setNotice(func_notice func);
11}

使用动态库DLL,也就是导入操作:

 1#pragma once
 2
 3typedef int(__stdcall* func_notice)(int, int);
 4
 5extern "C" {
 6	__declspec(dllimport) void __stdcall my_print();
 7
 8	__declspec(dllimport) int __stdcall calc_add(int a, int b);
 9
10	__declspec(dllimport) void __stdcall setNotice(func_notice func);
11}

func_notice 是一个函数指针,即是定义的回调函数。头文件定义了,具体实现如下(就简单实现一下):

 1#include <stdio.h>
 2#include "calc.h"
 3
 4func_notice call_back = NULL;
 5
 6void my_print() 
 7{
 8	printf("my_print exec..\n");
 9}
10
11int calc_add(int a, int b)
12{
13	printf("calc_add exec.. a = %d, b = %d\n", a, b);
14	if (call_back) {
15		printf("call_back exec..\n");
16		call_back(a + 10, b + 10);
17	}
18	return a + b;
19}
20
21void setNotice(func_notice func) 
22{
23	call_back = func;
24}

本地使用测试一下,测试没问题就通过C#来调用。

 1#include "calc.h"
 2#include <iostream>
 3
 4int my_func(int a, int b) {
 5    printf("my_func callback success! a = %d, b = %d\n", a, b);
 6    return 0;
 7}
 8
 9int main()
10{
11    setNotice(my_func);
12    my_print();
13
14    printf("calc_add -> %d", calc_add(50, 100));
15
16    return 0;
17}

C# 如何使用调用DLL(同样的DLL和exe放在同一个文件夹底下):

 1using System;
 2using System.Runtime.InteropServices;
 3
 4namespace CSCallTest
 5{
 6    class Program
 7    {
 8        // 定义回调方法
 9        public static int CallBackFunction(int a, int b)
10        {
11            Console.WriteLine("C++ call me, a = " + a + ", b = " + b);
12            return a + b;
13        }
14
15        static void Main(string[] args)
16        {
17            DllImportX.CallbackDel callback = CallBackFunction;
18            DllImportX.my_print();
19            DllImportX.setNotice(callback);
20            int calcAdd = DllImportX.calc_add(20, 90);
21            Console.WriteLine("calcAdd = " + calcAdd);
22        }
23    }
24
25
26    public class DllImportX
27    {
28        [DllImport("call_test.dll", EntryPoint = "my_print", 
29            CallingConvention = CallingConvention.StdCall)]
30        public static extern void my_print();
31
32
33        [DllImport("call_test.dll", EntryPoint = "calc_add",
34            CallingConvention = CallingConvention.StdCall)]
35        public static extern int calc_add(int a, int b);
36
37
38        [DllImport("call_test.dll", EntryPoint = "setNotice",
39            CallingConvention = CallingConvention.StdCall)]
40        public static extern void setNotice(CallbackDel callback);
41
42        // 定义委托
43        public delegate int CallbackDel(
44            [MarshalAs(UnmanagedType.I4)] int a,
45            [MarshalAs(UnmanagedType.I4)] int b
46        );
47    }
48}

UnmanagedType.XXX 对应表格,类型需要对应,参考地址 UnmanagedType Enum

参考资料:

1、 UnmanagedType Enum

2、 演练:创建和使用自己的动态链接库 (C++)