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
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/341392.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习笔记:2.Jupyter Notebook

Jupyter Notebook 常用操作快捷键魔法指令_jupyter notebook快捷键调用函数-CSDN博客https://blog.csdn.net/qq_26917905/article/details/137211336?ops_request_misc%257B%2522request%255Fid%2522%253A%2522171748112816800182160793%2522%252C%2522scm%2522%253A%25222014…

CS4344国产替代音频DAC数模转换芯片DP7344采样率192kHz

目录 DAC应用简介DP7344简介结构框图DP7344主要特性微信号&#xff1a;dnsj5343参考原理图 应用领域 DAC应用简介 DAC&#xff08;中文&#xff1a;数字模拟转换器&#xff09;是一种将数字信号转换为模拟信号&#xff08;以电流、电压或电荷的形式&#xff09;的设备。电脑对…

废品回收小程序怎么做?有哪些核心功能?

废品回收行业正逐步走向高质量发展的道路。在国家政策的推动下&#xff0c;再生资源市场需求旺盛&#xff0c;行业内部竞争格局逐渐明朗。 随着互联网技术的发展&#xff0c;"互联网回收"成为废品回收行业的一个新趋势。通过微信小程序这种线上平台&#xff0c;用户…

甲方的苛刻,是成就优质作品的必要条件,辩证看待。

取其上、得其中&#xff0c;取其中&#xff0c;得其下&#xff0c;取其下、则无所的。在进行B端界面的设计的时候&#xff0c;设计师除了自我加压外&#xff0c;还少不了客户的严格要求&#xff0c;贝格前端工场为大家辩证分析一下。 一、严格产出高品质作品 甲方提出苛刻的要…

自然语言处理(NLP)—— C-value方法

自然语言处理&#xff08;NLP&#xff09;和文本挖掘是计算机科学与语言学的交叉领域&#xff0c;旨在通过计算机程序来理解、解析和生成人类语言&#xff0c;以及从大量文本数据中提取有用的信息和知识。这些技术在现代数据驱动的世界中扮演着关键角色&#xff0c;帮助我们从海…

数据结构:单调栈

数据结构&#xff1a;单调栈 题目描述参考代码 题目描述 输入样例 5 3 4 2 7 5输出样例 -1 3 -1 2 2参考代码 #include <iostream>using namespace std;const int N 100010;int stk[N], top; int n, x;int main() {cin >> n;while (n--){cin >> x;while …

Redis 异常三连环

本文针对一种特殊情况下的Reids连环异常&#xff0c;分别是下面三种异常&#xff1a; NullPointerException: Cannot read the array length because “arg” is nullJedisDataException: ERR Protocol error: invalid bulk lengthJedisConnectionException: Unexpected end o…

c++ - list常用接口模拟实现

文章目录 一、模拟list类的框架二、函数接口实现1、迭代器接口2、常用删除、插入接口3、常用其他的一些函数接口4、默认成员函数 一、模拟list类的框架 1、使用带哨兵的双向链表实现。 2、链表结点&#xff1a; // List的结点类 template<class T> struct ListNode {Li…

Docker之路(三)docker安装nginx实现对springboot项目的负载均衡

Docker之路&#xff08;三&#xff09;dockernginxspringboot负载均衡 前言&#xff1a;一、安装docker二、安装nginx三、准备好我们的springboot项目四、将springboot项目分别build成docker镜像五、配置nginx并且启动六、nginx的负载均衡策略七、nginx的常用属性八、总结 前言…

Android WebView上传文件/自定义弹窗技术,附件的解决方案

安卓内核开发 其实是Android的webview默认是不支持<input type"file"/>文件上传的。现在的前端页面需要处理的是&#xff1a; 权限 文件路径AndroidManifest.xml <uses-permission android:name"android.permission.WRITE_EXTERNAL_STORAGE"/&g…

计算机网络ppt和课后题总结(上)

