Re0: 从零实现一个置顶任意窗口的小工具

前言

话不多说,先上效果:

动画

这里展示的是通过下拉框选择窗口,让窗口显示并置顶,其实还可以直接通过快捷键(先鼠标点击要置顶的窗口,再使用CTRL+SHIFT+T),本文涉及到的完整代码已上传到GitHub,也可以选择直接下载exe(35k)体验。

实现

实现显示并置顶功能

其实置顶的核心功能就是通过SetWindowPos函数实现的:

// 置顶窗口
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);// 取消置顶
SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 100, 100, SWP_NOMOVE | SWP_NOSIZE);

不过如果只使用该函数,无法满足窗口未处于前台(例如最小化状态)无法显示在前台的情况,需要手动让窗口处于前台才能满足最终效果,因此还需要结合SetForegroundWindow函数使用,但是由于官网中提到的如下限制:

image-20231129160938483

因此还需要使用AttachThreadInput函数进行处理,最终的函数如下:

// 展示并置顶窗口
void topwin(HWND hWnd)
{// 通过快捷键方式不需要传递窗口句柄if (hWnd == nullptr) {hWnd = GetForegroundWindow();}DWORD currentId = GetCurrentThreadId();DWORD topId = GetWindowThreadProcessId(GetForegroundWindow(), NULL);AttachThreadInput(currentId, topId, TRUE);// 展示并置顶窗口ShowWindow(hWnd, SW_SHOWNORMAL);SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);SetForegroundWindow(hWnd);AttachThreadInput(currentId, topId, FALSE);
}

快捷键方式实现

讲完置顶功能后,再说一下如何将功能和快捷键进行关联。

首先,需要注册快捷键(CTRL+SHIFT+T用于置顶,CTRL+SHIFT+C用于取消置顶):

int topHotkeyId = 1;
int untopHotkeyId = 2;// 注册全局快捷键
if (!RegisterHotKey(NULL, topHotkeyId, MOD_CONTROL | MOD_SHIFT, 'T')) {return 1;
}if (!RegisterHotKey(NULL, untopHotkeyId, MOD_CONTROL | MOD_SHIFT, 'C')) {return 1;
}

然后就可以在消息循环中处理快捷键事件:

// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)){TranslateMessage(&msg);DispatchMessage(&msg);if (msg.message == WM_HOTKEY) {HWND hWnd = GetForegroundWindow();if (msg.wParam == topHotkeyId) {topwin(nullptr);}if (msg.wParam == untopHotkeyId) {SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 100, 100, SWP_NOMOVE | SWP_NOSIZE);}}}
}

WM_HOTKEY 的处理需要放在 DispatchMessage(&msg); 之后,否则可能出现打包后的 exe 无法正常运行。在使用快捷键方式置顶窗口时,需要先选中指定窗口使其获取焦点。

下拉列表方式实现

最后再讲一下如何实现下拉列表选择窗口进行置顶。

这里先展示获取窗口列表的具体实现:

