C# Winform 使用委托实现C++中回调函数的功能

C# Winform 使用委托实现C++中回调函数的功能

在项目中遇到了使用C#调用C++封装的接口,其中C++接口有一个回调函数的参数。参考对比后,在C#中是使用委托(delegate)来实现类似的功能。

下面使用一个示例来介绍具体的使用方式:

第一步:新建C++动态连接库

1.首先新建一个C++动态库程序,如下图所示:

并且在dllmain.cpp文件中添加如下导数函数代码:

C++ 复制代码
typedef int (*FUNC)(int a, int b);
extern "C"  __declspec(dllexport) void _stdcall AddNum(int a,int b,FUNC callback)
{
    callback(a, b);
}

该函数传入两个整数,并且调用外部的回调函数对这两个整数运算处理。

运行程序生成了相应的DLL

第二步:新建WinForm项目

我新建了一个简单的WinForm程序的界面如下:

由用户输入两个数值,选择相应的运算符,分别有+,-,*,/ 四种运算符,点击计算按钮就得到运算结果。上面我填了10 * 20 = 200;

具体代码如下:

C# 复制代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Net.Mime.MediaTypeNames;

namespace WinFormCallBackTest
{
    public partial class Form1 : Form
    {
        public delegate int CallBackFunc(int a, int b);
        CallBackFunc callback = null;

        [DllImport("CallBackDLLTest.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern void AddNum(int a,int b,CallBackFunc callback);

        public Form1()
        {
            InitializeComponent();
            // 设置窗体启动位置为屏幕中央
            this.StartPosition = FormStartPosition.CenterScreen;

            callback = new CallBackFunc(CallBackAddNum);

        }

        public int CallBackAddNum(int a, int b)
        {
            int nSum = 0;
            if (comboBox1.Text == "+")
            {
                nSum = a + b;
            }
            else if (comboBox1.Text == "-")
            {
                nSum = a - b;
            }
            else if (comboBox1.Text == "*")
            {
                nSum = a * b;
            }
            else if (comboBox1.Text == "/")
            {
                nSum = a / b;
            }

            // 检查是否需要跨线程操作
            if (textBox3.InvokeRequired)
            {
                // 使用Invoke将操作排队到UI线程
                textBox3.Invoke(new Action(() => textBox3.Text = nSum.ToString()));
            }
            else
            {
                // 直接更新控件
                textBox3.Text = nSum.ToString();
            }


            return 0;
        }

        private void button_Calculate_Click(object sender, EventArgs e)
        {
            int a = 0,b = 0;
            Int32.TryParse(textBox1.Text,out a);
            Int32.TryParse(textBox2.Text,out b);
            AddNum(a,b, CallBackAddNum);
        }
    }
}

代码说明:

c# 复制代码
public delegate int CallBackFunc(int a, int b);
CallBackFunc callback = null;

1.上述代码使用delegate声明一个委托,并且将其初始化为null,在构造函数中将回调函数和委托进行绑定。

2.然后再按钮中调用导入的C++接口。

3.在回调函数中将计算结果更新到控件中。

这里有多种方式:

方式1:使用InvokeBeginInvoke方法

InvokeBeginInvoke是.NET提供的用于跨线程更新控件的方法。它们可以确保控件的更新操作在UI线程中执行。

示例代码:

c# 复制代码
 // 检查是否需要跨线程操作
            if (textBox3.InvokeRequired)
            {
                // 使用Invoke将操作排队到UI线程
                textBox3.Invoke(new Action(() => textBox3.Text = nSum.ToString()));
            }
            else
            {
                // 直接更新控件
                textBox3.Text = nSum.ToString();
            }

上述代码中

  • InvokeRequired :用于检查当前线程是否是控件的创建线程。如果是,则不需要跨线程操作;如果不是,则需要使用InvokeBeginInvoke
  • Invoke:同步执行委托,阻塞当前线程,直到操作完成。
  • BeginInvoke:异步执行委托,不会阻塞当前线程。

方式2:使用SynchronizationContext

SynchronizationContext是一个更通用的线程同步机制,可以用于在任何线程中同步到UI线程。

示例代码:

c# 复制代码
// 在主线程中保存SynchronizationContext
private SynchronizationContext _uiContext;

public Form1()
{
    InitializeComponent();
    _uiContext = SynchronizationContext.Current; // 保存主线程的SynchronizationContext
}

// 在委托回调函数中使用SynchronizationContext更新控件
public void UpdateButtonCallback()
{
    _uiContext.Post(_ =>
    {
        button1.Text = "更新后的文本";
    }, null);
}
  • SynchronizationContext.Current:获取当前线程的同步上下文。
  • Post:异步执行委托。

方式3:使用TaskContinueWith(异步任务)

如果你在异步任务中需要更新UI控件,可以使用ContinueWith方法,并指定在UI线程中执行。

示例代码:

c# 复制代码
// 假设有一个按钮控件button1和一个异步任务
public void UpdateButtonAsync()
{
    Task.Run(() =>
    {
        // 模拟耗时操作
        Thread.Sleep(2000);
    }).ContinueWith(t =>
    {
        button1.Text = "更新后的文本";
    }, TaskScheduler.FromCurrentSynchronizationContext()); // 确保在UI线程中执行
}
  • TaskScheduler.FromCurrentSynchronizationContext:指定任务继续在UI线程中执行。

总结

  • 如果是WinForms应用程序,推荐使用InvokeBeginInvoke
  • 如果是WPF应用程序,推荐使用Dispatcher.Invoke
  • 如果需要更通用的解决方案,可以使用SynchronizationContext
  • 如果是异步任务,可以使用TaskContinueWith

好了,关于在C#WinForm中使用委托实现回调函数功能的介绍就到这里了。

源码地址:Gitee

相关推荐
★YUI★6 小时前
学习游戏制作记录(玩家掉落系统,删除物品功能和独特物品)8.17
java·学习·游戏·unity·c#
谷宇.6 小时前
【Unity3D实例-功能-拔枪】角色拔枪(二)分割上身和下身
游戏·unity·c#·游戏程序·unity3d·游戏开发·游戏编程
LZQqqqqo7 小时前
C# 中 ArrayList动态数组、List<T>列表与 Dictionary<T Key, T Value>字典的深度对比
windows·c#·list
Dm_dotnet9 小时前
Stylet启动机制详解:从Bootstrap到View显示
c#
三千道应用题11 小时前
WPF&C#超市管理系统(6)订单详情、顾客注册、商品销售排行查询和库存提示、LiveChat报表
开发语言·c#·wpf
唐青枫15 小时前
别滥用 Task.Run:C# 异步并发实操指南
c#·.net
我好喜欢你~1 天前
C#---StopWatch类
开发语言·c#
一阵没来由的风1 天前
拒绝造轮子(C#篇)ZLG CAN卡驱动封装应用
c#·can·封装·zlg·基础封装·轮子
一枚小小程序员哈1 天前
基于微信小程序的家教服务平台的设计与实现/基于asp.net/c#的家教服务平台/基于asp.net/c#的家教管理系统
后端·c#·asp.net
Eternity_GQM1 天前
【Word VBA Zotero 引用宏错误分析与改正指南】【解决[21–23]参考文献格式插入超链接问题】
开发语言·c#·word