首页 > 编程语言 >实例1--C#上位机+后台C应用线程

实例1--C#上位机+后台C应用线程

时间:2025-01-19 12:29:06浏览次数:3  
标签:-- C++ DLL C# int 线程

实例1

C#上位机+后台C应用线程


目录

实例1

C#上位机+后台C应用线程

1. 需求

1.1 使用场景

1.2 关联工具

1.3  实例需求概述

1.4  实现步骤设计

2. C/C++项目

2.1 创建项目

2.2 C/C++代码

2.3 生成DLL

3. C#项目

3.1 新建项目

3.2 窗体UI控件设计

3.3 C#代码

3.4 配置与调试

总结



1. 需求

1.1 使用场景

        1. 工控行业

        在工业控制系统中,C#通常用于开发用户界面(UI)和数据监控、分析功能。而C/C++则负责与硬件设备(如PLC、传感器等)进行实时通信和数据处理。通过 C# 调用 C/C++ 生成的 DLL(动态链接库),可以将 C/C++ 实现的高效控制算法、实时数据处理、复杂计算等能力集成到 C# 上位机应用中,从而实现高性能与易用性的平衡。

     

        2. 视觉行业

        在机器视觉系统中,图像处理算法往往需要大量的计算和优化,这些功能通常由C/C++编写,因为C/C++能够充分利用硬件性能,提供高效的图像处理能力。而C#则用于构建用户界面,展示处理结果,进行控制和管理。C#通过调用 C/C++ 编写的 DLL,能够利用 C/C++ 实现的图像处理和计算能力,同时利用 C# 提供的便利性与可扩展性,简化上位机的开发。

    

        3. 人工智能行业

        在人工智能领域,尤其是在深度学习、机器学习等复杂算法的实现上,C/C++因其高性能常常作为核心算法的开发语言。而C#则可以作为前端界面,提供数据输入、结果展示、参数配置等功能。通过 C# 调用 C/C++ 算法库,能够将C/C++中实现的AI模型和算法高效集成到C#上位机应用中,实现强大的人工智能功能和良好的用户体验。

    

        4. 数据采集与控制系统

        在一些需要高频率、低延迟数据采集与控制的场合,例如实验室自动化系统、机器人控制系统等,C/C++通常承担实时性要求较高的任务。C#则提供前端展示和控制面板,供操作人员查看数据、调整参数或进行指令控制。C#通过调用C/C++编写的后台线程或DLL,确保系统能够在满足实时性要求的同时,提供清晰的用户界面和控制功能。

    

        5. 嵌入式软件的开发测试仿真

        嵌入式系统的开发往往需要在没有物理硬件或硬件设备还未完成时进行仿真。C#可以作为上位机开发语言,通过图形界面展示嵌入式系统的模拟状态,允许开发人员模拟外部设备的行为、模拟输入信号,并观察仿真结果。C/C++则用来实现嵌入式系统中的核心算法和硬件模拟,如模拟微控制器的I/O操作、定时器、PWM信号等功能。C#通过调用C/C++编写的DLL或通过多线程技术与C/C++仿真模块交互,确保仿真环境的高效性与可靠性。

    

1.2 关联工具

        C/C++ DLL开发平台:Visual Studio 2022

        C# 上位机开发平台:Visual Studio 2022

     

        基本能力要求:C/C++ 项目开发、C# 上位机开发、

    

1.3  实例需求概述

        C工程

        现有”App.c”文件,其代码如下:

#include <stdio.h>

int x;//输入量
int y;//监控量

int main()
{
	int k=0;
	while(1)
	{
		y=x+k;
		printf("y==%d\n",y);
        k++;
	}
	
	return 0;
}

    

        C# 上位机

        在C# 界面开发中,"textBox1"中输入x的值,点击"button1",调度运行C文件中函数main(),并每隔1秒获取y的值显示在"textBox2"中。点击"button2",终止函数main()的运行。注意函数main()中有 while 循环,建议多线程。

    

