C++屏幕水印实现遇到问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
koomox
V2EX    程序员

C++屏幕水印实现遇到问题

  •  
  •   koomox 2023-09-26 07:04:48 +08:00 1871 次点击
    这是一个创建于 750 天前的主题,其中的信息可能已经有所发展或是发生改变。

    C++用得不多,并且对 GDIPLUS 也不是很了解。想实现一个屏幕水印得工具,下面得代码运行后,Graphics 创建得对象背景非透明,并且程序无法正常关闭退出。大佬们帮忙看看,指定迷津

    代码如下:

    #include <Windows.h> #include <gdiplus.h> #include <string> using namespace Gdiplus; #pragma comment(lib, "gdiplus.lib") const std::wstring watermarkText = L"Your Watermark Text"; const int watermarkFOntSize= 38; const int watermarkSpacing = 100; void DrawWatermark(HDC hdc, int windowWidth, int windowHeight) { // 初始化 GDI+ GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); SetBkMode(hdc, TRANSPARENT); // 创建 Graphics 对象 Graphics graphics(hdc); // 创建字体 FontFamily fontFamily(L"Arial"); Font font(&fontFamily, watermarkFontSize, FontStyleRegular, UnitPixel); // 设置文本颜色 SolidBrush textBrush(Color(255, 0, 0, 0)); // 文本颜色为黑色 // 获取文本尺寸 RectF layoutRect; graphics.MeasureString(watermarkText.c_str(), -1, &font, PointF(0, 0), &layoutRect); // 计算水印文本块的总数以填满整个屏幕 int numBlocksX = (windowWidth + watermarkSpacing) / (static_cast<int>(layoutRect.Width) + watermarkSpacing); int numBlocksY = (windowHeight + watermarkSpacing) / (static_cast<int>(layoutRect.Height) + watermarkSpacing); // 计算实际的间距 int actualSpacingX = (windowWidth - numBlocksX * static_cast<int>(layoutRect.Width)) / (numBlocksX - 1); int actualSpacingY = (windowHeight - numBlocksY * static_cast<int>(layoutRect.Height)) / (numBlocksY - 1); // 保存当前的世界变换矩阵 Matrix oldTransform; graphics.GetTransform(&oldTransform); // 绘制水印文本块 for (int y = 0; y < numBlocksY; y++) { for (int x = 0; x < numBlocksX; x++) { int textX = x * (static_cast<int>(layoutRect.Width) + actualSpacingX); int textY = y * (static_cast<int>(layoutRect.Height) + actualSpacingY); // 移动 Graphics 对象到文本块位置 graphics.ResetTransform(); graphics.TranslateTransform(static_cast<float>(textX), static_cast<float>(textY)); graphics.RotateTransform(-45.0f); // 绘制水印文本 graphics.DrawString(watermarkText.c_str(), -1, &font, PointF(0, 0), &textBrush); // 恢复原始的世界变换矩阵 graphics.SetTransform(&oldTransform); } } // 关闭 GDI+ GdiplusShutdown(gdiplusToken); } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: { // 设置窗口样式为 WS_EX_LAYERED SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT); // 设置窗口为完全透明 SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 0, LWA_COLORKEY); // 设置窗口大小为屏幕大小 int windowWidth = GetSystemMetrics(SM_CXSCREEN); int windowHeight = GetSystemMetrics(SM_CYSCREEN); SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, windowWidth, windowHeight, SWP_SHOWWINDOW); break; } case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); int windowWidth = GetSystemMetrics(SM_CXSCREEN); int windowHeight = GetSystemMetrics(SM_CYSCREEN); // 绘制水印 DrawWatermark(hdc, windowWidth, windowHeight); EndPaint(hwnd, &ps); break; } case WM_DESTROY: { PostQuitMessage(0); break; } default: return DefWindowProc(hwnd, message, wParam, lParam); } return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wcex; ZeroMemory(&wcex, sizeof(WNDCLASSEX)); wcex.cbSize = sizeof(WNDCLASSEX); wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.lpszClassName = L"WatermarkWindowClass"; RegisterClassEx(&wcex); HWND hwnd = CreateWindow(L"WatermarkWindowClass", L"", WS_POPUP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), NULL, NULL, hInstance, NULL); if (hwnd == NULL) { return 0; } ShowWindow(hwnd, nCmdShow); MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return static_cast<int>(msg.wParam); } 
    6 条回复    2023-09-26 23:18:32 +08:00
    ysc3839
        1
    ysc3839  
       2023-09-26 08:12:40 +08:00 via Android
    没有必要用 std::wstring ,直接写 const wchar_t* watermarkText 即可。
    CreateWindow 改成 CreateWindowExW ,第一个参数直接写 WS_EX_LAYERED | WS_EX_TRANSPARENT ,不需要 SetWindowLong 。
    不应该用 SetLayeredWindowAttributes ,而应该用 UpdateLayeredWindow ,大致方法参见 https://www.cnblogs.com/strive-sun/p/13073015.html 。后续就不用管了,WM_PAINT 那块删掉。
    tool2d
        2
    tool2d  
       2023-09-26 09:30:33 +08:00
    ”程序无法正常关闭退出“ 这是缺少 WM_CLOSE 。

    你多问几次 GPT ,复杂代码一次成型几乎不太可能。多问几次就可以了。
    xqb
        3
    xqb  
       2023-09-26 16:04:27 +08:00
    保证 GdiplusShutdown 前 graphics 析构就可以了
    zhuangzhuang1988
        4
    zhuangzhuang1988  
       2023-09-26 20:08:09 +08:00
    koomox
        5
    koomox  
    OP
       2023-09-26 23:01:04 +08:00
    感谢各位。 @ysc3839 参考你提供的方案,解决了很多问题。放出最终版本,还有一些小问题,但是可以正常运行了,欢迎大家指正。
    ```
    #include <Windows.h>
    #include <gdiplus.h>
    #include <string>
    #include <thread>

    using namespace Gdiplus;
    #pragma comment(lib, "gdiplus.lib")

    const wchar_t* watermarkText = L"Your Watermark Text";
    const int watermarkFOntSize= 38;
    const int watermarkSpacing = 100;

    void DrawWatermarkToBitmap(HWND hwnd, HDC hdc, int width, int height)
    {
    // 创建位图上下文
    HDC memDC = CreateCompatibleDC(hdc);
    HBITMAP hBitmap = CreateCompatibleBitmap(hdc, width, height);
    HBITMAP hOldBitmap = static_cast<HBITMAP>(SelectObject(memDC, hBitmap));

    // 创建 Graphics 对象
    Graphics graphics(memDC);

    // 创建字体
    FontFamily fontFamily(L"Arial");
    Font font(&fontFamily, watermarkFontSize, FontStyleRegular, UnitPixel);

    // Create a SolidBrush with the text color
    SolidBrush textBrush(Color(128, 255, 0, 0));

    // 获取文本尺寸
    RectF layoutRect;
    graphics.MeasureString(watermarkText, -1, &font, PointF(0, 0), &layoutRect);

    // 计算水印文本块的总数以填满整个位图
    int numBlocksX = (width + watermarkSpacing) / (static_cast<int>(layoutRect.Width) + watermarkSpacing);
    int numBlocksY = (height + watermarkSpacing) / (static_cast<int>(layoutRect.Height) + watermarkSpacing);

    // 计算实际的间距
    int actualSpacingX = (width - numBlocksX * static_cast<int>(layoutRect.Width)) / (numBlocksX - 1);
    int actualSpacingY = (height - numBlocksY * static_cast<int>(layoutRect.Height)) / (numBlocksY - 1);

    // 绘制水印文本块
    for (int y = 0; y < numBlocksY; y++) {
    for (int x = 0; x < numBlocksX; x++) {
    int textX = x * (static_cast<int>(layoutRect.Width) + actualSpacingX);
    int textY = y * (static_cast<int>(layoutRect.Height) + actualSpacingY);

    // 移动 Graphics 对象到文本块位置
    graphics.ResetTransform();
    graphics.TranslateTransform(static_cast<float>(textX), static_cast<float>(textY));
    graphics.RotateTransform(-45.0f);

    // 绘制水印文本
    graphics.DrawString(watermarkText, -1, &font, PointF(0, 0), &textBrush);
    }
    }

    // 渲染位图到窗口
    BLENDFUNCTION blend = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
    POINT ptSrc = { 0, 0 };
    SIZE sizeWnd = { width, height };
    POINT ptDst = { 0, 0 };
    UpdateLayeredWindow(hwnd, hdc, &ptDst, &sizeWnd, memDC, &ptSrc, 0, &blend, ULW_ALPHA);

    // 清理资源
    SelectObject(memDC, hOldBitmap);
    DeleteObject(hBitmap);
    DeleteDC(memDC);

    //ReleaseDC(hwnd, hdc);
    }

    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    switch (message)
    {
    case WM_CREATE:
    SetTimer(hwnd, 1, 1000, NULL); // 创建定时器,每秒更新水印
    break;
    case WM_TIMER:
    {
    DrawWatermarkToBitmap(hwnd, GetDC(hwnd), GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
    break;
    }
    case WM_SIZE:
    // 窗口大小改变时重新绘制
    {
    DrawWatermarkToBitmap(hwnd, GetDC(hwnd), GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
    break;
    }
    case WM_CLOSE:
    DestroyWindow(hwnd);
    break;
    case WM_DESTROY:
    {
    PostQuitMessage(0);
    break;
    }
    default:
    return DefWindowProc(hwnd, message, wParam, lParam);
    }

    return 0;
    }

    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {


    // 注册窗口类
    WNDCLASSEX wcex;
    ZeroMemory(&wcex, sizeof(WNDCLASSEX));
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.lpfnWndProc = WndProc;
    wcex.hInstance = hInstance;
    wcex.lpszClassName = L"WatermarkWindowClass";
    RegisterClassEx(&wcex);

    HWND hwnd = CreateWindowExW(WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST, L"WatermarkWindowClass", L"Watermark Window", WS_POPUP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), NULL, NULL, hInstance, NULL);

    if (hwnd == NULL)
    {
    return 0;
    }

    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    ShowWindow(hwnd, nCmdShow);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }

    // 关闭 GDI+
    GdiplusShutdown(gdiplusToken);

    return static_cast<int>(msg.wParam);
    }
    ```
    koomox
        6
    koomox  
    OP
       2023-09-26 23:18:32 +08:00
    @ysc3839 感谢,参考你的方案,已解决问题。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3080 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 00:28 PVG 08:28 LAX 17:28 JFK 20:28
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86