试在下列条件下比较电路交换和分组交换。要传送的报文共 x(bit)。从源点到终点共经过 k 段链路&#xff0c;每段链路的传播时延为 d(s)&#xff0c;数据率为 b(b/s)。在电路交换时电路的建立时间为 s(s)。在分组交换时分组长度为 p(bit)&#xff0c;且各结点的排队等待时间可忽…

数据觉醒时代,以“存力”激活数据资产潜能

近日&#xff0c;质汇“杨数浦”主题研讨会首场活动在杨浦滨江举行&#xff0c;是杨浦区筹推进数字经济与城市数字化发展的一大重要举措&#xff0c;各行业协会、科研院所及企业代表参加活动&#xff0c;共商行业发展新机遇。活动现场&#xff0c;优刻得董事长兼CEO季昕华被授予…

Mysql的两种安装方式

文章目录 第一种安装方式国内镜像库下载解压安装配置环境变量初始化数据库安装mysql登录mysql设置root密码退出登录假如忘记了密码&#xff0c;重置密码的步骤1、步骤一&#xff1a;停止 MySQL 服务2、步骤二&#xff1a;使用安全模式启动 MySQL3、步骤三&#xff1a;重置密码4…

大数据之Schedule调度错误(一)

当我们在利用ooize发起整个任务的调度过程中,如果多个调度任务同时运行并且多个调度任务操作了相同的表,那么就会出现如下的错误关系: Invalid path hdfs://iZh5w01l7f8lnog055cpXXX:8000/user/admin/xxx: No files matching path hdfs://iZh5w01l7f8lnog055cpXXX:8000/user/ad…

线性表、单循环链表学习

背景&#xff1a; 单循环链表是一种链表结构&#xff0c;其中最后一个节点指向第一个节点&#xff0c;从而形成一个环。 实现单循环链表通常涉及节点定义、插入节点、删除节点以及遍历链表等操作。以下是如何在Python中实现单循环链表的示例。 单循环链表的实现 1. 节点类 …

掌握ChatGPT的正确打开方式

引言 随着人工智能技术的飞速发展&#xff0c;自然语言处理&#xff08;NLP&#xff09;领域取得了显著的突破。其中&#xff0c;聊天生成预训练变换器&#xff08;ChatGPT&#xff09;作为一种新型的对话式AI模型&#xff0c;引起了广泛关注。本文将详细介绍ChatGPT的正确使用…

使用html2canvas和jspdf导出pdf包含跨页以及页脚

首先要下载两个文件&#xff0c;一个为html2canvas.min.js&#xff0c;另一个是jspdf.umd.min.js这两个文件分别下载的地址我也附录上&#xff0c;都在官网git&#xff1a; html2canvas.min.js: https://html2canvas.hertzen.com/dist/html2canvas.min.js jspdf.umd.min.js: …

vue-pdf 部分中文显示错误,第二次打开是空白,解决方法

首先鸣谢 1. https://blog.csdn.net/m0_71537867/article/details/131614868?spm1001.2014.3001.5506 2. https://blog.csdn.net/weixin_43763952/article/details/133769647 3. https://github.com/FranckFreiburger/vue-pdf/issues/229 4. https://blog.csdn.net/weixin_449…

康谋技术 | 自动驾驶:揭秘高精度时间同步技术(一)

众所周知&#xff0c;在自动驾驶中&#xff0c;主要涵盖感知、规划、控制三个关键的技术层面。在感知层面&#xff0c;单一传感器采集外界信息&#xff0c;各有优劣&#xff0c;比如摄像头采集信息分辨率高&#xff0c;但是受外界条件影响较大&#xff0c;一般缺少深度信息&…

推荐一个免费的相亲工具

推荐一个免费的相亲工具&#xff0c;步骤如下&#xff1a; 1&#xff09;微信里面搜索公众号“光源桥”&#xff0c;并关注 2&#xff09;输入搜索条件进行搜索对象 例如下面搜索&#xff1a;