1.4  实现步骤设计

        Step1. 设计 C# 界面:包括 `textBox1`(用于输入 `x` 的值),`textBox2`(用于显示 `y` 的值),`button1`(启动函数),`button2`(停止函数)。

        Step2. 实现多线程:使用 `System.Threading` 中的 `Thread` 类来异步运行 C 的 `main()` 函数中的 `while` 循环。

        Step3. 使用 C# 中的锁定机制:防止 UI 线程和后台线程之间的数据竞争,确保安全地更新 UI 控件。

        Step4. 使用 `CancellationToken` 终止线程:点击按钮 2 时能够终止 `while` 循环中的线程。

        Step5. 调用C/C++项目在 Visual Studio 生成的DLL 。

2. C/C++项目

2.1 创建项目

        1.新建一个C++项目,选择 “动态链接库”

        2.项目名称 -- “C_DLL”,位置-- “C:\Users\Administrator\Desktop” ,勾选“将解决方案和项目放在同一目录中”。 点击“创建”

3.创建成功后,有自动生成的默认代码 “dllmain.cpp”

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

        将该 “dllmain.cpp” 中 代码替换如2.2 中所示。

    

2.2 C/C++代码

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include <pch.h>
/*-----------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*/
//动态库dll工程自动生成的内容
//BOOL APIENTRY DllMain( HMODULE hModule,
//                       DWORD  ul_reason_for_call,
//                       LPVOID lpReserved
//                     )
//{
//    switch (ul_reason_for_call)
//    {
//    case DLL_PROCESS_ATTACH:		
//    case DLL_THREAD_ATTACH:
//    case DLL_THREAD_DETACH:
//    case DLL_PROCESS_DETACH:
//        break;
//    }
//    return TRUE;
//}

/*-----------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*/
//头文件.h

#include <iostream>   
using namespace std;

#include <stdio.h>
#include "stdlib.h"
#include <tchar.h>

/*-----------------------------------------------------------------------*/
//全局变量
int X;//while()控制状态值
int Y;//C工程模块输出值

/*-----------------------------------------------------------------------*/
// C工程主接口函数
extern "C" __declspec(dllexport) int APPMain(int value_in)
{
	/*-----------------------*/
	//**** C工程软件标签 ****//
	cout << "    " << endl; cout << "    " << endl; cout << "    " << endl;
	cout << "//**********************************************//" << endl;
	cout << "This is the C progect!" << endl;
	cout << "//**********************************************//" << endl;
	cout << "    " << endl; cout << "    " << endl; cout << "    " << endl;
	//---------------------------//
	printf("AABBCCDD!\n");//调用DLL printf 在DLL函数调用结束后才有相关打印内容
	//---------------------------//
	int k = 0;

	while (X == 1)
	{
		Y = k + value_in;
		//cout << "Y==" << Y << endl;

		k++;
		Sleep(20);//设置C工程 机器周期为20毫秒
	}

	return 0;
}

/*-----------------------------------------------------------------------*/
// 获取 全局变量 Y 的状态值
extern "C" __declspec(dllexport) int GetOut_Y(void)
{
	//cout << "GetOut_Y==" << Y << endl;
	return Y;
}

/*-----------------------------------------------------------------------*/
// 设置 全局变量 X 的状态值
extern "C" __declspec(dllexport) int GetIn_X(int value_in)
{
	X = value_in;
	cout << "GetIn_X==" << X << endl;
	return X;
}
/*-----------------------------------------------------------------------*/

    

