如何在Windows任意窗口上层显示透明的GPU/CPU状态文本?
实现全局透明悬浮的CPU/GPU使用率显示
原代码的问题分析
- 直接操作桌面窗口DC无效:
GetDesktopWindow()获取的桌面窗口不支持WS_EX_LAYERED扩展风格,调用SetLayeredWindowAttributes无法实现透明效果。 DrawText默认填充背景:该函数会用当前DC的背景色填充文本所在矩形,导致出现不透明背景块。- 绘制内容会被覆盖:直接在桌面绘制的文本会被其他窗口遮挡,无法实现“所有窗口右上角显示”的需求。
WinAPI 实现方案
正确的做法是创建一个分层透明的顶层悬浮窗口,而非直接操作桌面窗口。以下是完整实现代码:
#include <windows.h> #include <stdio.h> LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { const wchar_t CLASS_NAME[] = L"TransparentOverlayClass"; // 注册窗口类 WNDCLASSEX wc = {0}; wc.cbSize = sizeof(WNDCLASSEX); wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME; wc.hCursor = LoadCursor(NULL, IDC_ARROW); RegisterClassEx(&wc); // 获取屏幕分辨率,定位窗口到右上角 int screenWidth = GetSystemMetrics(SM_CXSCREEN); int screenHeight = GetSystemMetrics(SM_CYSCREEN); int overlayWidth = 220; int overlayHeight = 60; int xPos = screenWidth - overlayWidth - 15; int yPos = 15; // 创建分层、顶层、穿透鼠标的窗口 HWND hwnd = CreateWindowEx( WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TRANSPARENT, CLASS_NAME, L"", WS_POPUP, xPos, yPos, overlayWidth, overlayHeight, NULL, NULL, hInstance, NULL ); if (!hwnd) return 0; // 设置黑色为透明色,仅显示非黑色内容(文本) SetLayeredWindowAttributes(hwnd, RGB(0,0,0), 0, LWA_COLORKEY); ShowWindow(hwnd, nCmdShow); // 消息循环 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; } LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); // 设置文本背景透明,避免填充底色 SetBkMode(hdc, TRANSPARENT); // 设置文本颜色为白色 SetTextColor(hdc, RGB(255,255,255)); // 替换为实际获取CPU/GPU数据的逻辑 const wchar_t text[] = L"CPU: 45% | GPU: 32%"; RECT rect; GetClientRect(hwnd, &rect); // 居中绘制文本 DrawText(hdc, text, -1, &rect, DT_CENTER | DT_VCENTER); EndPaint(hwnd, &ps); return 0; } case WM_DESTROY: PostQuitMessage(0); return 0; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } }
关键细节说明
WS_EX_TOPMOST:确保窗口始终在所有其他窗口上方。WS_EX_TRANSPARENT:窗口不拦截鼠标事件,底层窗口可正常操作。LWA_COLORKEY模式:指定黑色为透明色,窗口中黑色区域完全透明,仅文本可见。- 可添加
SetTimer实现定时刷新CPU/GPU数据(比如每秒更新一次)。
SFML 实现方案
如果觉得WinAPI过于繁琐,SFML库可以更简洁地实现需求,代码如下:
#include <SFML/Graphics.hpp> #include <iostream> int main() { // 获取屏幕参数,定位窗口到右上角 sf::VideoMode desktop = sf::VideoMode::getDesktopMode(); const int overlayW = 220, overlayH = 60; int x = desktop.width - overlayW - 15; int y = 15; // 创建无边框、顶层、透明窗口 sf::RenderWindow window( sf::VideoMode(overlayW, overlayH), "", sf::Style::None | sf::Style::Topmost ); window.setPosition(sf::Vector2i(x, y)); window.setFramerateLimit(30); // 加载系统Arial字体 sf::Font font; if (!font.loadFromFile("C:/Windows/Fonts/arial.ttf")) { std::cerr << "字体加载失败" << std::endl; return 1; } // 配置文本 sf::Text text; text.setFont(font); text.setString("CPU: 45% | GPU: 32%"); text.setCharacterSize(19); text.setFillColor(sf::Color::White); // 居中文本 sf::FloatRect textBounds = text.getLocalBounds(); text.setOrigin(textBounds.width/2, textBounds.height/2); text.setPosition(overlayW/2.f, overlayH/2.f); // 主循环 while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed || (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape)) window.close(); } // 此处添加CPU/GPU数据更新逻辑,修改text.setString()内容 window.clear(sf::Color::Transparent); window.draw(text); window.display(); } return 0; }
SFML 优势
- 无需手动处理WinAPI窗口注册、消息循环等底层细节,代码更简洁易读。
- 内置文本渲染、窗口透明等功能,快速实现需求。
- 跨平台兼容,若后续需要移植到其他系统也无需大幅修改。
内容的提问来源于stack exchange,提问作者Erz
相关产品推荐
相关产品推荐

