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.pswp.cn/news/165953.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/165953.shtml
英文地址,請注明出處:http://en.pswp.cn/news/165953.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

計算給定字符串中各個數字的和的平均值…… ← Python 列表

【題目描述】 給定字符串 s"9876543210"。 請編程計算給定字符串中各個數字的和的平均值&#xff0c;并統計大于平均值的數字個數。【算法分析】 ◆ alist("abcd") # Create a list with characters a, b, c, d◆ eval(a[i]) # Converts characters to i…

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…

項目經理面試題持續更新

1.項目中常用的文檔有哪些&#xff1f; 1、可行性報告 可行性報告的目的是調查和展示任務要求&#xff0c;并確定項目是否值得和可行。可行性由五個主要因素驗證——技術和系統、成本、法律、運營和進度。次要可行性因素包括市場、資源和文化因素。 2、項目章程 項目章程是證明…

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新…

01 LM 算法及 Cpp 實現

文章目錄 01 LM 算法及 Cpp 實現1.1 應用1.2 阻尼法推導1.3 Cpp 算法實現 01 LM 算法及 Cpp 實現 1.1 應用 LM 算法用于解決非線性最小二乘問題 min ? x F ( x ) 1 2 ∥ f ( x ) ∥ 2 2 (1) \min _x F(x)\frac{1}{2}\|f(\boldsymbol{x})\|_2^2 \tag{1} xmin?F(x)21?∥f(x…

代理模式 rust和java的實現

文章目錄 代理模式介紹實現javarust rust倉庫 代理模式 在代理模式&#xff08;Proxy Pattern&#xff09;中&#xff0c;一個類代表另一個類的功能。在代理模式中&#xff0c;我們創建具有現有對象的對象&#xff0c;以便向外界提供功能接口。 介紹 意圖&#xff1a;為其他對…