如何在没有窗口的情况下绘制文本

Posted

技术标签:

【中文标题】如何在没有窗口的情况下绘制文本【英文标题】:How to draw text without window 【发布时间】:2014-12-03 11:57:34 【问题描述】:

我试图在屏幕上显示文本,在所有内容之上,不可点击,没有任何窗口。这个想法是能够显示通知。我离我想要的有点近,但刚刚出现了一个非常奇怪的问题。这是代码:

#include <Windows.h>  

int main(void)

    HDC hdc = ::GetDC(0);
    RECT rect;
    SetTextColor(hdc, RGB(0, 0, 255));
    SetBkMode(hdc, TRANSPARENT);
    SetBkColor(hdc, RGB(0, 0, 0));
    auto hFont = CreateFont(40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, L"Verdana");
    auto hTmp = (HFONT)SelectObject(hdc, hFont);
    rect.left = 40;
    rect.top = 10;

    while (true)
        DrawText(hdc, L"THIS IS A TEXT", -1, &rect, DT_SINGLELINE | DT_NOCLIP);
        Sleep(1);
    
    DeleteObject(SelectObject(hdc, hTmp));
    ::ReleaseDC(0, hdc);
    return 0;


当我将文本设置从 red 更改为 blue 并将大小 80 更改为 40 时会发生这种情况:

由于某种原因,在重新运行程序后,我仍然可以看到旧文本,这告诉我我误解了一些东西。有没有更好、更清洁的方法来做到这一点?

编辑:我检查了 Windows 通知,但这不是解决方案。假设您正在玩全屏游戏,并且想知道是否有电子邮件到达。另一个重要的事情是它不能被点击,因此错误的点击不会最小化你的游戏。当您接到电话时,将您的应用程序最小化的 Skype 弹出窗口有多烦人?

【问题讨论】:

您正在绘制主设备上下文。 @mudasobwa 你能澄清你的评论吗?这是什么意思? “这个想法是能够显示通知。”自定义通知系统很糟糕,最好不要收到任何通知。当您的操作系统支持内置的系统通知机制(如 Windows 10 那样)时,只需使用它即可。 @bames53 是否可以使用 Windows 7 的功能来显示通知而不妨碍?我检查了这种可能性,但一无所获。我只想要一个小矩形文字说:你有一封新邮件或类似的东西。以一种不会打扰您正在做的事情的方式。 我已经添加了 winapi 标签,但我不相信这是一个 C++ 问题。 【参考方案1】:

您已绕过所有窗口/客户端控件,因此系统不知道需要清除此区域。您需要手动告诉它,尤其是因为您没有使用 Windows 消息通知机制。

在绘制到它之前,您希望使屏幕的该部分无效并告诉窗口重新绘制它:

::InvalidateRect (0, &rect, false);   //  Redraw without erasing. If doesn't help, try true
::UpdateWindow (0);
while (true)...

【讨论】:

谢谢,这成功了!还有一件事:有没有办法在全屏应用上完成这项工作? 如果有人在使用全屏应用,那么他们不想看到通知。不要参加***别的比赛。【参考方案2】:

在尝试完成这项工作时,我最终遇到了很多问题。如果有人最终访问此页面以寻找我遇到的相同问题的答案,我希望您的时间比我更轻松。这是对我有用的代码:

#include <Windows.h> 


INT WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR lpCmdLine, INT nCmdShow)

    // Define and initialize variables
    HDC          hdc;
    HDC          hdcMem;
    HBITMAP      hbmMem;
    HANDLE       hOld;
    RECT rect;
    SIZE sz;
    int win_width = 0;
    int win_height = 0;
    int font_size = 20;
    int location_x = 40;
    int location_y = 40;
    int border = font_size / 4;
    int duration = 10000;           // In miliseconds. The notification will always stay up more time
    wchar_t* font_face = L"Consolas";
    wchar_t message[100];

    // Save command-line arguments to message; They are showed by the notification
    MultiByteToWideChar(0, 0,
        lpCmdLine,
        strlen(lpCmdLine),
        message,
        100
        );
    message[strlen(lpCmdLine)] = L'\0';


    // Acquire screen
    hdc = ::GetDC(0);

    //Create necessary font
    HFONT hFont = CreateFont(font_size, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, font_face);
    HFONT hTmp = (HFONT)SelectObject(hdc, hFont);

    // Calculate size of the text
    GetTextExtentPoint32(hdc, message, wcslen(message), &sz);
    win_width = sz.cx;
    win_height = sz.cy;
    rect =  0, 0, sz.cx, sz.cx ;

    // Create an off-screen DC for double-buffering
    hdcMem = CreateCompatibleDC(hdc);
    hbmMem = CreateCompatibleBitmap(hdc, win_width + 2 * border, win_height + 2 * border);

    // Configure off-screen DC
    SetBkMode(hdcMem, OPAQUE);
    SetTextColor(hdcMem, RGB(125, 125, 255));
    SetBkColor(hdcMem, RGB(0, 0, 0));
    SelectObject(hdcMem, hFont);
    hOld = SelectObject(hdcMem, hbmMem);

    // Draw loop
    for (int i = 0; i < duration; i++)
    
        // Draw into hdcMem
        DrawText(hdcMem, message, -1, &rect, DT_SINGLELINE);

        // Transfer the off-screen DC to the screen
        BitBlt(hdc, location_x, location_y, win_width + 2 * border, win_height + 2 * border, hdcMem, -5, -5, SRCCOPY);


        // Don't eat all the cpu!
        Sleep(1);
    

    // Delete notification right after time expires
    ::InvalidateRect(0, &rect, false);
    ::UpdateWindow(0);

    // Free-up the off-screen DC
    SelectObject(hdcMem, hOld);
    DeleteObject(hbmMem);
    DeleteDC(hdcMem);

    // Release created objects
    DeleteObject(SelectObject(hdc, hTmp));
    ::ReleaseDC(0, hdc);
    return 0;

它仍然可以改进很多。唯一出现的是带有通知的矩形。 您传递给程序的参数将显示为一条消息。与hdcMem 相关的所有内容都已实施以避免闪烁。我还不能更改较大矩形的背景。

【讨论】:

以上是关于如何在没有窗口的情况下绘制文本的主要内容,如果未能解决你的问题,请参考以下文章

Java:如何将绘制的文本动态适应窗口

Kivy:如何在不关闭弹出窗口的情况下更新弹出标签文本

如何在不显示文本的情况下分配 Win32 EDIT 控件的窗口名称?

如何在 C# 中显示没有窗口的图像

如何在没有中间绘制的情况下调整 QWidget 的大小

在 iOS 中,如何在没有抗锯齿/插值的情况下绘制位图?