Windows | 模仿网易云任务栏实现自定义按钮及缩略图

前言

最近更新网易云发现任务栏按钮中除了播放相关的按钮,多了一个喜欢的按钮:

image-20231123151125974

之前我一直以为网易云任务栏的按钮只是 Windows 为音乐软件专门提供的,于是我又看了一眼系统自带的播放器,发现并没有爱心按钮:

image-20231123151504786

这时我就想会不会是 Windows 提供了相关接口可以让用户自定义,一搜发现还真有,ITaskbarList3接口提供了自定义任务栏按钮的方法,于是就有了下面这个 demo 的实现:

动画

在实现的过程中也遇到了很多问题:

  • 由于自定义缩略图,导致悬浮在缩略图上无法查看原有的预览窗口内容。
  • 使用 WIN + TAB 切换窗口时,显示的预览图是缩略图无法查看原有的预览窗口内容。

不过,经过搜索发现网易云的开发者已经分享过相关的思路(文末的参考文献),就是没有相应的编码实现,之后我就按照自己的理解实现了相关的功能,相关效果见下图,本文涉及到的完整代码已上传到GitHub。

使用 WIN + TAB 正常显示原窗口信息:

image-20231123153136728

鼠标悬浮缩略图上正常显示原窗口信息:

image-20231123153228763

自定义按钮

首先是自定义按钮的实现,我们先添加四个按钮,使用ITaskbarList3接口即可:

#include <shobjidl.h>#define BTN_COUNT 4// 任务栏按钮
THUMBBUTTON btns[BTN_COUNT];// 任务栏对象
ITaskbarList3* pTaskbar;// 初始化 COM
CoInitialize(NULL);
CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pTaskbar));WCHAR tips[BTN_COUNT][4] = { L"上一首", L"暂停", L"下一首", L"喜欢" };
int icons[BTN_COUNT] = { IDI_PREVIOUS, IDI_PAUSE, IDI_NEXT, IDI_UNLIKE };for (int i = 0; i < BTN_COUNT; i++)
{btns[i].dwMask = THB_BITMAP | THB_ICON | THB_FLAGS | THB_TOOLTIP;btns[i].iId = 1000 + i;btns[i].iBitmap = i;btns[i].hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(icons[i]));btns[i].dwFlags = THBF_ENABLED;wcscpy_s(btns[i].szTip, tips[i]);
}pTaskbar->ThumbBarAddButtons(hWnd, BTN_COUNT, btns);// 释放资源
pTaskbar->Release();
CoUninitialize();

然后针对对应的按钮,设置相应的点击事件,这里的DwmSetIconicThumbnail用于设置缩略图,留到下面再具体说明,1000 ~ 1003对应上文中设置的按钮的iId

