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. 说明2. 重要性3. 主要内容4. 编写原则5. 实践建议6. 例题6.1 例题1 1. 说明 1.绝大多数的体系结构都是抽象的,由一些概念上的构建组成。2.层的概念在任何程序设计语言中都不存在。3.要让系统分析员和程序员去实现体系结构,还必须将体系结构进行…

嵌入式AI革命:DeepSeek开源如何终结GPU霸权,开启单片机智能新时代?

2025年,全球AI领域最震撼的突破并非来自算力堆叠的超级模型,而是中国团队DeepSeek通过开源策略,推动大模型向微型化、低功耗场景的跨越。相对于当人们还在讨论千亿参数模型的训练成本被压缩到600万美金而言,被称作“核弹级别”的操…

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

神经网络框架搭建及模型优化 目录 神经网络框架搭建及模型优化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优化算法…

机器学习之心的创作纪念日

机缘 今天,是我成为创作者的第1460天。 在这段时间里,获得了很大的成长。 虽然日常忙碌但还在坚持创作、初心还在。 日常 创作已经成为我生活的一部分,尤其是在我的工作中,创作是不可或缺的,创作都是核心能力之一。…

【RabbitMQ重试】重试三次转入死信队列

以下是基于RabbitMQ死信队列实现消息重试三次后转存的技术方案&#xff1a; 方案设计要点 队列定义改造&#xff08;核心参数配置&#xff09; Bean public Queue auditQueue() {Map<String, Object> args new HashMap<>();args.put("x-dead-letter-exchan…

软件工程-软件需求分析基础

基本任务 准确地回答“系统必须做什么&#xff1f;”&#xff0c;也就是对目标系统提出完整、准确、清晰、具体的要求 目标是&#xff0c;在分析阶段结束之前&#xff0c;系统分析员应该写出软件需求规格说明书&#xff0c;以书面形式准确地描述软件需求。 准则 1&#xff…

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

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

9 Pydantic复杂数据结构的处理

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

链表(LinkedList) 1

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

【C#】任务调度的实现原理与组件应用Quartz.Net

Quartz 是一个流行的开源作业调度库&#xff0c;最初由 Terracotta 开发&#xff0c;现在由 Terracotta 的一部分 Oracle 所有。它主要用于在 Java 应用程序中调度作业的执行。Quartz 使用了一种复杂的底层算法来管理任务调度&#xff0c;其中包括任务触发、执行、持久化以及集…

torch_bmm验算及代码测试

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

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

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

web前端布局--使用element中的Container布局容器

前端页面&#xff0c;跟Qt中一样&#xff0c;都是有布局设置的。 先布局&#xff0c;然后再在各布局中添加显示的内容。 Element网站布局容器&#xff1a;https://element.eleme.cn/#/zh-CN/componet/container 1.将element相应的布局容器代码layout&#xff0c;粘贴到vue项…

vcredist_x64.exe 是 Microsoft Visual C++ Redistributable 的 64 位版本

vcredist_x64.exe 是 Microsoft Visual C++ Redistributable 的 64 位版本,它提供了运行基于 Visual C++ 编写的应用程序所需的库文件。许多 Windows 应用程序都依赖这些库来正常运行,特别是使用 Visual Studio 编译的程序。 用途和重要性: 运行时库:vcredist_x64.exe 安装…

一个简单的Windows TCP服务器实现

初始化 WSADATA wsaData; SOCKET serverSocket, clientSocket; struct sockaddr_in serverAddr { 0x00 }; struct sockaddr_in clientAddr { 0x00 }; int clientAddrLen sizeof(clientAddr);if (WSAStartup(MAKEWORD(2, 2), &wsaData) ! 0) {printf("WSAStartup f…

AF3 drmsd函数解读

drmsd(distance Root Mean Square Deviation,距离均方根偏差)函数在AlphaFold3的 src.utils.validation_metrics模块中定义,用于计算两个蛋白质结构(或其他分子结构)之间的距离偏差。它衡量了两个结构的 成对原子间距离 差异,而不是直接比较原子坐标。这种度量方式比 RM…

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

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

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

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

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

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

Golang GORM系列:GORM CRUM操作实战

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