2.3 生成DLL

        1. 配置活动平台为 “x64” (C# 中也要设置为 x64),统一项目环境。

2.点击 “生成” -- “生成解决方案” ,输出框显示 成功。

    

3.输出 DLL 文件 位置 “C:\Users\Administrator\Desktop\C_DLL\x64\Debug”

    

3. C#项目

3.1 新建项目

        1. 新建一个C#项目,选择 “Windows窗体应用(.NET Framework)”

        2.项目名称 -- “C#_Call_C” ,位置 -- “C:\Users\Administrator\Desktop” ,勾选 “将解决方案和项目放在同一目录中” ,框架 -- “.NET Framework 4.7.2” 。 点击 “创建”

        3.点击 选项卡 “视图” -- “工具箱” ,使左侧挂载 “工具箱” 控件。

    

3.2 窗体UI控件设计

        上位机窗体UI控件

C#_UI控件配置

   

3.3 C#代码

        “Form1.cs” 中的

        元素:

private int C_Program_Status = 1;//C工程调度状态; 0--未触发调度;1--触发调度
private int Get_Count;//获取dll数据 计时器的计数值

private CancellationTokenSource app_cts;//用于控制线程终止的令牌。
private Thread workerThread;//创建的工作线程

#region 调用C工程函数 dll
public class DemoDelegate
{
    [DllImport("C_DLL.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
    public static extern int APPMain(int x);

    [DllImport("C_DLL.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
    public static extern int GetOut_Y();

    [DllImport("C_DLL.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
    public static extern int GetIn_X(int X);

}
#endregion

    

        函数:

public Form1()
private void button1_Click(object sender, EventArgs e)
private void RunMain(int x, CancellationToken token)
private void timer_GetDate(object sender, EventArgs e)

public Form1()

 public Form1()
 {
     InitializeComponent();
 }

    

private void button1_Click(object sender, EventArgs e)

/*-----------------------------------------------------------------------*/
private void button1_Click(object sender, EventArgs e)
{
    int x;

    //[调度C工程]按钮点击触发
    if (C_Program_Status==0)
    {
        /*-----------------------*/ //UI控件 设置
        button1.BackColor = System.Drawing.Color.Gray;
        button1.Text = "关闭C工程";

        /*-----------------------*/
        C_Program_Status = 1;

        DemoDelegate.GetIn_X(0);//C工程 while() 中断
        app_cts?.Cancel();// 请求停止线程;确保app_cts不为 null,避免发生空引用异常。

        this.timer_GetCprogram.Enabled = false;//关闭计时器
        /*-----------------------*/
    }
    else
    {
        // 点击按钮1,启动线程
        if (int.TryParse(textBox1.Text, out x))
        {
            /*-----------------------*/ //UI控件 设置
            button1.BackColor = System.Drawing.SystemColors.ButtonFace;
            button1.Text = "调度C工程";

            /*-----------------------*/ //
            this.timer_GetCprogram.Enabled = true;//打开计时器
            this.Get_Count = 0;

            C_Program_Status = 0;

            /*-----------------------*/
            // 创建一个取消令牌源
            app_cts = new CancellationTokenSource();
            // 启动后台线程来执行main()中的逻辑
            DemoDelegate.GetIn_X(1);
            workerThread = new Thread(() => RunMain(x, app_cts.Token));
            workerThread.Start();
            /*-----------------------*/
        }
        else
        {
            MessageBox.Show("请输入有效的x值!");
        }
    }
}
/*-----------------------------------------------------------------------*/

    

private void button2_Click(object sender, EventArgs e)

/*-----------------------------------------------------------------------*/
private void button2_Click(object sender, EventArgs e)
{
    /*-----------------------*/ //UI控件 设置
    //[清空]按钮点击触发 清空输入与输出值
    textBox1.Clear();
    textBox2.Text = "";

    button1.BackColor = System.Drawing.Color.Gray;
    button1.Text = "关闭C工程";

    /*-----------------------*/
    C_Program_Status = 1;

    DemoDelegate.GetIn_X(0);//C工程 while() 中断
    app_cts?.Cancel();// 请求停止线程

    this.timer_GetCprogram.Enabled = false;//关闭计时器
    /*-----------------------*/
}
/*-----------------------------------------------------------------------*/

    

private void RunMain(int x, CancellationToken token)

/*-----------------------------------------------------------------------*/
// 模拟C语言main()中的功能,传入值x
private void RunMain(int x, CancellationToken token)
{
    Debug.WriteLine("--- 启动后台线程 --");
    if (!token.IsCancellationRequested)
    {
        Debug.WriteLine("*** Start C progect ***");
        
        DemoDelegate.APPMain(x);
        //Invoke(new Action(() => textBox2.Text = y.ToString()));//更新textBox2中的y值(要使用Invoke来访问UI控件)
        Debug.WriteLine("*** End of C progect run! ***");
    }

    /*-----------------------*/ //另一种配置方案
    //int k = 0;
    //while (!token.IsCancellationRequested)
    //{
    //    // 计算y = x + k
    //    int y = x + k;
    //    // 更新textBox2中的y值(要使用Invoke来访问UI控件)
    //    Invoke(new Action(() => textBox2.Text = y.ToString()));
    //    // 每20毫秒更新一次
    //    Thread.Sleep(20);
    //    k++;
    //}
    /*-----------------------*/

}

    

private void timer_GetDate(object sender, EventArgs e)

/*-----------------------------------------------------------------------*/
//获取获取dll数据计时器
private void timer_GetDate(object sender, EventArgs e)
{
    this.Get_Count++;
    if((Get_Count%50==0)&&(C_Program_Status==0))//每20*50=1000毫秒,刷新获取的数据
    {
        int y = 0;
        y = DemoDelegate.GetOut_Y();
        
        textBox2.Text = y.ToString();
        Debug.WriteLine("Get_Count=={0}, y=={1}", Get_Count, y);
    }
}

/*-----------------------------------------------------------------------*/

    

3.4 配置与调试

        1. 配置活动平台为 “x64” (C# 中也要设置为 x64),统一项目环境。

        2.点击 “生成” -- “生成解决方案” ,输出框显示 成功。

        3.将 “C_DLL” 生成的  .dll  文件,拷贝至 C#_call_C 文件的 “C#_call_C\bin\x64\Debug” 路径下,以便做工程的调试。

        4.将 “C_DLL” 生成的 .dll 文件,拷贝至 C#_call_C 文件的 “C#_call_C\obj\x64\Debug” 路径下,以便做工程的独立运行。

    

        5.调试态如下动图所示

      

总结

C# 上位机 + 后台 C/C++ 应用的优势:

        -- 性能和效率: C/C++负责处理计算密集型的任务,如图像处理、数据分析和实时控制等,能够充分利用硬件资源并提供高性能。

       -- 可扩展性与开发效率: C#拥有丰富的开发工具和库,特别是在UI设计、数据处理和网络通信方面。它使得上位机界面的开发更加便捷,可以快速搭建用户交互界面。

        -- 多线程与异步处理: 在复杂的工业系统中,后台C/C++线程可以处理实时数据采集和控制指令,而C#则通过调用后台线程提供的数据,实时更新UI,确保界面不会因阻塞而卡顿。

        -- 模块化与解耦: 将C/C++算法封装为DLL,使得C#与C/C++代码之间的依赖关系最小化,便于维护和升级。同时,C/C++和C#的分工明确,互不干扰,便于团队协作。

        通过这种结合方式,上位机不仅能够充分利用C/C++的高效性能,还能通过C#简化开发过程,提升用户体验,在许多高性能计算和工业控制系统中具有重要的应用价值。

//--------------------------------------------------------------------------------------------------------------------------//

2025年1月14日

By:Mr.C

//--------------------------------------------------------------------------------------------------------------------------//

标签:--,C++,DLL,C#,int,线程
From: https://blog.csdn.net/weixin_40978412/article/details/145141601

相关文章

  • 【自学嵌入式(4)STA模式、AP模式、体验天气时钟】
    STA模式、AP模式、体验天气时钟一、无线终端模式*概念**特点**优点**缺点*二、接入点模式*概念**特点**优点**缺点*三、天气时钟的实现上一篇文章主要展示的是无线终端模式的应用(两个开发板都作为终端设备,连接外部网络);为了了解开发板作为无线接入点(AP)的功能,前几天......
  • 【自学嵌入式(5)环形电阻、数码管、随机数字程序】
    环形电阻、数码管、随机数字程序环形电阻*概念**特点*共阴极数码管*概念*随机数字程序本篇文章将会手动搭建一个电路,使用到的硬件主要有面包板一个、若干杜邦线、UNO开发板一个、按键开关一个、一个200-500欧姆的电阻、共阴极数码管一个由于对即将要使用的环形电......
  • 计算机毕业设计Springboot房屋租赁管理系统 基于SpringBoot的住宅租赁平台开发 Spring
    计算机毕业设计Springboot房屋租赁管理系统v26r4k2k(配套有源码程序mysql数据库论文)本套源码可以先看具体功能演示视频领取,文末有联xi可分享随着城市化进程的加速,房屋租赁市场日益繁荣,但同时也面临着诸多管理难题,如信息不对称、流程繁琐等。为了提升租赁市场的运营效率和......
  • 计算机毕业设计Springboot超市进销存管理系统 基于Springboot的超市库存与销售管理系
    计算机毕业设计Springboot超市进销存管理系统r1401773(配套有源码程序mysql数据库论文)本套源码可以先看具体功能演示视频领取,文末有联xi可分享随着社会经济的快速发展和人民生活水平的提高,超市作为零售业的主要业态,其规模和业务范围不断扩大。传统的超市进销存管理模式已......
  • 当你懒惰时,非常好用的神器
    今天给大家带来一款好用的摸鱼软件,简单易上手,有需要的小伙伴赶紧收藏起来吧!隐藏软件(Windows)隐藏软件是一款能通过热键隐藏你当前软件和窗口的便捷工具,软件体积非常小,大小只有764K,并且其是绿色单文件版。软件使用十分简单,点左下角的【添加程序】,既可添加单程序隐藏,也可以......
  • 12000台虚拟机大迁移!又一家公司宣布弃用VMware,自制KVM平台替代
    曾几何时,提起虚拟化,VMware是一家绕不开躲不过的公司,它也是第一个虚拟化x86架构并取得商业成功的公司,备受业界关注。可惜的是,自从2023年11月,VMware被博通以610亿美元收购,后者对其进行大刀阔斧地改革,并把VMware原有云服务的“永久许可证”改为了订阅制度之后,遭到了不......
  • Java初学者笔记-05、集合框架
    集合体系集合是可变长的。Collection:代表单列集合,每个元素包含一个值。Map:代表双列集合,每个元素包含两个值。Collection集合List系列集合:添加的元素是有序、可重复、有索引。ArrayList、LinekdList:有序、可重复、有索引。Set系列集合:添加的元素是无序、不重复、无索......
  • ServletContext 的 4 个常见应用!
    一、ServletContext类详解Web容器在启动时,会为每一个Web项目创建一个对应的ServletContext对象。它是当前项目中所有Servlet实例之间信息交互的“中间商”。(参见“Servlet的运行原理”)注:一个Web项目对应一个ServletContext。(即:同一个Web项目中的所有servlet实例共用同一个Servle......
  • 从零开始掌握圆锥曲线大法
    \(\texttt{0x00}\)写在前面没想到退役之后一重返whk就被这玩意儿困扰得苦不堪言……所以现决定将这一整个知识框架全部重新补一遍。为什么写这篇文章(Why)哲学上讲分析原因要从多方面来思考,所以我也进行了如下根本不正经的思考:必要性:理论必要性:圆锥曲线位于选择性必修一,是......
  • Servlet 映射(访问)路径的 3 种方式!
    Servlet的映射(访问)路径是在Web配置文件(web.xml)中编写的。如:<!--Servlet的访问路径--><servlet-mapping><servlet-name>servlet1</servlet-name><url-pattern>/hello</url-pattern></servlet-mapping>1、servlet映射(访问)路径的方式注:一个......