#define BG_COUNT 3// 当前下标
int bgIndex = 0;// 背景图
WCHAR bgImgs[3][8] = { L"bg1.bmp", L"bg2.bmp", L"bg3.bmp" };// 控制暂停/播放切换
bool play = true;// 控制喜欢/取消喜欢切换
bool unlike = true;case WM_COMMAND:{int wmId = LOWORD(wParam);// 分析菜单选择:switch (wmId){case 1000:bgIndex = (bgIndex + BG_COUNT - 1) % BG_COUNT;DwmSetIconicThumbnail(hWnd, LoadImageAndConvertToHBITMAP(bgImgs[bgIndex]), 0);break;case 1001:if (play) {btns[1].hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_PAUSE));wcscpy_s(btns[1].szTip, L"播放");}else {btns[1].hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_PLAY));wcscpy_s(btns[1].szTip, L"暂停");}play = !play;// 更新按钮显示pTaskbar->ThumbBarUpdateButtons(hWnd, BTN_COUNT, btns);break;case 1002:bgIndex = (bgIndex + 1) % BG_COUNT;DwmSetIconicThumbnail(hWnd, LoadImageAndConvertToHBITMAP(bgImgs[bgIndex]), 0);break;case 1003:if (unlike) {btns[3].hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_LIKE));wcscpy_s(btns[3].szTip, L"取消喜欢");}else {btns[3].hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_UNLIKE));wcscpy_s(btns[3].szTip, L"喜欢");}unlike = !unlike;// 更新按钮显示pTaskbar->ThumbBarUpdateButtons(hWnd, BTN_COUNT, btns);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}}break;

以上两步实现了以下效果:

自定义缩略图

自定义缩略图需要使用到DwmSetIconicThumbnail接口,同时还需要注意缩略图的格式必须为bmp,这里使用GDI进行加载:

#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")// 开启自定义背景
BOOL enableBg = TRUE;// 初始化 GDI+
ULONG_PTR gdiplusToken;// 是否初始化 GDI+
bool initGDI = false;void InitializeGDIPlus() {Gdiplus::GdiplusStartupInput gdiplusStartupInput;Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
}void ShutdownGDIPlus() {Gdiplus::GdiplusShutdown(gdiplusToken);
}// 加载图像文件并返回 HBITMAP
HBITMAP LoadImageAndConvertToHBITMAP(const WCHAR* filePath) {if (!initGDI) {InitializeGDIPlus();initGDI = true;}Gdiplus::Bitmap bitmap(filePath);if (bitmap.GetLastStatus() != Gdiplus::Ok) {return nullptr;}HBITMAP hBitmap = nullptr;Gdiplus::Color color;bitmap.GetHBITMAP(color, &hBitmap);return hBitmap;
}case WM_CREATE:// 开启自定义缩略图DwmSetWindowAttribute(hWnd, DWMWA_HAS_ICONIC_BITMAP, &enableBg, sizeof(BOOL));DwmSetWindowAttribute(hWnd, DWMWA_FORCE_ICONIC_REPRESENTATION, &enableBg, sizeof(BOOL));DwmInvalidateIconicBitmaps(hWnd);break;case WM_DWMSENDICONICTHUMBNAIL:// 设置缩略图DwmSetIconicThumbnail(hWnd, LoadImageAndConvertToHBITMAP(bgImgs[bgIndex]), 0);break;

GdiplusStartup不能在 main 中调用,原因参考官方文档。

image-20231124090806930

以上步骤就实现了我们的基本功能:

image-20231124091144682

细节优化

通过上述操作,我们已经完成了自定义按钮和缩略图的功能,但是通过 WIN + TAB 会发现显示的窗口也变成光秃秃的缩略图:

image-20231124091312916

同时悬浮在缩略图上显示的窗口也不正常:

image-20231124091424258

强迫症表示受不了!

于是就有了下面的优化(思路参考参考文献的文章):

  1. 创建一个临时窗口用于正常显示以上两个界面,并把该窗口设置为隐藏。
  2. 通过ITaskbarList3接口的RegisterTabSetTabOrder方法将隐藏窗口和原窗口设置成组。

具体实现如下:

// 临时窗口
HWND tmp;tmp = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,0, 0, 0, 0, nullptr, nullptr, hInstance, nullptr);// SW_HIDE 隐藏窗口
ShowWindow(tmp, SW_HIDE);// 注册成组
pTaskbar->RegisterTab(tmp, hWnd);
pTaskbar->SetTabOrder(tmp, hWnd);UpdateWindow(tmp);// 发送 WM_DWMSENDICONICTHUMBNAIL 信息避免第一次缩略图显示异常
SendMessage(tmp, WM_DWMSENDICONICTHUMBNAIL, (WPARAM)tmp, 0);case WM_CREATE:// 开启自定义缩略图DwmSetWindowAttribute(hWnd, DWMWA_HAS_ICONIC_BITMAP, &enableBg, sizeof(BOOL));break;case WM_DWMSENDICONICTHUMBNAIL:// 需要重新设置按钮, 否则无法正常显示pTaskbar->ThumbBarAddButtons(hWnd, BTN_COUNT, btns);pTaskbar->ThumbBarUpdateButtons(hWnd, BTN_COUNT, btns);DwmSetWindowAttribute(hWnd, DWMWA_FORCE_ICONIC_REPRESENTATION, &enableBg, sizeof(BOOL));DwmInvalidateIconicBitmaps(hWnd);DwmSetIconicThumbnail(hWnd, LoadImageAndConvertToHBITMAP(bgImgs[bgIndex]), 0);break;

以上步骤就可以解决 WIN+TAB 的显示问题了,如下图所示:

image-20231124093118748

但是仍然无法处理悬浮在缩略图上显示异常的问题,这是由于原窗口自定义了缩略图后未定义实时预览图,导致原窗口无法正常显示,也就导致了临时窗口的预览图无法显示,解决方法如下:

// 设置实时预览图
void SetWindowLivePreview(HWND hwnd, HBITMAP hBitmap) {// 不显示原窗口的预览图, 这里设置负坐标POINT ptOffset;ptOffset.x = -1000;ptOffset.y = -2000;DwmSetIconicLivePreviewBitmap(hwnd, hBitmap, &ptOffset, 0);
}case WM_DWMSENDICONICLIVEPREVIEWBITMAP:SetWindowLivePreview(hWnd, LoadImageAndConvertToHBITMAP(bgImgs[bgIndex]));break;

