如何用C++编写Windows屏幕保护程序?

10
我想使用Windows API编写Windows屏幕保护程序。我应该从哪里开始写呢?

5
你需要别人为你编写代码吗?有很多在线教程,你试过谷歌搜索吗?这个问题比较宽泛,无法在此回答。我的任务是翻译内容,不提供解释或其他信息。 - Cody Gray
3个回答

21

基本上,屏幕保护程序只是一个接受一些由Windows提供的命令行选项的普通应用程序,以确定它是否应该以全屏或预览窗口启动。

因此,请编写一个普通的exe应用程序,它可以接受以下命令行参数(来自http://msdn.microsoft.com/en-us/library/ms686421(v=vs.85).aspx):

  • /s – 以全屏模式启动屏幕保护程序。
  • /c – 显示配置设置对话框。
  • /p #### – 使用指定的窗口句柄显示屏幕保护程序的预览。

接下来,请查看一些DirectX / OpenGL / SDL教程并编写一些华丽的效果。

显然,您应该检测鼠标移动和按键操作,并在用户唤醒时退出应用程序。


4

祝你找到心仪的内容。

最好能找到真正的屏幕保护程序代码,而不是自己编写。 这样你就能更快地开发屏幕保护程序。

屏幕保护程序还有其他方面需要考虑。 你想要双显示器支持吗? 你会怎么做呢? 两个屏幕合并成一个大屏幕还是分别显示?

OpenGL 的处理方式与其他问题略有不同。

微软有 OpenGL 和 DirectX 屏幕保护程序的演示代码,如果你需要,我可以找出这些项目的名称,因为我可能在我的电脑上保存了这些代码。这些项目名称可以帮助你更轻松地进行搜索。


2
屏幕保护程序是正常的*.exe文件。只需将扩展名更改为*.scr,即可安装。 但是,您应该处理3个命令行参数以在设置中正确显示它。
\s - 以全屏模式启动屏幕保护程序(通常情况)。当您不移动鼠标和键盘一段时间或用户在屏幕保护程序设置中单击预览或双击屏幕保护程序文件时,就会出现这种情况。
\c或者根本没有参数 - 显示特定于您的屏幕保护程序的设置窗口。当用户在“屏幕保护程序设置”窗口中单击“设置”按钮时,就会出现这种情况。您可以只显示MessageBox,说明没有设置。
\p 1234 - 在预览窗口中运行屏幕保护程序。当用户打开“屏幕保护程序设置”窗口并选择您的屏幕保护程序时,就会出现这种情况。在这种情况下,您应在窗口1234中显示您的屏幕保护程序(此数字仅为示例)。数字将以十进制表示。将其转换为int,并强制转换为HWND,然后用于渲染。 有些人喜欢创建自己的窗口,并将其放置在Windows提供给您的窗口上。这两个选项都有一些缺点。
例如。为简单起见,我没有进行任何错误检查,您应该真正进行错误检查。
#include <Windows.h>
#include <d3d11.h>
#include <string>

/// DirectX 11 lib (works only in Visual Studio, in other compilers add this lib in linker settings)
/// this is just for DirectX. Skip if you are going to use different API.
#pragma comment(lib, "d3d11")


/// function for handling window messages
LRESULT WINAPI wndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ){
    switch(msg){
        case WM_MOUSEMOVE:{
            int x= LOWORD( lParam ); /// new mouse position
            int y= HIWORD( lParam );
            static int startX; /// mouse position at start of screen saver
            static int startY;
            static int timesCalled= 0; /// WM_MOUSEMOVE message is sent at creation of window, and later every time user move mouse
            if( timesCalled < 1 ){ /// remember starting position at first call
                startX= x;
                startY= y;
            }
            else if( startX != x && startY != y ){ /// if mouse was moved to different position, then exit
                ::PostQuitMessage( 0 );
            }
            timesCalled++;
        }break;
        case WM_KEYDOWN:{
            ::PostQuitMessage( 0 ); /// exit when user press any key
        }break;
        case WM_DESTROY:{ /// standard exiting from winapi window
            ::PostQuitMessage(0);
            return 0;}
    }
    return ::DefWindowProc(hWnd, msg, wParam, lParam);
}


