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

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

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

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

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

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

在这里插入图片描述

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

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程序的界面如下:

微信截图_20250209232909

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

具体代码如下:

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);}}
}

代码说明:

public delegate int CallBackFunc(int a, int b);
CallBackFunc callback = null;

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

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

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

这里有多种方式:

方式1:使用InvokeBeginInvoke方法

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

示例代码:

 // 检查是否需要跨线程操作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线程。

示例代码:

// 在主线程中保存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线程中执行。

示例代码:

// 假设有一个按钮控件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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/69622.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习之神经网络框架搭建及模型优化

神经网络框架搭建及模型优化 目录 神经网络框架搭建及模型优化1 数据及配置1.1 配置1.2 数据1.3 函数导入1.4 数据函数1.5 数据打包 2 神经网络框架搭建2.1 框架确认2.2 函数搭建2.3 框架上传 3 模型优化3.1 函数理解3.2 训练模型和测试模型代码 4 最终代码测试4.1 SGD优化算法…

2025.2.9 每日学习记录2:技术报告写了一半+一点点读后感

0.近期主任务线 1.完成小论文准备 目标是3月份完成实验点1的全部实验和论文。 2.准备教资笔试 打算留个十多天左右,一次性备考笔试的三个科目 1.实习申请技术准备:微调、Agent、RAG 1.今日完成任务 1.电子斗蛐蛐(文本书写领域&am…

9 Pydantic复杂数据结构的处理

在构建现代 Web 应用时,我们往往需要处理复杂的输入和输出数据结构。例如,响应数据可能包含嵌套字典、列表、元组,甚至是多个嵌套对象。Pydantic 是一个强大的数据验证和序列化库,可以帮助我们轻松地处理这些复杂的数据结构&#…

链表(LinkedList) 1

上期内容我们讲述了顺序表,知道了顺序表的底层是一段连续的空间进行存储(数组),在插入元素或者删除元素需要将顺序表中的元素整体移动,时间复杂度是O(n),效率比较低。因此,在Java的集合结构中又引入了链表来解决这一问…

torch_bmm验算及代码测试

文章目录 1. torch_bmm2. pytorch源码 1. torch_bmm torch.bmm的作用是基于batch_size的矩阵乘法,torch.bmm的作用是对应batch位置的矩阵相乘,比如, mat1的第1个位置和mat2的第1个位置进行矩阵相乘得到mat3的第1个位置mat1的第2个位置和mat2的第2个位置…

shell+kafka实现服务器健康数据搜集

今天有一个徒弟问我,分发、代理服务器都装有kafka,如何快速收集服务器的健康数据,每10秒就收集一次? 我当时听完之后,楞了一下,然后说出了我的见解:认为最快速的方法无法就是建议shell脚本直接采…

macbook2015升级最新MacOS 白苹果变黑苹果

原帖:https://www.bilibili.com/video/BV13V411c7xz/MAC OS系统发布了最新的Sonoma,超酷的动效锁屏壁纸,多样性的桌面小组件,但是也阉割了很多老款机型的升级权利,所以我们可以逆向操作,依旧把老款MAC设备强…

建筑物损坏程度分割数据集labelme格式2816张5类别

数据集格式:labelme格式(不包含mask文件,仅仅包含jpg图片和对应的json文件) 图片数量(jpg文件个数):2816 标注数量(json文件个数):2816 标注类别数:5 标注类别名称:["minor-damage","destroyed&quo…

ReactNative进阶(五十九):存量 react-native 项目适配 HarmonyOS NEXT

文章目录 一、前言二、ohos_react_native2.1 Fabric2.2 TurboModule2.2.1 ArkTSTurboModule2.2.2 cxxTurboModule: 三、拓展阅读 一、前言 2024年10月22日19:00,华为在深圳举办“原生鸿蒙之夜暨华为全场景新品发布会”,主题为“星河璀璨&…

Golang GORM系列:GORM CRUM操作实战

在数据库管理中,CRUD操作是应用程序的主干,支持数据的创建、检索、更新和删除。强大的Go对象关系映射库GORM通过抽象SQL语句的复杂性,使这些操作变得轻而易举。本文是掌握使用GORM进行CRUD操作的全面指南,提供了在Go应用程序中有效…

【Windows】PowerShell 缓存区大小调节

PowerShell 缓存区大小调节 方式1 打开powershell 窗口属性调节方式2,修改 PowerShell 配置文件 方式1 打开powershell 窗口属性调节 打开 CMD(按 Win R,输入 cmd)。右键标题栏 → 选择 属性(Properties)…

Json-RPC框架项目(一)

目录 1. 项目介绍: 2. 技术选择; 3. 第三方库介绍; 4. 项目功能; 5. 模块功能; 6. 项目实现: 1. 项目介绍: RPC是远程过程调用, 像调用本地接口一样调用远程接口, 进行完成业务处理, 计算任务等, 一个完整的RPC包括: 序列化协议, 通信协议, 连接复用, 服务注册, 服务发…

深度整理总结MySQL——MySQL加锁工作原理

MySQL加锁工作原理 前言前置知识- 锁为什么加在索引上锁的粒度优化提高并发性避免全表扫描优化死锁处理解决幻读问题 什么SQL语句会加行级锁MySQL是如何加行级锁场景模拟代码唯一索引等值查询退化为记录锁为什么会退化为记录锁分析加了什么锁为什么会退化为间隙锁为什么我可以插…

Deepseek系列从v3到R易背面经版

deepseek v3 base要点 MTP : Multi-Token Prediction 训练时: 1. 把前一个block中input tokens经过embedding layer和transformer block的输出,进入output head之前的内容记为h,与下一个block的input tokens经过embedding layer输出的内容都…

大模型融入推荐系统

结合项目实际给用户推荐,比如是商家项目,用户问了几个关于商品的信息,大模型就可以根据根据用户画像,给用户推荐商品。 我们现在做的是针对于用户学习的推荐,首先我们要对我们的数据进行处理,提取出我们数…

MariaDB MaxScale实现mysql8主从同步读写分离

一、MaxScale基本介绍 MaxScale是maridb开发的一个mysql数据中间件,其配置简单,能够实现读写分离,并且可以根据主从状态实现写库的自动切换,对多个从服务器能实现负载均衡。 二、MaxScale实验环境 中间件192.168.121.51MaxScale…

【JVM详解五】JVM性能调优

示例: 配置JVM参数运行 #前台运行 java -XX:MetaspaceSize-128m -XX:MaxMetaspaceSize-128m -Xms1024m -Xmx1024m -Xmn256m -Xss256k -XX:SurvivorRatio8 - XX:UseConcMarkSweepGC -jar /jar包路径 #后台运行 nohup java -XX:MetaspaceSize-128m -XX:MaxMetaspaceS…

畅聊deepseek-r1,SiliconFlow 硅基流动注册+使用

文章目录 SiliconFlow 硅基流动注册使用注册创建API密钥使用网页端使用代码调用api调用支持的模型 SiliconFlow 硅基流动注册使用 注册 硅基流动官网 https://cloud.siliconflow.cn/i/XcgtUixn 注册流程 切换中文 ​ 邀请码: XcgtUixn 创建API密钥 账户管理 --&g…

C++ Primer 类型转换

欢迎阅读我的 【CPrimer】专栏 专栏简介:本专栏主要面向C初学者,解释C的一些基本概念和基础语言特性,涉及C标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级…

Gitlab中如何进行仓库迁移

需求:之前有一个自己维护的新仓库A,现在需要将这个仓库提交并覆盖另一个旧的仓库B,需要保留A中所有的commit信息。 1.方法一:将原有仓库A导出后再导入到新的仓库B中 适用场景:新的仓库B是一个待建仓库,相当…