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

QT案例 记录解决在管理员权限下QFrame控件获取拖拽到控件上的文件路径

参考知乎问答 Qt管理员权限如何支持拖放操作? 的回答和代码示例。
解决在管理员权限运行下,通过窗体的QFrame子控件获取到拖拽的内容。

目录标题

    • 导读
    • 解决方案详解
      • 示例详细 【管理员权限】
      • 在QFrame控件中获取拖拽内容 【管理员权限】
      • 继承 IDropTarget 类实现拖拽 【管理员权限下无效】
    • 测试源码

导读

在QT 程序中,非管理员运行的软件,正常的拖拽功能实现
只需要重写drag 拖拽事件,并且设置 setAcceptDrops(true);
就正常实现拖拽相关功能。

void dragEnterEvent(QDragEnterEvent *event) override;
void dragLeaveEvent(QDragLeaveEvent *event) override;
void dragMoveEvent(QDragMoveEvent *event) override;
void dropEvent(QDropEvent *event) override;'

但是程序一旦设置了管理员权限启动,就获取不到拖拽事件。
这一点在 知乎问答:Qt管理员权限如何支持拖放操作? 有相关说明。
而我也对 qt_uac_drag_demo 示例进行了简单的测试。
因为我直接使用的Msvc2017编译器,所以直接添加头文件

#include <Windows.h>
#include <WinUser.h>
#include <ole2.h>
#include <shellapi.h>
#pragma comment(lib,"user32.lib")
#pragma comment(lib,"Ole32.lib")

然后直接调用 ChangeWindowMessageFilterExDragAcceptFilesRevokeDragDrop等函数

解决方案详解

  • 示例详细 【管理员权限】

通过加载 qt_uac_drag_demo 项目,可以了解到示例主要
先通过DragAcceptFiles注册窗口接收拖拽事件,RevokeDragDrop取消掉注册已注册事件

void* user32 = LoadLibraryA("user32");
FARPROC func = GetProcAddress((HMODULE)user32, "ChangeWindowMessageFilter");
qDebug() << (*func)();
user32 = LoadLibraryA("user32");
func = GetProcAddress((HMODULE)user32, "ChangeWindowMessageFilter");
qDebug() << (*func)();
ChangeWindowMessageFilter(WM_DROPFILES, 1);
//    ChangeWindowMessageFilter(WM_COPYDATA, 1);
//    ChangeWindowMessageFilter(0x0049, 1);
qDebug() << w.winId() << w.effectiveWinId();
qDebug() << ChangeWindowMessageFilterEx((HWND)w.effectiveWinId(), WM_DROPFILES, MSGFLT_ALLOW, NULL);
qDebug() << ChangeWindowMessageFilterEx((HWND)w.effectiveWinId(), WM_COPYDATA, MSGFLT_ALLOW, NULL);
qDebug() << ChangeWindowMessageFilterEx((HWND)w.effectiveWinId(), 0x0049, MSGFLT_ALLOW, NULL);
DragAcceptFiles((HWND)w.effectiveWinId(), true);
qDebug() << GetLastError();
HRESULT res = RevokeDragDrop((HWND)w.winId());
qDebug() << "res:" <<res;