通过以上设置就可以发现实时预览图也显示正常了:

image-20231124093046537

总结

本文简单讲解了如何在 Windows 下实现任务栏自定义按钮和缩略图,由于个人水平有限,示例代码可能存在一些问题,欢迎一起交流讨论。

参考文献

  • 一个体验好的Windows 任务栏缩略图开发心得

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

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

相关文章

C在国内就业已经拉胯,ChatGPT告诉我的

文章目录 一、前言二、ChatGPT查到的数据三、数据亮点 1.C语言近3年数据大跌2.招聘数量每年都在剧增的是全栈工程师3.薪资涨幅最高的是全栈和网安 四、结语 一、前言 不仅前在微信群里搭建了一个ChatGPT 5.0做智能助手&#xff0c;让他来帮我回答群问题&#xff0c; 搭建好…

数十亿美元商机!英国数字基础设施公司Equinix与法国量子计算公司Alice Bob 合作

​&#xff08;图片来源&#xff1a;网络&#xff09; 近日&#xff0c;全球数字基础设施公司Equinix宣布与全球领先的法国量子计算公司Alice & Bob合作&#xff0c;旨在共同开发市场上最为可靠的量子处理器之一。此次合作将使Equinix公司的客户通过使用Equinix Metal和Eq…

好的程序员有什么特质呢?

程序员想要提升自己&#xff0c;一定要关注到工作中的方方面面。而一个好的程序员&#xff0c;一般都有这些特质&#xff1a; 弱者抱怨环境&#xff0c;强者改变环境 不要试图通过抱怨环境来获得工作环境上的改变&#xff0c;这不仅不会给你带来任何实质性的改变&#xff0c;…

自定义字符-摄氏度汉字一

本文为博主 日月同辉&#xff0c;与我共生&#xff0c;csdn原创首发。希望看完后能对你有所帮助&#xff0c;不足之处请指正&#xff01;一起交流学习&#xff0c;共同进步&#xff01; > 发布人&#xff1a;日月同辉,与我共生_单片机-CSDN博客 > 欢迎你为独创博主日月同…

springboot+vue项目如何集成onlyoffice开源文档组件

一、onlyoffice是什么 ONLYOFFICE 是一个开源的办公套件&#xff0c;适合多人在线协作。由总部位于总部在拉脱维亚的 IT 公司Acensio System SIA 开发。它提供在线协作文档编辑器&#xff08;包括文档、电子表格、演示文稿和表单&#xff09;&#xff0c;适用于 Windows、Linu…

python tkinter使用(五)

python tkinter使用(五) 本篇文章讲述tkinter 中treeview的使用 Treeview是一个多列列表框&#xff0c;可以显示层次数据。 #!/usr/bin/python3 # -*- coding: UTF-8 -*- """Author: zhTime 2023/11/23 下午8:28 .Email:Describe: treeview 使用 "&quo…

Linux上自动挂载windows下的网络共享文件夹

比如我们想在ubuntu上挂载一个windows的共享文件夹&#xff0c;我们可以用如下方式实现。 首先我们将windows下的文件夹右键选择【属性】&#xff0c;然后选择【共享】。 选择需要共享的用户&#xff0c;然后设置权限级别。 点击共享&#xff0c;然后点击完成。 这样我们在wi…

Go语言网络爬虫工程经验分享:pholcus库演示抓取头条新闻的实例

网络爬虫是一种自动从互联网上获取数据的程序&#xff0c;它可以用于各种目的&#xff0c;如数据分析、信息检索、竞争情报等。网络爬虫的实现方式有很多&#xff0c;不同的编程语言和框架都有各自的优势和特点。在本文中&#xff0c;我将介绍一种使用Go语言和pholcus库的网络爬…

基于opencv+ImageAI+tensorflow的智能动漫人物识别系统——深度学习算法应用(含python、JS、模型源码)+数据集(一)

目录 前言总体设计系统整体结构图系统流程图 运行环境爬虫1.安装Anaconda2.安装Python3.63.更换pip源4.安装Python包5.下载phantomjs 模型训练1.安装依赖2.安装lmageAl 实际应用1.前端2.安装Flask3.安装Nginx 相关其它博客工程源代码下载其它资料下载 前言 本项目通过爬虫技术…

Word怎么看字数?简单教程分享!

