需求
在 Windows 上,一些视频会议软件进行屏幕分享的时候,分享的屏幕时会有一个绿色边框将其标记出来,方便分享人知道当前正在分享的是哪一个屏幕,这篇博客主要就介绍一下如何实现这么一个窗口。
我们要做到下面几个点
1、始终置顶窗口(WS_EX_TOPMOST)
2、透明并且可穿透(WS_EX_LAYERED | WS_EX_TRANSPARENT)
3、没有最大化、最小化等(WS_EX_TOOLWINDOW)
4、有绿边(使用 Gdiplus::SolidBrush)
具体实现
1、创建一个透明窗口,这里使用到的扩展属性是 WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW
#include <Windows.h>
LRESULT CALLBACK WndProc(HWND hWND, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_ERASEBKGND:
return 0;
case WM_CLOSE:
DestroyWindow(hWND);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWND, msg, wParam, lParam);
}
return 0;
}
int main(void) {
HINSTANCE hinstance = GetModuleHandle(0);
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "TransparentWindow";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc))
{
return NULL;
}
HWND hwnd = CreateWindowEx(WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW, "TransparentWindow", "TransparentWindow", WS_POPUP | WS_VISIBLE, 0, 0, 1920, 1080, NULL, NULL, hinstance, NULL);
if (!hwnd)
{
return NULL;
}
MSG msg = { 0 };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
2、上面我们创建了一个大小为 1920 x 1080 的窗口,也就是我的屏幕大小,接下来需要给这个窗口带上绿边,这里我们填充 4 个矩形区域,可以先看下面的图
就像上面两张图片这样,假设绿边的宽度为 4,那么我们需要将
上方:{0,0,width,4}
下方:{0,height-4,width,4}
左方:{0,4,4,height-4}
右方:{width-4,4,4,height-4}
这四个区域填充为绿色,接下来我们填充区域
全部代码
#include <Windows.h>
extern "C" {
#include <gdiplus.h>
}
#pragma comment(lib, "Gdiplus.lib")
LRESULT CALLBACK WndProc(HWND hWND, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_ERASEBKGND:
return 0;
case WM_CLOSE:
DestroyWindow(hWND);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWND, msg, wParam, lParam);
}
return 0;
}
void FillWindow(HWND hwnd) {
RECT rect;
GetClientRect(hwnd, &rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
int linesize = 4;
HDC hdc = GetDC(hwnd);
HDC mem_dc = ::CreateCompatibleDC(hdc);
HBITMAP hbitmap = ::CreateCompatibleBitmap(hdc, width, height);
HBITMAP old_hbitmap = (HBITMAP)::SelectObject(mem_dc, hbitmap);
Gdiplus::Graphics graphics(mem_dc);
graphics.Clear(Gdiplus::Color(255, 0, 0));
Gdiplus::SolidBrush brush(Gdiplus::Color(0, 230, 0));
graphics.FillRectangle(&brush, 0, 0, width, linesize);
graphics.FillRectangle(&brush, 0, height - linesize, width, linesize);
graphics.FillRectangle(&brush, 0, linesize, linesize, height-4);
graphics.FillRectangle(&brush, width - linesize, linesize, linesize, height-4);
BLENDFUNCTION bs = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
tagPOINT pos{ 0,0 };
tagPOINT dstPos = { rect.left, rect.top };
tagSIZE dstSize = { width, height };
BOOL bRes = ::UpdateLayeredWindow(hwnd, hdc, &dstPos, &dstSize, graphics.GetHDC(), &pos, RGB(255, 0, 0), &bs, ULW_COLORKEY);
graphics.ReleaseHDC(mem_dc);
::SelectObject(mem_dc, old_hbitmap);
::DeleteDC(mem_dc);
::DeleteObject(hbitmap);
ReleaseDC(hwnd, hdc);
mem_dc = NULL;
hbitmap = NULL;
old_hbitmap = NULL;
}
int main(void) {
SetProcessDPIAware();
HINSTANCE hinstance = GetModuleHandle(0);
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "TransparentWindow";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc))
{
return NULL;
}
HWND hwnd = CreateWindowEx(WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW, "TransparentWindow", "TransparentWindow",
WS_POPUP | WS_VISIBLE, 0, 0, 1920, 1080, NULL, NULL, hinstance, NULL);
if (!hwnd)
{
return NULL;
}
Gdiplus::GdiplusStartupInput input;
Gdiplus::GdiplusStartupOutput output;
ULONG_PTR token;
Gdiplus::Status status = GdiplusStartup(&token, &input, &output);
FillWindow(hwnd);
MSG msg = { 0 };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Gdiplus::GdiplusShutdown(token);
return 0;
}
可以自己手动运行观察效果