Windows API: write to screen as on screen display

那年仲夏 提交于 2019-12-28 06:27:23

问题


I am writing a (very) small application which just performs some minor things at start and should write a message on the screen similar as for an on-screen-display: Big letters, without any window, above everything, visible for some moment and then fade away.

If possible I do not want to create a window for it.

What is the right way to do this?

(I hope there are no special toolkits like DirectX, direct graphics access etc. required)


回答1:


As pointed out in the comments, you can draw directly to the screen. GetDC offers to return the appropriate device context:

hWnd [in]

A handle to the window whose DC is to be retrieved. If this value is NULL, GetDC retrieves the DC for the entire screen.

Rendering directly to the screen poses at least two problems that need to be addressed:

  1. The screen DC is a shared resource. Whenever someone else renders to the screen (e.g. when a window is displayed), that portion of the screen gets overwritten.
  2. Rendering is destructive. When rendering into a device context, the original contents get overwritten. To implement a fade-out effect you would have to save the original contents (and update them dynamically as other windows are displayed).

Both issues can be solved by creating a window instead. A window is not required to have a border, caption bar, system menu or minimize/maximize/close buttons. The appropriate Window Styles are WS_POPUP | WS_VISIBLE.

To make the window show up in front of everything else, it needs to be marked as topmost (using the WS_EX_TOPMOST Extended Window Style). Note, that this places the window above all other non-topmost windows in the Z-order. You still have to fight with other topmost windows (an arms race you cannot win).

To implement transparency the window must have the WS_EX_LAYERED extended window style as well to create a Layered Window. Alpha transparency is then enabled calling SetLayeredWindowAttributes. To keep the window background fully transparent regardless of the window's alpha transparency you also need to enable color keying. A simple way to do this is to set the hbrBackground member of the WNDCLASSEX structure to (HBRUSH)GetStockObject(BLACK_BRUSH), and specify RGB(0, 0, 0) as the crKey argument in the call to SetLayeredWindowAttributes.


Proof of concept (error checking elided for brevity):
#define STRICT 1
#define WIN32_LEAN_AND_MEAN
#include <SDKDDKVer.h>
#include <windows.h>

// Forward declarations
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );

// Entry point
int APIENTRY wWinMain( HINSTANCE hInstance,
                       HINSTANCE /*hPrevInstance*/,
                       LPTSTR    /*lpCmdLine*/,
                       int       nCmdShow ) {

First up is registering the main application window class. The important piece is the hbrBackground member. This controls background rendering, and will eventually be made fully transparent.

    const wchar_t k_WndClassName[] = L"OverlayWindowClass";

    // Register window class
    WNDCLASSEXW wcex = { 0 };
    wcex.cbSize = sizeof( wcex );
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.hInstance      = hInstance;
    wcex.hCursor        = ::LoadCursorW( NULL, IDC_ARROW );
    wcex.hbrBackground  = (HBRUSH)::GetStockObject( BLACK_BRUSH );
    wcex.lpszClassName  = k_WndClassName;
    ::RegisterClassExW( &wcex );

This is all the setup code required to instantiate a window, and adjust it's attributes. Alpha transparency is enabled to prepare for the fade-out effect, while color keying masks out those areas of the window that aren't rendered to.

    HWND hWnd = ::CreateWindowExW( WS_EX_TOPMOST | WS_EX_LAYERED,
                                   k_WndClassName,
                                   L"Overlay Window",
                                   WS_POPUP | WS_VISIBLE,
                                   CW_USEDEFAULT, CW_USEDEFAULT,
                                   800, 600,
                                   NULL, NULL,
                                   hInstance,
                                   NULL );
    // Make window semi-transparent, and mask out background color
    ::SetLayeredWindowAttributes( hWnd, RGB( 0, 0, 0 ), 128, LWA_ALPHA | LWA_COLORKEY );

The remainder of wWinMain is boilerplate windows application code.

    ::ShowWindow( hWnd, nCmdShow );
    ::UpdateWindow( hWnd );

    // Main message loop:
    MSG msg = { 0 };
    while ( ::GetMessageW( &msg, NULL, 0, 0 ) > 0 )
    {
        ::TranslateMessage( &msg );
        ::DispatchMessageW( &msg );
    }

    return (int)msg.wParam;
}

The window procedure is performs simple rendering. To demonstrate both alpha and key color transparency the code renders a white ellipse with the client area as the bounding rectangle. In addition, the WM_NCHITTEST message is also handled, to provide a simple way to dragged the window accross the screen using the mouse or another pointing device. Note that mouse input is passed to the window underneath for all areas that are fully transparent.

LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    switch ( message )
    {
    case WM_PAINT:
        {
            PAINTSTRUCT ps = { 0 };
            HDC hDC = ::BeginPaint( hWnd, &ps );
            RECT rc = { 0 };
            ::GetClientRect( hWnd, &rc );
            HBRUSH hbrOld = (HBRUSH)::SelectObject( hDC,
                                                    ::GetStockObject( WHITE_BRUSH ) );
            ::Ellipse( hDC, rc.left, rc.top, rc.right, rc.bottom );
            ::SelectObject( hDC, hbrOld );
            ::EndPaint( hWnd, &ps );
        }
        return 0;

    case WM_NCHITTEST:
        return HTCAPTION;

    case WM_DESTROY:
        ::PostQuitMessage( 0 );
        return 0;

    default:
        break;
    }
    return ::DefWindowProc( hWnd, message, wParam, lParam );
}


Alternative WM_PAINT handler, that outputs text. It's important to use a text color different from the key color. If you want to use black text you will have to use a different key color.
    case WM_PAINT:
        {
            PAINTSTRUCT ps = { 0 };
            HDC hDC = ::BeginPaint( hWnd, &ps );
            RECT rc = { 0 };
            ::GetClientRect( hWnd, &rc );
            ::SetTextColor( hDC, RGB( 255, 255, 255 ) );
            ::SetBkMode( hDC, TRANSPARENT );
            ::DrawTextExW( hDC, L"Hello, World!", -1, &rc,
                           DT_SINGLELINE | DT_CENTER | DT_VCENTER, NULL );
            ::EndPaint( hWnd, &ps );
        }
        return 0;


来源:https://stackoverflow.com/questions/29091028/windows-api-write-to-screen-as-on-screen-display

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!