std::vector<HWND> windows;// 判断窗口是否在桌面正常显示
bool IsWindowOnDesktop(HWND hwnd) {WINDOWPLACEMENT placement = { sizeof(WINDOWPLACEMENT) };if (GetWindowPlacement(hwnd, &placement)) {return placement.showCmd == SW_SHOWNORMAL;}return false;
}// 遍历窗口并获取窗口标题展示在下拉框中
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {if (IsWindowVisible(hwnd) && !IsWindowOnDesktop(hwnd)) {int titleLength = GetWindowTextLength(hwnd);if (titleLength > 0) {std::wstring windowTitle(titleLength + 1, L'\0');GetWindowText(hwnd, &windowTitle[0], titleLength + 1);// 将窗口标题加入到下拉列表中SendMessageW(hComboBox, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(windowTitle.c_str()));windows.push_back(hwnd);}}return TRUE;
}// 遍历窗口
EnumWindows(EnumWindowsProc, 0);

结合代码可以直观的知道就是先进行窗口遍历,获取符合条件的窗口(窗口可见且是在桌面正常显示的窗口)标题。此外通过windows.push_back(hwnd);保存窗口句柄,用于后续选中条件的处理。

实现了获取窗口标题列表的功能后,再来说明一下如何将数据填充到下拉列表中并处理选择事件:

#define MENU_ID 29HWND hComboBox;
// 创建下拉框
hComboBox = CreateWindowW(L"ComboBox", L"", CBS_DROPDOWN | WS_CHILD | WS_VISIBLE,10, 10, 360, 200, hWnd, (HMENU)MENU_ID, nullptr, nullptr);// 设置默认选中项
SendMessageW(hComboBox, CB_SETCURSEL, 0, 0);case WM_COMMAND:{// 处理下拉列表选中事件if (HIWORD(wParam) == CBN_SELCHANGE) {LRESULT selectedIndex = SendMessageW(GetDlgItem(hWnd, MENU_ID), CB_GETCURSEL, 0, 0);topwin(windows.at(selectedIndex));}}break;

通过代码不难理解,就是创建一个下拉框组件,并设置唯一标识MENU_ID,之后处理相应的下拉选择事件即可,窗口句柄通过windows.at(selectedIndex)进行获取。

这里暂时未实现取消置顶的方式,可以使用CTRL+SHIFT+C快捷键进行取消置顶的操作。

总结

本文简单实现了一个置顶任意窗口的小工具,实现思路很简单,还只算是一个半成品,不过核心功能已经有了,只需要自己额外进行一些扩展就足够日常使用,欢迎一起交流讨论。

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

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

相关文章

【JavaEE初阶】 HTTP 请求 (Request)详解

文章目录 &#x1f340;序言&#x1f384;认识URL&#x1f6a9;URL 基本格式&#x1f6a9;query string&#x1f6a9;关于 URL encode &#x1f334;认识 "方法" (method)&#x1f6a9;GET方法&#x1f6a9;POST 方法&#x1f6a9; GET 和 POST 的区别 &#x1f38b;…

Java后端开发——JDBC(万字详解)

Java后端开发——JDBC&#xff08;万字详解&#xff09; 今日目标 掌握JDBC的的CRUD理解JDBC中各个对象的作用掌握Druid的使用 1&#xff0c;JDBC概述 在开发中我们使用的是java语言&#xff0c;那么势必要通过java语言操作数据库中的数据。这就是接下来要学习的JDBC。 1.1 …

【Axure高保真原型】区间评分条

今天和大家分享区间评分条的原型模板&#xff0c;我们可以左右拖动移动滑块到指定位置&#xff0c;评分条上方会根据两个滑块所在的位置&#xff0c;自动计算出对应的区间范围&#xff1b;这个原型模板使用也很简单&#xff0c;只需要在里面填写区间的最大值&#xff0c;即可自…

盛最多水的容器-中等

leetcode链接 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明&#xff1a;你不能倾…

JS之Object.defineProperty方法

给对象添加属性的方法有许多&#xff0c;这次让我为大家介绍一种给对象添加属性的静态方法吧&#xff01; 语法&#xff1a;Objcet.defineProperty(对象的名称&#xff0c;“添加的键名”&#xff0c;{value&#xff1a;键值}) const obj {name:"张三",age:18}// 我…

Typora .MD笔记中本地图片批量上传到csdn (.PNG格式)(无需其他任何图床软件)

Typora .MD笔记中本地图片批量上传到csdn &#xff08;.PNG格式&#xff09;(无需其他任何图床软件) 截图软件推荐 qq 截图 快捷键 ctrlshiftA. 步骤一 设置Typora 的图片 点击文件. 点击偏好设置 ->图像 我们可以选择将图片复制到我们的文件夹中。 建议刚写好文件标题就…

C#键盘钩子(Hook)拦截器的使用

引言 键盘钩子(Hook)是一种机制&#xff0c;允许程序捕获和处理操作系统中的键盘输入。在C#中&#xff0c;我们可以使用键盘钩子来创建一个拦截器&#xff0c;用于拦截特定的键盘事件并执行自定义操作。本文将介绍如何使用C#开发一个键盘钩子拦截器&#xff0c;并给出一些示例代…

算法中的时间复杂度,空间复杂度

一、前言 算法&#xff08;Algorithm&#xff09;是指用来操作数据、解决程序问题的一组方法。对于同一个问题&#xff0c;使用不同的算法&#xff0c;也许最终得到的结果是一样的&#xff0c;但在过程中消耗的资源和时间却会有很大的区别 衡量不同算法之间的优劣主要是通过时…

Java-多线程基本知识学习总结

多线程 前言一、线程的创建1、继承Thread类2、实现Runnable接口 二、线程的生命周期三、操作线程的方法1、线程的休眠2、线程的加入3、线程的礼让4、线程的优先级 四、线程同步End 前言 Java是支持多线程的编程语言&#xff0c;所谓多线程就是程序能够同时完成多种操作。 计算…

Windows挂载NFS

ubuntu开启nfs 安装 sudo apt install nfs-kernel-server编辑 /etc/exports /data/share *(rw,no_root_squash)重启服务 sudo systemctl restart nfs-server.service验证 showmount -e localhostwindows连接NFS 选择控制面板 > 程序 > 启用或关闭 Windows 功能 添加…

理解射频中常用的史密斯圆图(Smith Chart)

理解射频中常用的史密斯圆图&#xff08;Smith Chart&#xff09; 工程中常用史密斯圆图表示射频器件端口的回波损耗 Γ \Gamma Γ. 回波损耗 回波损耗&#xff0c;又称器件端口的反射系数&#xff0c;反映了器件的端口阻抗 Z_{1L} 与传输线阻抗 Z 0 Z_0 Z0​ 之间的匹配&…

入耳耳机对耳朵有损害吗?戴哪种耳机不伤耳朵听力?

长时间佩戴入耳式耳机会对耳朵造成伤害的&#xff0c;建议佩戴骨传导耳机&#xff0c;不入耳佩戴使用更健康。 为什么推荐使用骨传导耳机&#xff0c;首先因为入耳式耳机需要塞入耳道内佩戴&#xff0c;长时间使用会使耳道内滋生细菌&#xff0c;容易引发耳道疾病&#xff0c;…

Python爬虫入门:如何设置代理IP进行网络爬取

目录 前言 一、获取代理IP 1.1 获取免费代理IP 1.2 验证代理IP 二、设置代理IP 三、使用代理IP进行网络爬取 四、总结 前言 在进行网络爬取时&#xff0c;经常会遇到一些反爬虫的措施&#xff0c;比如IP封锁、限制访问频率等。为了解决这些问题&#xff0c;我们可以使用…

软件测评中心▏软件集成测试和功能测试之间的区别和联系简析

软件集成测试是在软件开发周期的后期阶段进行的测试活动&#xff0c;旨在验证系统各个组件之间的接口和交互是否正常工作。而功能测试是一种验证软件系统是否按照需求规格说明书所规定的功能进行正确实现的测试。接下来&#xff0c;我们来分别探讨一下软件集成测试和功能测试有…

windows系统用nginx部署web应用

要在Windows系统上使用Nginx进行本地部署和运行Web应用程序&#xff0c;可以按照以下步骤进行操作&#xff1a; 1.首先下载nginx&#xff0c;需要去nginx官网&#xff1a; nginx: download 下载最新版本的&#xff1a; 2.解压缩Nginx&#xff1a;找个磁盘位置&#xff0c;新…

Vue3生命周期函数(简述题)

1.图示 2.说明 3.补充 1.在vue3组合式API中&#xff0c;我们需要将生命周期函数先导入&#xff0c;然后才能使用。 import {onMounted} from vue2.beforeCreate和created被setup()方法所代替

re:Invent 构建未来:云计算生成式 AI 诞生科技新局面

文章目录 前言什么是云计算云计算类型亚马逊云科技云计算最多的功能最大的客户和合作伙伴社区最安全最快的创新速度最成熟的运营专业能力 什么是生成式 AI如何使用生成式 AI后记 前言 在科技发展的滚滚浪潮中&#xff0c;我们见证了云计算的崛起和生成式 AI 的突破&#xff0c…

基于ssm亚盛汽车配件销售业绩管理系统

摘 要 如今的信息时代&#xff0c;对信息的共享性&#xff0c;信息的流通性有着较高要求&#xff0c;因此传统管理方式就不适合。为了让亚盛汽车配件销售信息的管理模式进行升级&#xff0c;也为了更好的维护亚盛汽车配件销售信息&#xff0c;亚盛汽车配件销售业绩管理系统的开…

RNN-T Training,RNN-T模型训练详解——语音信号处理学习(三)(选修三)

参考文献&#xff1a; Speech Recognition (option) - RNN-T Training哔哩哔哩bilibili 2020 年 3月 新番 李宏毅 人类语言处理 独家笔记 Alignment Train - 8 - 知乎 (zhihu.com) 本次省略所有引用论文 目录 一、如何将 Alignment 概率加和 对齐方式概率如何计算 概率加和计…

PyQt6第一个程序HelloWorld实现

锋哥原创的PyQt6视频教程&#xff1a; 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计12条视频&#xff0c;包括&#xff1a;2024版 PyQt6 Python桌面开发 视频教程(无废话版…