Windows API:以屏幕显示方式写入屏幕 [英] Windows API: write to screen as on screen display

查看:175
本文介绍了Windows API:以屏幕显示方式写入屏幕的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个(非常)小的应用程序,该应用程序在开始时只执行一些次要的事情,并且应该在屏幕上写一条消息,类似于在屏幕上显示的内容:大字样,没有任何窗口,位于所有内容的上方,对于片刻,然后消失.

如果可能的话,我不想为其创建一个窗口.

正确的方法是什么?

(我希望不需要DirectX,直接图形访问等特殊工具包)

解决方案

正如注释中指出的那样,您可以直接在屏幕上绘制. GetDC 提供了返回适当的设备上下文的方法:

hWnd [in]

要获取其DC的窗口的句柄. 如果该值为NULL,则GetDC检索整个屏幕的DC.

直接渲染到屏幕上至少需要解决两个问题:

  1. 屏幕DC是共享资源.每当其他人渲染到屏幕上时(例如,当显示一个窗口时),屏幕的该部分就会被覆盖.
  2. 渲染具有破坏性.当渲染到设备上下文中时,原始内容将被覆盖.要实现淡入淡出效果,您必须保存原始内容(并在显示其他窗口时动态更新它们).

这两个问题都可以通过创建一个窗口来解决.窗口不需要具有边框,标题栏,系统菜单或最小/最大化/关闭按钮.适当的窗口样式WS_POPUP | WS_VISIBLE.

要使该窗口显示在其他所有窗口的前面,需要将其标记为最顶部(使用WS_EX_TOPMOST SetLayeredWindowAttributes 启用Alpha透明性.要使窗口背景完全透明,而与窗口的Alpha透明度无关,您还需要启用颜色键控.一种简单的方法是设置hbrBackground成员> WNDCLASSEX结构 (HBRUSH)GetStockObject(BLACK_BRUSH) ,并指定RGB(0, 0, 0)作为对SetLayeredWindowAttributes的调用中的crKey自变量.


概念证明(为简洁起见,不进行错误检查):

#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 ) {

首先是注册主应用程序窗口类.重要的是hbrBackground成员.这样可以控制背景渲染,并最终使其完全透明.

    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 );

这是实例化窗口并调整其属性所需的所有设置代码.启用Alpha透明度可以为淡入淡出效果做准备,而颜色键控则可以遮盖窗口中未被渲染的那些区域.

    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 );

wWinMain的其余部分是样板Windows应用程序代码.

    ::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;
}

窗口过程执行简单的渲染.为了演示alpha和键颜色的透明度,该代码使用工作区作为边界矩形渲染了一个白色的椭圆.此外,还处理了 WM_NCHITTEST消息,提供一种使用鼠标或其他定点设备在整个屏幕上拖动窗口的简单方法.请注意,对于所有完全透明的区域,鼠标输入将传递到下面的窗口.

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 );
}


备用WM_PAINT处理程序,输出文本.重要的是要使用与键颜色不同的文本颜色.如果要使用黑色文本,则必须使用其他键色.

    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;

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)

解决方案

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;

这篇关于Windows API:以屏幕显示方式写入屏幕的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