“我在写文章时&#xff0c;总是想看看写了多少字。但是我发现我的Word无法看到字数。在Word中应该怎么查看字数呢&#xff1f;请帮帮我&#xff01;” Word是一个广泛使用的文档编辑工具。在我们编辑文章时&#xff0c;如果想查看写了多少字&#xff0c;也是可以轻松完成的。 …

leetcode:环形链表的入环点

题目描述 题目链接:力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目分析 我们假设起点到环的入口点的距离是L&#xff0c;入口点到相遇点的距离是X&#xff0c;环的长度是C 那么画图我们可以得知&#xff1a; 从开始到相遇时slow走的距离是LX从…

Adobe的组织工具程序Bridge 2024 版本下载与安装

目录 前言一、Bridge 2024安装二、使用配置总结 前言 Adobe Bridge是由 Adobe 公司开发的一款用于管理和组织创意资产的工具。它是Adobe Creative Cloud 套件的一部分&#xff0c;为设计师、摄影师和其他创意专业人员提供了一个集中管理和浏览其多媒体文件的平台。注&#xff…

Ubuntu开机显示No bootable devices found

Ubuntu开机报错&#xff0c;显示显示No bootable devices found&#xff0c;如下图所示&#xff1a; 解决方案如下&#xff1a; 1. F2进入BIOS (1) 重启开启&#xff0c;按F2进入BIOS系统。 (2) 进入Boot Sequence&#xff0c;目前系统选择了UEFI&#xff0c;而Legacy选项为…

Android : AlertDialog对话框、单选、多选、适配器-简单应用

示例图&#xff1a; 1 &#xff1a;创建 AlertDialog.Builder 对象&#xff1b; 2 &#xff1a;调用 setIcon() 设置图标&#xff0c; setTitle() 或 setCustomTitle() 设置标题&#xff1b; 3 &#xff1a;设置对话框的内容&#xff1a; setMessage() 还有其他方法来指定显示…

【每日一题】2824. 统计和小于目标的下标对数目-2023.11.24

题目&#xff1a; 2824. 统计和小于目标的下标对数目 给你一个下标从 0 开始长度为 n 的整数数组 nums 和一个整数 target &#xff0c;请你返回满足 0 < i < j < n 且 nums[i] nums[j] < target 的下标对 (i, j) 的数目。 示例 1&#xff1a; 输入&#xff1…

双12电视盒子什么牌子好?数码小编力荐目前最强的电视盒子

最近想买电视盒子的网友非常多&#xff0c;小编收到了很多关于电视盒子方面的咨询&#xff0c;因此我特意整理了今年测评过的电视盒子&#xff0c;总结了五款目前最强的电视盒子&#xff0c;想知道双十二买电视盒子什么牌子好就赶紧收藏起来吧。 推荐一&#xff1a;泰捷WEBOX新…

“KeyarchOS:国产Linux新星的崛起与创新之路“

简介 KeyarchOS是一款由浪潮信息自主研发的服务器操作系统。它因为几个特点而受到我的青睐和一些用户的关注。 首先&#xff0c;KeyarchOS注重安全性和稳定性。它有一些防护和隔离功能&#xff0c;来帮助系统稳定运行&#xff0c;而且是中文语言更接地气。 其次&#xff0c;Ke…

OSG编程指南<十>:OSG几何体的绘制

1、场景基本绘图类 在 OSG 中创建几何体的方法比较简单&#xff0c;通常有 3 种处理几何体的手段&#xff1a; 使用松散封装的OpenGL 绘图基元&#xff1b;使用 OSG 中的基本几何体&#xff1b;从文件中导入场景模型。 使用松散封装的OpenGL 绘图基元绘制几何体具有很强的灵活…

牛气霸屏-快抖云推独立版V1.6.7

介绍 快抖云推全插件独立版是最近很火的牛气霸屏系统独立版&#xff0c;牛气霸屏系统就是商家通过系统在线创建抖音或快手霸屏活动&#xff0c;并生成该活动的爆客二维码&#xff0c;用户通过扫二维码即可参加活动&#xff08;活动可以是领取卡劵&#xff0c;抽奖等&#xff0…

DevExpress中文教程 - 如何在macOS和Linux (CTP)上创建、修改报表(下)

DevExpress Reporting是.NET Framework下功能完善的报表平台&#xff0c;它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集&#xff0c;包括数据透视表、图表&#xff0c;因此您可以构建无与伦比、信息清晰的报表。 DevExpress Reports — 跨平台报表组件&#x…