C#的Native Callback

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

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

#pragma once

typedef int(__stdcall* func_notice)(int, int);

extern "C" {
	__declspec(dllexport) void __stdcall my_print();

	__declspec(dllexport) int __stdcall calc_add(int a, int b);

	__declspec(dllexport) void __stdcall setNotice(func_notice func);
}

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

#pragma once

typedef int(__stdcall* func_notice)(int, int);

extern "C" {
	__declspec(dllimport) void __stdcall my_print();

	__declspec(dllimport) int __stdcall calc_add(int a, int b);

	__declspec(dllimport) void __stdcall setNotice(func_notice func);
}

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

#include <stdio.h>
#include "calc.h"

func_notice call_back = NULL;

void my_print() 
{
	printf("my_print exec..\n");
}

int calc_add(int a, int b)
{
	printf("calc_add exec.. a = %d, b = %d\n", a, b);
	if (call_back) {
		printf("call_back exec..\n");
		call_back(a + 10, b + 10);
	}
	return a + b;
}

void setNotice(func_notice func) 
{
	call_back = func;
}

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

#include "calc.h"
#include <iostream>

int my_func(int a, int b) {
    printf("my_func callback success! a = %d, b = %d\n", a, b);
    return 0;
}

int main()
{
    setNotice(my_func);
    my_print();

    printf("calc_add -> %d", calc_add(50, 100));

    return 0;
}

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

using System;
using System.Runtime.InteropServices;

namespace CSCallTest
{
    class Program
    {
        // 定义回调方法
        public static int CallBackFunction(int a, int b)
        {
            Console.WriteLine("C++ call me, a = " + a + ", b = " + b);
            return a + b;
        }

        static void Main(string[] args)
        {
            DllImportX.CallbackDel callback = CallBackFunction;
            DllImportX.my_print();
            DllImportX.setNotice(callback);
            int calcAdd = DllImportX.calc_add(20, 90);
            Console.WriteLine("calcAdd = " + calcAdd);
        }
    }


    public class DllImportX
    {
        [DllImport("call_test.dll", EntryPoint = "my_print", 
            CallingConvention = CallingConvention.StdCall)]
        public static extern void my_print();


        [DllImport("call_test.dll", EntryPoint = "calc_add",
            CallingConvention = CallingConvention.StdCall)]
        public static extern int calc_add(int a, int b);


        [DllImport("call_test.dll", EntryPoint = "setNotice",
            CallingConvention = CallingConvention.StdCall)]
        public static extern void setNotice(CallbackDel callback);

        // 定义委托
        public delegate int CallbackDel(
            [MarshalAs(UnmanagedType.I4)] int a,
            [MarshalAs(UnmanagedType.I4)] int b
        );
    }
}

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

参考资料:

1、 UnmanagedType Enum

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