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")
然后直接调用 ChangeWindowMessageFilterEx,DragAcceptFiles,RevokeDragDrop等函数
解决方案详解
通过加载 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获取文件路径信息
这有点难以理解,注册后再取消掉,关键还生效了,实现了窗体的鼠标显示拖拽。
后面我改成先RevokeDragDrop 再 DragAcceptFiles也实现了鼠标显示拖拽。
示例是在 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();
}
值得注意的是:
qDebug() << winId() << this->window()->winId() << this->window()->effectiveWinId();
先打印一次WId,只要注释这一行代码,后面的DragAcceptFiles,ChangeWindowMessageFilterEx函数都会失败,完全不理解为啥,先记下来。RevokeDragDrop((HWND)this->window()->effectiveWinId());一定要注销窗体拖拽事件,否则获取拖拽事件失败。
在测试使用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管理员权限如何支持拖放操作? 的回答和代码示例。 解决在管理员权限运行下,通过窗体的QFrame子控件获取到拖拽的内容。 目录标题 导读解决方案详解示例详细 【管理员权限】在QFrame控件中获取拖拽内容 【管理员权限】继承 IDropTarget 类…...
[HNCTF 2022 WEEK4]flower plus
第一种花指令 第二种花指令 根据两种花指令特征,写出去花指令脚本 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字段分组,并对totalCustomerNum、refundCustomerNum字段 sum求和,同时取别名 Overridepublic List<AgentCountInfoBean> selectCurrentMonthNewResource(Set<String> orderTypeSet, List<String> agentIds,LocalDateTim…...
go语言后端开发学习(二)——基于七牛云实现的资源上传模块
前言 在之前的文章中我介绍过我们基于gin框架怎么实现本地上传图片和文本这类的文件资源(具体文章可以参考gin框架学习笔记(二) ——相关数据与文件的响应),但是在我们实际上的项目开发中一般却是不会使用本地上传资源的方式来上传的,因为文件的上传与读…...
探索微软新VLM Phi-3 Vision模型:详细分析与代码示例
引言 在最近的微软Build大会上,微软宣布了许多新内容,其中包括新款Copilot PC和围绕Copilot生态系统的一系列功能。其中最引人注目的是发布了一些新的Phi模型,特别是Phi-3 Vision模型。本文将详细探讨Phi-3 Vision模型的特性,并提…...
如何使用GPT-4o函数调用构建一个实时应用程序?
本教程介绍了如何使用OpenAI最新的LLM GPT-4o通过函数调用将实时数据引入LLM。 我们在LLM函数调用指南(详见https://thenewstack.io/a-comprehensive-guide-to-function-calling-in-llms/)中讨论了如何将实时数据引入聊天机器人和代理。现在,我们将通过将来自Fligh…...
[Vue-常见错误]浏览器显示Uncaught runtime errors
文章目录 错误描述正确写法具体如下 错误描述 当前端代码发生错误时,浏览器中出现以下错误提示。 正确写法 显然这不是我们所期望的,在vue.config.js中配置如下设置关闭Uncaught runtime errors显示 devServer: {client: {overlay: false}具体如下 …...
html常见的表单元素有哪些,html表单元素有哪些?
HTML中常用的表单元素包括:文本区域(TEXTAREA),列表框(SELECT),文本输入框(INPUT typetext),密码输入框(INPUT typepassword),单选输入框(INPUT typeradio),复选输入框(INPUT typecheckbox),重置…...
spring boot sso
代码:https://gitee.com/forgot940629/ssov2 授权服务 登录成功后,session中会存储UsernamePasswordAuthenticationToken,之后每次请求code时都会用UsernamePasswordAuthenticationToken生成OAuth2Authentication,并将OAuth2Aut…...
Keras深度学习框架实战(5):KerasNLP使用GPT2进行文本生成
1、KerasNLP与GPT2概述 KerasNLP的GPT2进行文本生成是一个基于深度学习的自然语言处理任务,它利用GPT-2模型来生成自然流畅的文本。以下是关于KerasNLP的GPT2进行文本生成的概述: GPT-2模型介绍: GPT-2(Generative Pre-trained …...
速盾:网站重生之我开了高防cdn
在互联网的广袤海洋中,网站就如同一个个独立的岛屿,面临着各种风雨和挑战。而作为一名专业程序员,我深知网站安全和性能的重要性。当我的网站遭遇频繁的攻击和访问压力时,我毅然决定开启高防 CDN,开启了一场网站的重生…...
【spark】spark列转行操作(json格式)
前言:一般我们列转行都是使用concat_ws函数或者concat函数,但是concat一般都是用于字符串的拼接,后续处理数据时并不方便。 需求:将两列数据按照设备id进行分组,每个设备有多个时间点位和对应值,将其一一对…...
记录一次Linux启动kafka后并配置了本地服务连接远程kafka的地址后依旧连接localhost的问题
问题的原因 我是使用docker来安装并启动kafka 的,所以在启动过程中并没有太多需要配置的地方,基本都是从网上照搬照抄,没动什么脑子,所以看着启动起来了觉得就没事了,但是运行项目的时候发现,我明明已经配…...
MacOS中Latex提示没有相关字体怎么办
在使用mactex编译中文的时候,遇到有些中文字体识别不到的情况,例如遇到识别不到Songti.ttc。其实这个时候字体是在系统里面的,但是只不过是latex没有找到正确的字体路径。 本文只针对于系统已经安装了字体库并且能够用find命令搜到࿰…...
物资材料管理系统建设方案(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 ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:[[3],[9,20],[15,7]] /*** Definition for…...
Vue3 + TS + Antd + Pinia 从零搭建后台系统(一) 脚手架搭建 + 入口配置
简易后台系统搭建开启,分几篇文章更新,本篇主要先搭架子,配置入口文件等目录 效果图一、搭建脚手架:二、处理package.json基础需要的依赖及运行脚本三、创建环境运行文件四、填充vue.config.ts配置文件五、配置vite-env.d.ts使项目…...
中国同胞进来看看,很多外国人想通过CSDN坑咱们中国人
地址:【诈骗离你我很近】中国同胞进来看看国外诈骗新套路。-CSDN博客...
Web前端电话咨询:深度解析与实用指南
Web前端电话咨询:深度解析与实用指南 在数字化时代,Web前端技术日新月异,对于许多企业和个人而言,通过电话咨询了解前端技术的最新动态和解决方案已成为一种高效且便捷的方式。本文将从四个方面、五个方面、六个方面和七个方面&a…...
使用python绘制季节图
使用python绘制季节图 季节图效果代码 季节图 季节图(Seasonal Plot)是一种数据可视化图表,用于展示时间序列数据的季节性变化。它通过将每个时间段(如每个月、每个季度)的数据绘制在同一张图表上,使得不同…...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