再重构的
bool nativeEvent(const QByteArray &eventType, void *message, long *result) 函数
然后通过 DragQueryFileW 获取文件/文件夹路径,🐂

  bool nativeEvent(const QByteArray &eventType, void *message, long *result){if (eventType == "windows_generic_MSG"){PMSG msg = (PMSG) message;if(msg->message == 563){qDebug() << msg->message << msg->hwnd << msg->wParam << msg->lParam << msg->time << msg->pt.x << msg->pt.y;UINT file_num = DragQueryFile((HDROP) msg->wParam, 0xFFFFFFFF, NULL, 0);qDebug() << "文件数量:" << file_num;for(int i=0;i<(int)file_num;i++){UINT file_name_size = DragQueryFile((HDROP) msg->wParam, i, NULL, 0);qDebug() << file_name_size;LPWSTR  fn = (LPWSTR)malloc(sizeof(WCHAR)*file_name_size);//! https://learn.microsoft.com/zh-cn/windows/win32/api/shellapi/nf-shellapi-dragqueryfilewUINT code = DragQueryFileW((HDROP) msg->wParam, i, fn, file_name_size);QString filename = QString::fromStdWString(fn);free(fn);qDebug() << "第" << i << "个文件:" << filename;qDebug() << "get name error:" << code;}qDebug() << eventType << message << *result;}}return QFrame::nativeEvent(eventType, message, result);}

一开始我是没有理解为什么要先调用
DragAcceptFiles((HWND)w.effectiveWinId(), true);
再调用
HRESULT res = RevokeDragDrop((HWND)w.winId());
直到我看到 评论下方的回答

以此看来,如果在Qt中简单的使用ChangeWindowMessageFilter,拖入文件时还是会显示禁止的标志,我参考了下其他非Qt的代码。
具体实现形式差不多是
1.是先使用RevokeDragDrop取消掉注册
2.DragAcceptFiles注册窗口接收拖拽事件
3.SetWindowLongA()设置窗口及事件回调函数
4.在事件回调中处理563消息(WM_DROPFILES)消息。
5.再使用DragQueryFileA获取文件路径信息

这有点难以理解,注册后再取消掉,关键还生效了,实现了窗体的鼠标显示拖拽。
后面我改成先RevokeDragDropDragAcceptFiles也实现了鼠标显示拖拽。


  • 在QFrame控件中获取拖拽内容 【管理员权限】

示例是在 MainWindow 窗体中获取的拖拽内容,而我需要限制某一个控件获取拖拽的内容,所以我在MainWindow 窗体中添加了一个QFrame 控件;
像这样:

在这里插入图片描述
这就需要重写QFrame 类
DragDrop_Frame.h
nativeEvent 函数和示例中一样,没有改动

class DragDrop_Frame:public QFrame
{
public:DragDrop_Frame(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());void dragEnterEvent(QDragEnterEvent *e) ;bool nativeEvent(const QByteArray &eventType, void *message, long *result){if (eventType == "windows_generic_MSG"){PMSG msg = (PMSG) message;if(msg->message == 563){qDebug() << msg->message << msg->hwnd << msg->wParam << msg->lParam << msg->time << msg->pt.x << msg->pt.y;UINT file_num = DragQueryFile((HDROP) msg->wParam, 0xFFFFFFFF, NULL, 0);qDebug() << "文件数量:" << file_num;for(int i=0;i<(int)file_num;i++){UINT file_name_size = DragQueryFile((HDROP) msg->wParam, i, NULL, 0);qDebug() << file_name_size;LPWSTR  fn = (LPWSTR)malloc(sizeof(WCHAR)*file_name_size);//! https://learn.microsoft.com/zh-cn/windows/win32/api/shellapi/nf-shellapi-dragqueryfilewUINT code = DragQueryFileW((HDROP) msg->wParam, i, fn, file_name_size);QString filename = QString::fromStdWString(fn);free(fn);qDebug() << "第" << i << "个文件:" << filename;qDebug() << "get name error:" << code;}qDebug() << eventType << message << *result;}}return QFrame::nativeEvent(eventType, message, result);}
};

DragDrop_Frame.cpp
为了让QFrame 能获取获取到拖拽事件,必须移除父窗体的注册,在重新添加父窗体和QFrame窗体的注册。再通过ChangeWindowMessageFilterEx 修改指定窗口 (UIPI) 消息筛选器的用户界面特权隔离。

#include "dragdrop_frame.h"
#include <QDebug>
#include "drophandler.h"
#include <QDragEnterEvent>
#include <QMimeData>DragDrop_Frame::DragDrop_Frame(QWidget* parent, Qt::WindowFlags f ):QFrame(parent,f)
{this->setAcceptDrops(true);setStyleSheet("QFrame { ""border-radius: 24px;""border: 2px dashed #676E89;""background-image: url(:/tuozhuai.png);""background-position: center;""background-repeat: no-repeat;""background-origin: content;"" }");//! ==========================================================//1.是先使用RevokeDragDrop取消掉注册qDebug() << winId()  << this->window()->winId() << this->window()->effectiveWinId();qDebug() <<RevokeDragDrop((HWND)this->window()->effectiveWinId());//2.DragAcceptFiles注册窗口接收拖拽事件//--3.SetWindowLongA()设置窗口及事件回调函数-- //不需要DragAcceptFiles((HWND)this->window()->effectiveWinId(), true);DragAcceptFiles((HWND)winId(), true);//在 nativeEvent(const QByteArray &eventType, void *message, long *result) 事件中实现//4.在事件回调中处理563消息(WM_DROPFILES)消息。//5.再使用DragQueryFileA获取文件路径信息qDebug() << ChangeWindowMessageFilterEx((HWND)winId(), WM_DROPFILES, MSGFLT_ALLOW, NULL);qDebug() << ChangeWindowMessageFilterEx((HWND)winId(), WM_COPYDATA, MSGFLT_ALLOW, NULL);qDebug() << ChangeWindowMessageFilterEx((HWND)winId(), 0x0049, MSGFLT_ALLOW, NULL);
}void DragDrop_Frame::dragEnterEvent(QDragEnterEvent *e){qDebug()<<"[mimeData] "<< e->mimeData()->urls().count();
}

值得注意的是:

  1. qDebug() << winId() << this->window()->winId() << this->window()->effectiveWinId();
    先打印一次WId,只要注释这一行代码,后面的DragAcceptFiles,ChangeWindowMessageFilterEx函数都会失败,完全不理解为啥,先记下来。
  2. RevokeDragDrop((HWND)this->window()->effectiveWinId()); 一定要注销窗体拖拽事件,否则获取拖拽事件失败。

  • 继承 IDropTarget 类实现拖拽 【管理员权限下无效】

在测试使用RevokeDragDrop 函数的时候,我找到了 RegisterDragDrop 函数,
我就想着注销掉系统默认的拖拽事件,在重新添加一个拖拽事件是不是就能解决掉被屏蔽的拖拽事件?
于是我从Giehub上借鉴了一份 IDropTarget 类的实现:
drophandler.h


#include <Windows.h>
#include <WinUser.h>
#include <ole2.h>
#include <shellapi.h>
#include <functional>
#include <vector>#pragma comment(lib,"user32.lib")
#pragma comment(lib,"Ole32.lib")/*!
* 从github 移植的阉割版的 IDropTarget 实现
* https://github.com/WinMerge/winmerge/blob/f62b2e5b8b3e9d415045426e276dfd4d2ed271e6/Src/DropHandler.h
*/class DropHandler: public IDropTarget
{
public:HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject);ULONG STDMETHODCALLTYPE AddRef();ULONG STDMETHODCALLTYPE Release();HRESULT STDMETHODCALLTYPE DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);HRESULT STDMETHODCALLTYPE DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);HRESULT STDMETHODCALLTYPE DragLeave(void);HRESULT STDMETHODCALLTYPE Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);explicit DropHandler(std::function<void(const std::vector<std::string>&)> callback);~DropHandler();std::function<void(const std::vector<std::string>&)> GetCallback() const { return m_callback; };private:LONG m_cRef;std::function<void(const std::vector<std::string>&)> m_callback;
};

drophandler.cpp

#include "drophandler.h"
#include <QDebug>DropHandler::DropHandler(std::function<void(const std::vector<std::string>&)> callback): m_cRef(0), m_callback(callback)
{qDebug()<<"[DropHandler] -->";
}DropHandler::~DropHandler(){}HRESULT STDMETHODCALLTYPE DropHandler::QueryInterface(REFIID riid, void **ppvObject)
{qDebug()<<"[QueryInterface] -->";if (!IsEqualIID(riid, IID_IUnknown) && !IsEqualIID(riid, IID_IDropTarget)){*ppvObject = nullptr;return E_NOINTERFACE;}*ppvObject = static_cast<IDropTarget *>(this);AddRef();return S_OK;
}ULONG STDMETHODCALLTYPE DropHandler::AddRef(void)
{qDebug()<<"[AddRef] -->";return InterlockedIncrement(&m_cRef);
}ULONG STDMETHODCALLTYPE DropHandler::Release(void)
{qDebug()<<"[Release] -->";ULONG cRef = InterlockedDecrement(&m_cRef);if (cRef == 0) {delete this;return 0;}return cRef;
}HRESULT STDMETHODCALLTYPE DropHandler::DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
{qDebug()<<"[DragEnter] -->";return S_OK;
}HRESULT STDMETHODCALLTYPE DropHandler::DragOver(DWORD, POINTL, DWORD *)
{qDebug()<<"[DragOver] -->";return S_OK;
}HRESULT STDMETHODCALLTYPE DropHandler::DragLeave(void)
{qDebug()<<"[DragLeave] -->";return S_OK;
}HRESULT DropHandler::Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
{qDebug()<<"[Drop] -->";return S_OK;
}

在通过RegisterDragDrop 注册

 qDebug() << winId()  << this->window()->winId() << this->window()->effectiveWinId();HRESULT res = RevokeDragDrop((HWND)this->window()->effectiveWinId());DragAcceptFiles((HWND)this->window()->effectiveWinId(), true);DragAcceptFiles((HWND)winId(), true);qDebug() << RegisterDragDrop((HWND)winId(),new DropHandler(NULL));

结果发现,父窗体可以拖拽,QFrame控件反而被禁止拖拽了、、、、
而在没有管理员权限的情况下直接注册:
RegisterDragDrop((HWND)winId(),new DropHandler(NULL));
就能在拖拽事件中执行在DropHandler类中的DragEnter,DragLeave等函数…
但是,在没有管理员权限的情况下显然用自带的dragEnterEvent等函数更合适…
所以,继承 IDropTarget 类实现拖拽 没多大用,仅供参考…


测试源码

完整测试源码已上传Github :
https://github.com/MliesMoT/Qt_Administrator_Drop_Text
在这里插入图片描述

相关文章:

QT案例 记录解决在管理员权限下QFrame控件获取拖拽到控件上的文件路径

参考知乎问答 Qt管理员权限如何支持拖放操作&#xff1f; 的回答和代码示例。 解决在管理员权限运行下&#xff0c;通过窗体的QFrame子控件获取到拖拽的内容。 目录标题 导读解决方案详解示例详细 【管理员权限】在QFrame控件中获取拖拽内容 【管理员权限】继承 IDropTarget 类…...

[HNCTF 2022 WEEK4]flower plus

第一种花指令 第二种花指令 根据两种花指令特征&#xff0c;写出去花指令脚本 saddr0x401000 eaddr0x435000 for i in range(saddr,eaddr):if get_wide_dword(i)0x01740275:print(hex(i),hex(get_wide_dword(i)))patch_byte(i-5,0x90)patch_dword(i-4,0x90909090)patch_dw…...

Mongo常用语法(java代码)

1、根据agentId字段分组&#xff0c;并对totalCustomerNum、refundCustomerNum字段 sum求和&#xff0c;同时取别名 Overridepublic List<AgentCountInfoBean> selectCurrentMonthNewResource(Set<String> orderTypeSet, List<String> agentIds,LocalDateTim…...

go语言后端开发学习(二)——基于七牛云实现的资源上传模块

前言 在之前的文章中我介绍过我们基于gin框架怎么实现本地上传图片和文本这类的文件资源(具体文章可以参考gin框架学习笔记(二) ——相关数据与文件的响应)&#xff0c;但是在我们实际上的项目开发中一般却是不会使用本地上传资源的方式来上传的&#xff0c;因为文件的上传与读…...

探索微软新VLM Phi-3 Vision模型:详细分析与代码示例

引言 在最近的微软Build大会上&#xff0c;微软宣布了许多新内容&#xff0c;其中包括新款Copilot PC和围绕Copilot生态系统的一系列功能。其中最引人注目的是发布了一些新的Phi模型&#xff0c;特别是Phi-3 Vision模型。本文将详细探讨Phi-3 Vision模型的特性&#xff0c;并提…...

如何使用GPT-4o函数调用构建一个实时应用程序?

本教程介绍了如何使用OpenAI最新的LLM GPT-4o通过函数调用将实时数据引入LLM。 我们在LLM函数调用指南(详见https://thenewstack.io/a-comprehensive-guide-to-function-calling-in-llms/)中讨论了如何将实时数据引入聊天机器人和代理。现在&#xff0c;我们将通过将来自Fligh…...

[Vue-常见错误]浏览器显示Uncaught runtime errors

文章目录 错误描述正确写法具体如下 错误描述 当前端代码发生错误时&#xff0c;浏览器中出现以下错误提示。 正确写法 显然这不是我们所期望的&#xff0c;在vue.config.js中配置如下设置关闭Uncaught runtime errors显示 devServer: {client: {overlay: false}具体如下 …...

html常见的表单元素有哪些,html表单元素有哪些?

HTML中常用的表单元素包括&#xff1a;文本区域(TEXTAREA)&#xff0c;列表框(SELECT)&#xff0c;文本输入框(INPUT typetext)&#xff0c;密码输入框(INPUT typepassword)&#xff0c;单选输入框(INPUT typeradio)&#xff0c;复选输入框(INPUT typecheckbox)&#xff0c;重置…...

spring boot sso

代码&#xff1a;https://gitee.com/forgot940629/ssov2 授权服务 登录成功后&#xff0c;session中会存储UsernamePasswordAuthenticationToken&#xff0c;之后每次请求code时都会用UsernamePasswordAuthenticationToken生成OAuth2Authentication&#xff0c;并将OAuth2Aut…...

Keras深度学习框架实战(5):KerasNLP使用GPT2进行文本生成

1、KerasNLP与GPT2概述 KerasNLP的GPT2进行文本生成是一个基于深度学习的自然语言处理任务&#xff0c;它利用GPT-2模型来生成自然流畅的文本。以下是关于KerasNLP的GPT2进行文本生成的概述&#xff1a; GPT-2模型介绍&#xff1a; GPT-2&#xff08;Generative Pre-trained …...

速盾:网站重生之我开了高防cdn

在互联网的广袤海洋中&#xff0c;网站就如同一个个独立的岛屿&#xff0c;面临着各种风雨和挑战。而作为一名专业程序员&#xff0c;我深知网站安全和性能的重要性。当我的网站遭遇频繁的攻击和访问压力时&#xff0c;我毅然决定开启高防 CDN&#xff0c;开启了一场网站的重生…...

【spark】spark列转行操作(json格式)

前言&#xff1a;一般我们列转行都是使用concat_ws函数或者concat函数&#xff0c;但是concat一般都是用于字符串的拼接&#xff0c;后续处理数据时并不方便。 需求&#xff1a;将两列数据按照设备id进行分组&#xff0c;每个设备有多个时间点位和对应值&#xff0c;将其一一对…...

记录一次Linux启动kafka后并配置了本地服务连接远程kafka的地址后依旧连接localhost的问题

问题的原因 我是使用docker来安装并启动kafka 的&#xff0c;所以在启动过程中并没有太多需要配置的地方&#xff0c;基本都是从网上照搬照抄&#xff0c;没动什么脑子&#xff0c;所以看着启动起来了觉得就没事了&#xff0c;但是运行项目的时候发现&#xff0c;我明明已经配…...

MacOS中Latex提示没有相关字体怎么办

在使用mactex编译中文的时候&#xff0c;遇到有些中文字体识别不到的情况&#xff0c;例如遇到识别不到Songti.ttc。其实这个时候字体是在系统里面的&#xff0c;但是只不过是latex没有找到正确的字体路径。 本文只针对于系统已经安装了字体库并且能够用find命令搜到&#xff0…...

物资材料管理系统建设方案(Word)—实际项目方案

二、 项目概述 2.1 项目背景 2.2 现状分析 2.2.1 业务现状 2.2.2 系统现状 三、 总体需求 3.1 系统范围 3.2 系统功能 3.3 用户分析 3.4 假设与依赖关系 四、 功能需求 4.4.11.7 非功能性需求 五、 非功能性需求 5.1 用户界面需求 5.2 软硬件环境需求 5.3 产品质量需求 5.4 接口…...

!力扣102. 二叉树的层序遍历

给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]] /*** Definition for…...

Vue3 + TS + Antd + Pinia 从零搭建后台系统(一) 脚手架搭建 + 入口配置

简易后台系统搭建开启&#xff0c;分几篇文章更新&#xff0c;本篇主要先搭架子&#xff0c;配置入口文件等目录 效果图一、搭建脚手架&#xff1a;二、处理package.json基础需要的依赖及运行脚本三、创建环境运行文件四、填充vue.config.ts配置文件五、配置vite-env.d.ts使项目…...

中国同胞进来看看,很多外国人想通过CSDN坑咱们中国人

地址&#xff1a;【诈骗离你我很近】中国同胞进来看看国外诈骗新套路。-CSDN博客...

Web前端电话咨询:深度解析与实用指南

Web前端电话咨询&#xff1a;深度解析与实用指南 在数字化时代&#xff0c;Web前端技术日新月异&#xff0c;对于许多企业和个人而言&#xff0c;通过电话咨询了解前端技术的最新动态和解决方案已成为一种高效且便捷的方式。本文将从四个方面、五个方面、六个方面和七个方面&a…...

使用python绘制季节图

使用python绘制季节图 季节图效果代码 季节图 季节图&#xff08;Seasonal Plot&#xff09;是一种数据可视化图表&#xff0c;用于展示时间序列数据的季节性变化。它通过将每个时间段&#xff08;如每个月、每个季度&#xff09;的数据绘制在同一张图表上&#xff0c;使得不同…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

听写流程自动化实践,轻量级教育辅助

随着智能教育工具的发展&#xff0c;越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式&#xff0c;也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建&#xff0c;…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而&#xff0c;传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案&#xff0c;能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...

CSS | transition 和 transform的用处和区别

省流总结&#xff1a; transform用于变换/变形&#xff0c;transition是动画控制器 transform 用来对元素进行变形&#xff0c;常见的操作如下&#xff0c;它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

NPOI Excel用OLE对象的形式插入文件附件以及插入图片

static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...

Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合

作者&#xff1a;来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布&#xff0c;Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明&#xff0c;Elastic 作为 …...