/// starting point
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ){
    int width;
    int height;
    HWND hwnd= NULL;
    const char* windowClassName= "simpleScreenSaverInDirectX11";
    MSG msg= {};
    WNDCLASSEX wc= {};
    bool isInPreviewWindow= false; /// are we in preview in screen saver settings window
    
    /// variables for directX (ignore if you are planning to use different API or library)
    ID3D11Device* device= NULL;
    ID3D11DeviceContext* context= NULL;
    IDXGISwapChain* swapChain= NULL;
    ID3D11RenderTargetView* renderTargetView= NULL;

    /// read command line arguments
    {
        bool showSettingsDialog= true;

        /// read command line
        std::string s= std::string(lpCmdLine).substr( 0, 2 ); /// first 2 letters from command line argument
        if( s=="\\c" || s=="\\C" || s=="/c" || s=="/C" || s=="" ){
            showSettingsDialog= true;
        }else if( s=="\\s" || s=="\\S" || s=="/s" || s=="/S" ){
            showSettingsDialog= false;
        }else if( s=="\\p" || s=="\\P" || s=="/p" || s=="/P" ){
            showSettingsDialog= false;
            isInPreviewWindow= true;
            hwnd= (HWND)atoi(lpCmdLine+3);
        }

        /// show screen server settings window
        if( showSettingsDialog ){
            ::MessageBox( NULL, "There are no settings for this", "Info", MB_OK );
            return 0;
        }
    }

    /// check are we the only instance
    /// sometimes windows starts screen saver multiple times over time
    if( !isInPreviewWindow && FindWindow( windowClassName, NULL ) ){
        return -1;
    }

    /// register windows class
    if( !isInPreviewWindow ){
        wc= {sizeof(WNDCLASSEX), CS_CLASSDC, wndProc, 0, 0, hInstance, NULL, NULL, NULL, NULL, windowClassName, NULL};
        ::RegisterClassEx( &wc );
    }

    /// create window or read size
    if( !isInPreviewWindow ){
        width= GetSystemMetrics(SM_CXSCREEN);
        height= GetSystemMetrics(SM_CYSCREEN);
        hwnd= ::CreateWindow( wc.lpszClassName, "DirectX 11 Screensaver", WS_POPUP|WS_VISIBLE, 0, 0, width, height, NULL, NULL, wc.hInstance, NULL );
    }else{
        RECT rc; GetWindowRect( hwnd, &rc );
        width= rc.right - rc.left;
        height= rc.bottom - rc.top;
    }

    /// init DirectX (ignore if you are planning to use different API or library)
    {
        DXGI_SWAP_CHAIN_DESC swapChainDesc= {};
        swapChainDesc.BufferCount= 2;
        swapChainDesc.BufferDesc.Width= 0;
        swapChainDesc.BufferDesc.Height= 0;
        swapChainDesc.BufferDesc.Format= DXGI_FORMAT_R8G8B8A8_UNORM;
        swapChainDesc.BufferDesc.RefreshRate.Numerator= 60;
        swapChainDesc.BufferDesc.RefreshRate.Denominator= 1;
        swapChainDesc.Flags= DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
        swapChainDesc.BufferUsage= DXGI_USAGE_RENDER_TARGET_OUTPUT;
        swapChainDesc.SampleDesc.Count= 1;
        swapChainDesc.SampleDesc.Quality= 0;
        swapChainDesc.Windowed= TRUE;
        swapChainDesc.SwapEffect= DXGI_SWAP_EFFECT_DISCARD;
        swapChainDesc.OutputWindow= hwnd;

        D3D_FEATURE_LEVEL featureLevel;
        const D3D_FEATURE_LEVEL featureLevelArray[]= {D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_1, };
        D3D11CreateDeviceAndSwapChain( NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_DEBUG, featureLevelArray, 3, D3D11_SDK_VERSION, &swapChainDesc, &swapChain, &device, &featureLevel, &context);

        ID3D11Texture2D* pBackBuffer;
        swapChain->GetBuffer( 0, IID_PPV_ARGS( &pBackBuffer ) );
        device->CreateRenderTargetView( pBackBuffer, NULL, &renderTargetView );
        pBackBuffer->Release( );

        D3D11_VIEWPORT viewport;
        viewport.Width= float( width );
        viewport.Height= float( height );
        viewport.MinDepth= 0;
        viewport.MaxDepth= 1;
        viewport.TopLeftX= 0;
        viewport.TopLeftY= 0;
        context->RSSetViewports( 1u, &viewport );
    }

    /// show window and hide cursor
    if( !isInPreviewWindow ){
        ::ShowWindow( hwnd, SW_SHOWDEFAULT );
        ::UpdateWindow( hwnd );
        ::ShowCursor( false );
    }

    /// main loop
    while( msg.message != WM_QUIT ){
        if( ::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE) ){
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
            continue;
        }

        /// draw single color on whole window
        float clear_color[]= { (rand()%100)/100.0, (rand()%100)/100.0, (rand()%100)/100.0, 0.0 };
        context->ClearRenderTargetView( renderTargetView, (float*)clear_color );
        if( swapChain->Present( 1, 0 ) != S_OK )
            break; /// probably we were in preview and user have closed settings window. exiting.
    }

    /// shutdown
    renderTargetView->Release();
    swapChain->Release();
    context->Release();
    device->Release();
    if( !isInPreviewWindow ){
        ::ShowCursor( true );
        ::DestroyWindow( hwnd );
        ::UnregisterClass( windowClassName, hInstance );
    }
    return msg.wParam;
}

如果您想在多个显示器上显示屏幕保护程序,您需要创建多个窗口,每个窗口对应一个显示器,并分别渲染每个窗口(有时可以在窗口之间共享资源),就像使用多个显示器的普通应用程序一样。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接