当前位置: 首页 > news >正文

基于C#UI Automation自动化测试

步骤

UI Automation 只适用于,标准的win32和 WPF程序

需要添加对UIAutomationClient、 UIAutomationProvider、 UIAutomationTypes的引用

代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Automation;
using System.Windows.Forms;
using System.Windows.Input;namespace WindowsFormsApp1
{public partial class Form1 : Form{private Process processnotepad;private Process processcalc;public Form1(){InitializeComponent();}private void button1_Click(object sender, EventArgs e){//打开笔记本processnotepad = Process.Start(@"C:\Windows\System32\notepad.exe");}private void button2_Click(object sender, EventArgs e){//关闭笔记本processnotepad.Kill();}private void button3_Click(object sender, EventArgs e){//AutomationElement表示 UI 自动化树中的一个 UI 自动化元素,并包含由 UI 自动化客户端应用程序用作标识符的值。//获取当前桌面的根 AutomationElement。AutomationElement desktop = AutomationElement.RootElement;//StringBuilder不在内存中创建新对象,而是动态扩展内存以容纳修改后的字符串。StringBuilder sb = new StringBuilder();//TreeScope(枚举)包含指定 UI 自动化目录树内元素的范围的值。具体参考请点击以下链接进行查看//TreeScope官方链接:https://learn.microsoft.com/zh-cn/dotnet/api/system.windows.automation.treescope?view=windowsdesktop-7.0AutomationElementCollection topWindows = desktop.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "ApplicationFrameWindow"));//查找计算器for (int i = 0; i < topWindows.Count; i++){AutomationElement topWindow = topWindows[i];sb.AppendLine("Name:" + topWindow.Current.Name + ";ClassName=" + topWindow.Current.ClassName);}MessageBox.Show(sb.ToString());}private void button4_Click(object sender, EventArgs e){AutomationElement desktop = AutomationElement.RootElement;var calcFrame1 = desktop.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "ApplicationFrameWindow"));AutomationElementCollection btn2 = calcFrame1.FindAll(TreeScope.Descendants, new PropertyCondition(AutomationElement.AutomationIdProperty, "num9Button"));AutomationElement btn = btn2[0];MessageBox.Show(btn.Current.Name);}private void button5_Click(object sender, EventArgs e){AutomationElement desktop = AutomationElement.RootElement;var calcFrame1 = desktop.FindFirst(TreeScope.Children,new PropertyCondition(AutomationElement.ClassNameProperty, "ApplicationFrameWindow"));Condition conditionBtn6 = new AndCondition(new PropertyCondition(AutomationElement.ClassNameProperty, "Button"),new PropertyCondition(AutomationElement.NameProperty, "六"));var btn6 = calcFrame1.FindFirst(TreeScope.Descendants, conditionBtn6);//InvokePattern 表示用于启动或执行单个明确操作的控件,并且这些控件在激活时不保持其状态。//InvokePattern.Pattern 标识 InvokePattern 控件模式。//InvokePattern官方链接 https://learn.microsoft.com/zh-cn/dotnet/api/system.windows.automation.invokepattern?view=windowsdesktop-7.0InvokePattern button6Invoke = (InvokePattern)btn6.GetCurrentPattern(InvokePattern.Pattern);//Invoke() 发送请求以激活控件并启动其单一、明确的操作。button6Invoke.Invoke();Condition conditionBtnPlus = new AndCondition(new PropertyCondition(AutomationElement.ClassNameProperty, "Button"),new PropertyCondition(AutomationElement.NameProperty, "加"));var btnPlus = calcFrame1.FindFirst(TreeScope.Descendants, conditionBtnPlus);InvokePattern buttonPlusInvoke = (InvokePattern)btnPlus.GetCurrentPattern(InvokePattern.Pattern);buttonPlusInvoke.Invoke();}private static void InvokeButton(AutomationElement e){InvokePattern Invoke = (InvokePattern)e.GetCurrentPattern(InvokePattern.Pattern);Invoke.Invoke();}private static void ClickCalculatorButton(AutomationElement calcFrame1, String name){Condition conditionBtn = new AndCondition(new PropertyCondition(AutomationElement.ClassNameProperty, "Button"),new PropertyCondition(AutomationElement.NameProperty, name));var btn = calcFrame1.FindFirst(TreeScope.Descendants, conditionBtn);// MessageBox.Show(btn.Current.Name);if (btn == null){throw new Exception("找不到此" + name + "的按钮");}InvokeButton(btn);}private void button6_Click(object sender, EventArgs e){AutomationElement desktop = AutomationElement.RootElement;var calcFrame1 = desktop.FindFirst(TreeScope.Children,new PropertyCondition(AutomationElement.ClassNameProperty, "ApplicationFrameWindow"));ClickCalculatorButton(calcFrame1, "三");ClickCalculatorButton(calcFrame1, "乘以");ClickCalculatorButton(calcFrame1, "五");ClickCalculatorButton(calcFrame1, "五");ClickCalculatorButton(calcFrame1, "等于");}[DllImport("user32.dll")]public static extern void SetCursorPos(int x, int y);[DllImport("user32.dll")]public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);private void button7_Click(object sender, EventArgs e){AutomationElement desktop = AutomationElement.RootElement;var calcFrame1 = desktop.FindFirst(TreeScope.Children,new PropertyCondition(AutomationElement.ClassNameProperty, "ApplicationFrameWindow"));Condition conditionBtn6 = new AndCondition(new PropertyCondition(AutomationElement.ClassNameProperty, "Button"),new PropertyCondition(AutomationElement.NameProperty, "六"));var btn6 = calcFrame1.FindFirst(TreeScope.Descendants, conditionBtn6);SetCursorPos((int)btn6.GetClickablePoint().X, (int)btn6.GetClickablePoint().Y);//mouse_event(0x0002 | 0x0004, 0, 0, 0, 0);mouse_event(0x0002, 0, 0, 0, 0);  // 模拟鼠标左键按下mouse_event(0x0004, 0, 0, 0, 0);  // 模拟鼠标左键弹起}private void button8_Click(object sender, EventArgs e){AutomationElement desktop = AutomationElement.RootElement;var calcFrame1 = desktop.FindFirst(TreeScope.Children,new PropertyCondition(AutomationElement.ClassNameProperty, "Notepad"));Condition conditionEdit = new AndCondition( new PropertyCondition(AutomationElement.ClassNameProperty, "Edit"), new PropertyCondition(AutomationElement.NameProperty, "文本编辑器"));AutomationElement txtEdit = calcFrame1.FindFirst(TreeScope.Descendants, conditionEdit);txtEdit.SetFocus();SendKeys.Send("追加123456789");}private const int WM_SETTEXT = 0x000C;[DllImport("user32.dll")]private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);[DllImport("User32.dll")]private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindows);[DllImport("User32.dll")]private static extern Int32 SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, StringBuilder lParam);private void button9_Click(object sender, EventArgs e){AutomationElement desktop = AutomationElement.RootElement;var calcFrame1 = desktop.FindFirst(TreeScope.Children,new PropertyCondition(AutomationElement.ClassNameProperty, "Notepad"));Condition conditionEdit = new AndCondition(new PropertyCondition(AutomationElement.ClassNameProperty, "Edit"), new PropertyCondition(AutomationElement.NameProperty, "文本编辑器"));AutomationElement txtEdit = calcFrame1.FindFirst(TreeScope.Descendants, conditionEdit);//.NET提供了一个结构体System.IntPtr专门用来代表句柄或指针。//句柄是对象的标识符,当调用这些API创建对象时,它们并不直接返回指向对象的指针,//而是会返回一个32位或64位的整数值,这个在进程或系统范围内唯一的整数值就是句柄(Handle),//随后程序再次访问对象,或者删除对象,都将句柄作为Windows API的参数来间接对这些对象进行操作。//句柄是一个结构体,简单的来说,它是指针的一个封装,是C#中指针的替代者//句柄链接:https://blog.csdn.net/sinat_40003796/article/details/127244155IntPtr hWnd = FindWindow("Notepad", null);if (!hWnd.Equals(IntPtr.Zero)){IntPtr edithWnd = FindWindowEx(hWnd, IntPtr.Zero, "Edit", null);if (!edithWnd.Equals(IntPtr.Zero)){SendMessage(edithWnd, WM_SETTEXT, IntPtr.Zero, new StringBuilder("重写123456789"));}}else{}}private void button10_Click(object sender, EventArgs e){//点击后有列表的按钮不要当成点击事件AutomationElement desktop = AutomationElement.RootElement;var calcFrame1 = desktop.FindFirst(TreeScope.Children,new PropertyCondition(AutomationElement.ClassNameProperty, "Notepad"));Condition myCondition2 = new PropertyCondition(AutomationElement.NameProperty, "编辑(E)");AutomationElement w2 = calcFrame1.FindFirst(TreeScope.Descendants, myCondition2);ExpandCollapsePattern ecp = (ExpandCollapsePattern)w2.GetCurrentPattern(ExpandCollapsePattern.Pattern);ecp.Expand();}private AutomationElement autoElementGet1(AutomationElement e, string s){Condition myCondition1 = new PropertyCondition(AutomationElement.AutomationIdProperty, s);Condition myCondition2 = new PropertyCondition(AutomationElement.NameProperty, s);Condition myCondition3 = new PropertyCondition(AutomationElement.AcceleratorKeyProperty, s);Condition myCondition4 = new PropertyCondition(AutomationElement.ClassNameProperty, s);Condition myCondition5 = new PropertyCondition(AutomationElement.AccessKeyProperty, s);Condition myCondition6 = new PropertyCondition(AutomationElement.LocalizedControlTypeProperty, s);Condition myCondition = new OrCondition(myCondition1, myCondition2, myCondition3, myCondition4, myCondition5,myCondition6);AutomationElementCollection myCollection = e.FindAll(TreeScope.Descendants, myCondition);return myCollection[0];}private void button11_Click(object sender, EventArgs e){//随机元素获取AutomationElement desktop = AutomationElement.RootElement;var calcFrame1 = desktop.FindFirst(TreeScope.Children,new PropertyCondition(AutomationElement.ClassNameProperty, "Notepad"));AutomationElement filename = autoElementGet1(calcFrame1, "文件(F)");MessageBox.Show("Name:" + filename.Current.Name + ";ClassName=" + filename.Current.ClassName);}private void button12_Click(object sender, EventArgs e){//遍历子控件AutomationElement desktop = AutomationElement.RootElement;var calcFrame1 = desktop.FindFirst(TreeScope.Children,new PropertyCondition(AutomationElement.ClassNameProperty, "Notepad"));Condition myCondition1 = new PropertyCondition(AutomationElement.AutomationIdProperty, "MenuBar");AutomationElement MenuBar = calcFrame1.FindFirst(TreeScope.Descendants, myCondition1);AutomationElementCollection MenuBarChildAll = MenuBar.FindAll(TreeScope.Children, Condition.TrueCondition);for (int i = 0; i < MenuBarChildAll.Count; i++){AutomationElement MenuBarChild = MenuBarChildAll[i];MessageBox.Show("Name:" + MenuBarChild.Current.Name + ";ClassName=" + MenuBarChild.Current.ClassName + ";ControlType=" + MenuBarChild.Current.ControlType);}}private void button13_Click(object sender, EventArgs e){processcalc = Process.Start(@"C:\Windows\System32\calc.exe");}private void button14_Click(object sender, EventArgs e){processcalc.Kill();}private void button15_Click(object sender, EventArgs e){//点击后有列表的按钮不要当成点击事件AutomationElement desktop = AutomationElement.RootElement;var calcFrame1 = desktop.FindFirst(TreeScope.Children,new PropertyCondition(AutomationElement.ClassNameProperty, "Notepad"));Condition myCondition1 = new PropertyCondition(AutomationElement.NameProperty, "文件(F)");AutomationElement w1 = calcFrame1.FindFirst(TreeScope.Descendants, myCondition1);ExpandCollapsePattern ecp = (ExpandCollapsePattern)w1.GetCurrentPattern(ExpandCollapsePattern.Pattern);ecp.Expand();Condition myCondition2 = new PropertyCondition(AutomationElement.NameProperty, "退出(X)");AutomationElementCollection myCollection = w1.FindAll(TreeScope.Descendants, myCondition2);AutomationElement w2 = myCollection[0];InvokePattern ipn = (InvokePattern)w2.GetCurrentPattern(InvokePattern.Pattern);ipn.Invoke();Thread.Sleep(500);AutomationElement savefile = calcFrame1.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "保存(S)"));if (savefile != null){InvokePattern savefilebutton = (InvokePattern)savefile.GetCurrentPattern(InvokePattern.Pattern);savefilebutton.Invoke();Thread.Sleep(500);AutomationElement Saveasform = calcFrame1.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "另存为"));AutomationElement edit1 = Saveasform.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.AutomationIdProperty, "1001"));ValuePattern vp = (ValuePattern)edit1.GetCurrentPattern(ValuePattern.Pattern);vp.SetValue("aaa.txt");AutomationElement savefilepath = Saveasform.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "保存(S)"));InvokePattern savefilepathbutton = (InvokePattern)savefilepath.GetCurrentPattern(InvokePattern.Pattern);savefilepathbutton.Invoke();Thread.Sleep(500);AutomationElement yesSaveasform = Saveasform.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "确认另存为"));AutomationElement issavefilepath = Saveasform.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "是(Y)"));if (issavefilepath != null) {InvokePattern issavefilepathbutton = (InvokePattern)issavefilepath.GetCurrentPattern(InvokePattern.Pattern);issavefilepathbutton.Invoke();}}}}
}

各按钮的功能展示

打开记事本

关闭记事本

获取计算器窗体

修改对应代码,每个按钮点击事件下的的该属性都需要进行修改

 结果

获取控件属性

 结果

点击按钮

完成计算器计算

 鼠标点击按钮

功能是让鼠标去点击,实现点击按钮的功能

 追加记事本内容

原始

运行

 重写记事本内容

展开按钮列表

随机获取元素方法

遍历记事本菜单栏

打开计算器

控件关闭计算器

 保存记事本

工程示例

https://download.csdn.net/download/qq_39397927/88215681

参考

https://www.cnblogs.com/baihuitestsoftware/articles/9047705.html

UI自动化 --- 微软UI Automation_dotNET跨平台的博客-CSDN博客

相关文章:

基于C#UI Automation自动化测试

步骤 UI Automation 只适用于&#xff0c;标准的win32和 WPF程序 需要添加对UIAutomationClient、 UIAutomationProvider、 UIAutomationTypes的引用 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.D…...

深入了解Linux运维的重要性与最佳实践

Linux作为开源操作系统的代表&#xff0c;在企业级环境中的应用越来越广泛。而在保障Linux系统的正常运行和管理方面&#xff0c;Linux运维显得尤为关键。本文将介绍Linux运维的重要性以及一些最佳实践&#xff0c;帮助读者更好地了解和掌握Linux系统的运维技巧。 首先&#xf…...

90 | Python人工智能篇 —— 深度学习算法 Keras基于卷积神经网络的情感分类

情感分类是自然语言处理(NLP)领域的一个重要任务,它旨在将文本划分为积极、消极或中性等不同情感类别。深度学习技术,尤其是卷积神经网络(CNN),在情感分类任务中取得了显著的成果。Keras作为一个高级的深度学习框架,提供了便捷易用的工具来构建和训练情感分类模型。 文…...

自然语言处理从入门到应用——LangChain:记忆(Memory)-[记忆的类型Ⅲ]

分类目录&#xff1a;《自然语言处理从入门到应用》总目录 对话令牌缓冲存储器ConversationTokenBufferMemory ConversationTokenBufferMemory在内存中保留了最近的一些对话交互&#xff0c;并使用标记长度来确定何时刷新交互&#xff0c;而不是交互数量。 from langchain.me…...

【ARM 嵌入式 编译系列 10.3 -- GNU elfutils 工具小结】

文章目录 什么是 GNU elfutils&#xff1f;GNU elfutils 常用工具有哪些&#xff1f;objcopy 常用参数有哪些&#xff1f;GNU binutils和GNU elfutils区别是什么&#xff1f; 上篇文章&#xff1a;ARM 嵌入式 编译系列 10.2 – 符号表与可执行程序分离详细讲解 什么是 GNU elfu…...

黑马项目一阶段面试 项目介绍篇

我完成了一个外卖项目&#xff0c;名叫苍穹外卖&#xff0c;是跟着黑马程序员的课程来自己动手写的。 项目基本实现了外卖客户端、商家端的后端完整业务。 商家端分为员工管理、文件上传、菜品管理、分类管理、套餐管理、店铺营业状态、订单下单派送等的管理、数据统计等&…...

重构内置类Function原型上的call方法

重构内置类Function原型上的call方法 // > 重构内置类Function原型上的call方法 ~(function () {/*** call: 改变函数中的this指向* params* context 可以不传递&#xff0c;传递必须是引用类型的值&#xff0c;因为后面要给它加 fn 属性**/function myCall(context) {/…...

Nginx之lnmp架构

目录 一.什么是LNMP二.LNMP环境搭建1.Nginx的搭建2.安装php3.安装数据库4.测试Nginx与PHP的连接5.测试PHP连接数据库 一.什么是LNMP LNMP是一套技术的组合&#xff0c;Llinux&#xff0c;Nnginx&#xff0c;Mmysql&#xff0c;Pphp 首先Nginx服务是不能处理动态资源请求&…...

C# 使用FFmpeg.Autogen对byte[]进行编解码

C# 使用FFmpeg.Autogen对byte[]进行编解码&#xff0c;参考&#xff1a;https://github.com/vanjoge/CSharpVideoDemo 入口调用类&#xff1a; using System; using System.IO; using System.Drawing; using System.Runtime.InteropServices; using FFmpeg.AutoGen;namespace F…...

websocket是多线程的嘛

经过测试, onOpen事件的threadId和onMessage的threadId是不一样的&#xff0c;但是onMessage的threadId一直是同一个&#xff0c;就是说收消息的部分是单线程的&#xff0c;收到第一个Message后如果给它sleep较长时间&#xff0c;期间收到第二个&#xff0c;效果是它在排队&am…...

CentOS7.9 禁用22端口,使用其他端口替代

文章目录 业务场景操作步骤修改sshd配置文件修改SELinux开放给ssh使用的端口修改防火墙&#xff0c;开放新端口重启sshd生效 相关知识点介绍sshd服务SELinux服务firewall.service服务 业务场景 我们在某市实施交通信控平台项目&#xff0c;我们申请了一台服务器&#xff0c;用…...

2023国赛 高教社杯数学建模ABCDE题思路汇总分析

文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 建模常见问题类型3.1 分类问题3.2 优化问题3.3 预测问题3.4 评价问题 4 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 竞赛信息 全国大学生数学建模…...

【网络层+数据链路层】深入理解IP协议和MAC帧协议的基本原理

文章目录 前言一、IP协议二、MAC帧协议 1.以太网2.以太网帧&#xff08;MAC帧&#xff09;格式报头3.基于协议讲解局域网转发的原理总结 前言 为什么经常将TCP/IP放在一起呢&#xff1f;这是因为IP层的核心工作就是通过IP地址来定位主机的&#xff0c;具有将一个数据报从A主机…...

银行家算法【学习算法】

银行家算法【学习算法】 前言版权推荐银行家算法7.避免死锁7.1 系统安全状态7.2 利用银行家算法避免死锁 Java算法实现代码结果 最后 前言 2023-8-14 18:18:01 以下内容源自《【学习算法】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台…...

萤石直播以及回放的接入和销毁

以下基于vue项目 1.安装 npm i ezuikit-js 2、导入 main.js中 import EZUIKit from "ezuikit-js"; //导入萤石Vue.use(EZUIKit); 3、创建容器 <div class"video"><div id"video-container"></div><!-- <iframe :src…...

C语言易错知识点总结2

函数 第 1 题&#xff08;单选题&#xff09; 题目名称&#xff1a; 能把函数处理结果的二个数据返回给主调函数&#xff0c;在下面的方法中不正确的是&#xff1a;&#xff08; &#xff09; 题目内容&#xff1a; A .return 这二个数 B .形参用数组 C .形参用二个指针 D .用…...

Go学习-Day1

Go学习-Day1 个人博客&#xff1a;CSDN博客 打卡。 Go语言的核心开发团队&#xff1a; Ken Thompson (C语言&#xff0c;B语言&#xff0c;Unix的发明者&#xff0c;牛人)Rob Pike(UTF-8发明人)Robert Griesemer(协助HotSpot编译器&#xff0c;Js引擎V8) Go语言有静态语言的…...

冠达管理:机构密集调研医药生物股 反腐政策影响受关注

进入8月&#xff0c;跟着反腐事件发酵&#xff0c;医药生物板块呈现震荡。与此一起&#xff0c;组织出资者对该板块上市公司也展开了密集调研。 到昨日&#xff0c;8月以来就有包含南微医学、百济神州、维力医疗、方盛制药等12家医药生物板块的上市公司接受组织调研&#xff0c…...

安装Tomac服务器——安装步骤以及易出现问题的解决方法

文章目录 前言 一、下载Tomcat及解压 1、选择下载版本&#xff08;本文选择tomcat 8版本为例&#xff09; 2、解压安装包 二、配置环境 1、在电脑搜索栏里面搜索环境变量即可 2、点击高级系统设置->环境变量->新建系统变量 1) 新建系统变量&#xff0c;变量名为…...

JVM 性能优化思路

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ 一般在系统出现问题的时候&#xff0c;我们会考虑对 JVM 进行性能优化。优化思路就是根据问题的情况&#xff0c;结合工具进行问题排查&#xff0c;针对排查出来的可能问题…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

Bean 作用域有哪些?如何答出技术深度?

导语&#xff1a; Spring 面试绕不开 Bean 的作用域问题&#xff0c;这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开&#xff0c;结合典型面试题及实战场景&#xff0c;帮你厘清重点&#xff0c;打破模板式回答&#xff0c…...

UE5 音效系统

一.音效管理 音乐一般都是WAV,创建一个背景音乐类SoudClass,一个音效类SoundClass。所有的音乐都分为这两个类。再创建一个总音乐类&#xff0c;将上述两个作为它的子类。 接着我们创建一个音乐混合类SoundMix&#xff0c;将上述三个类翻入其中&#xff0c;通过它管理每个音乐…...

如何把工业通信协议转换成http websocket

1.现状 工业通信协议多数工作在边缘设备上&#xff0c;比如&#xff1a;PLC、IOT盒子等。上层业务系统需要根据不同的工业协议做对应开发&#xff0c;当设备上用的是modbus从站时&#xff0c;采集设备数据需要开发modbus主站&#xff1b;当设备上用的是西门子PN协议时&#xf…...

Appium下载安装配置保姆教程(图文详解)

目录 一、Appium软件介绍 1.特点 2.工作原理 3.应用场景 二、环境准备 安装 Node.js 安装 Appium 安装 JDK 安装 Android SDK 安装Python及依赖包 三、安装教程 1.Node.js安装 1.1.下载Node 1.2.安装程序 1.3.配置npm仓储和缓存 1.4. 配置环境 1.5.测试Node.j…...

深入理解 React 样式方案

React 的样式方案较多,在应用开发初期,开发者需要根据项目业务具体情况选择对应样式方案。React 样式方案主要有: 1. 内联样式 2. module css 3. css in js 4. tailwind css 这些方案中,均有各自的优势和缺点。 1. 方案优劣势 1. 内联样式: 简单直观,适合动态样式和…...

深入解析 ReentrantLock:原理、公平锁与非公平锁的较量

ReentrantLock 是 Java 中 java.util.concurrent.locks 包下的一个重要类,用于实现线程同步,支持可重入性,并且可以选择公平锁或非公平锁的实现方式。下面将详细介绍 ReentrantLock 的实现原理以及公平锁和非公平锁的区别。 ReentrantLock 实现原理 基本架构 ReentrantLo…...