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

server/2025/2/12 1:49:39/

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

代码说明:

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


http://www.ppmy.cn/server/166910.html

相关文章

推荐系统概述 | 《推荐系统教程》第一章笔记

推荐系统概述 | 《推荐系统教程》第一章笔记 一、推荐系统的意义1.推荐系统的核心价值1.1 平台方视角1.2. 信息生产者(物品)视角1.3. 信息消费者(用户)视角 2、推荐系统 vs 搜索系统3、推荐系统的典型应用场景1. 电商领域2. 视频平…

从MyBatis-Plus看Spring Boot自动配置原理

一、问题引入&#xff1a;神秘的配置生效之谜 当我们使用MyBatis-Plus时&#xff0c;只需在pom.xml中添加依赖&#xff1a; <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3…

RabbitMQ技术深度解析:打造高效消息传递系统

引言 在当前的分布式系统架构中&#xff0c;消息队列作为一种高效的消息传递机制&#xff0c;扮演着越来越重要的角色。RabbitMQ&#xff0c;作为广泛使用的开源消息代理&#xff0c;以其高可用性、扩展性和灵活性赢得了众多开发者的青睐。本文将深入探讨RabbitMQ的核心概念、…

Spring 中的 事务 隔离级别以及传播行为

1. 事务隔离级别&#xff08;Isolation Level&#xff09; 事务隔离级别定义了事务在并发环境下的行为&#xff0c;主要解决以下问题&#xff1a; 脏读&#xff08;Dirty Read&#xff09;&#xff1a;一个事务读取了另一个未提交事务的数据。 不可重复读&#xff08;Non-Re…

【GitHub】相关工具下载及使用

目录 背景GitHub的使用Git工具下载及安装 背景 需要在GitHub查阅相关资料&#xff0c;以下是对使用GitHub做相关记录。 GitHub的使用 参考链接: GitHub入门指南&#xff1a;一步一步教你使用GitHub Git工具下载及安装 参考链接: windows安装git&#xff08;全网最详细&…

初窥强大,AI识别技术实现图像转文字(OCR技术)

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据、人工智能领域创作者。目前从事python全栈、爬虫和人工智能等相关工作&#xff0c;主要擅长领域有&#xff1a;python…

体验 DeepSeek-R1:解密 1.5B、7B、8B 版本的强大性能与应用

文章目录 &#x1f34b;引言&#x1f34b;DeepSeek 模型简介&#x1f34b;版本更新&#xff1a;1.5B、7B、8B 的区别与特点&#x1f34b;模型评估&#x1f34b;体验 DeepSeek 的过程&#x1f34b;总结 &#x1f34b;引言 随着大规模语言模型的持续发展&#xff0c;许多模型在性…

vue-vite axios bug

axios-bug http proxy error Error: write ECONNABORTED 代码写法 一般baseURL不是单写前缀就可以了吗&#xff0c;为何要写死就不会出现以上错误&#xff0c;求解。