如何在 C++ 中为 Windows 编写屏幕保护程序?

Posted

技术标签:

【中文标题】如何在 C++ 中为 Windows 编写屏幕保护程序?【英文标题】:How can I write a screen saver for Windows in C++? 【发布时间】:2011-03-02 08:38:14 【问题描述】:

我想使用 Windows API 为 Windows 编写屏幕保护程序。怎么开始写呢?

【问题讨论】:

您希望有人为您编写代码吗?网上有很多教程;你试过谷歌搜索吗?这是一个相当广泛的问题,需要在这里回答。 【参考方案1】:

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

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

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

接下来查看一些 DirectX / OpenGL / SDL 教程并写一些吸引眼球的东西。

显然,如果用户醒来,您应该检查鼠标移动和按键并退出您的应用程序。

【讨论】:

【参考方案2】:

祝您搜索顺利。

如果你能找到真正的屏幕保护程序代码而不是破解一些东西,那就更好了。 这样您就可以更快地使用屏幕保护程序。

屏幕保护程序不仅仅是修改过的 exe。 你想要双显示器支持吗? 你会怎么做? 两个屏幕作为一个大屏幕还是两个屏幕作为单独的屏幕?

Opengl 做的事情有点不同,还有其他问题。

Microsoft 有 opengl 和 directx 屏幕保护程序的演示代码,如果您需要,我可以找到名称,因为我的计算机上的某个地方可能有代码。使用一些项目名称可以使您的搜索更容易。

【讨论】:

【参考方案3】:

屏幕保护程序是正常的 *.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;

如果您想在多个监视器上显示屏幕保护程序,您必须创建多个窗口,每个监视器一个,并分别渲染每个窗口(有时您可以在窗口之间共享资源)。与使用多台显示器的正常应用完全一样。

【讨论】:

以上是关于如何在 C++ 中为 Windows 编写屏幕保护程序?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Windows 7 上的 C++ 中为虚拟文件夹创建快捷方式?

在 C++ 中为 Windows CE 在 Linux 中开发

如何在 C++ 中为矩阵类型构建构造函数

将互斥保护构建到 C++ 类中的线程安全方法?

在 C++ 中为链表类创建实例

用于 linux 的 C++ 中的屏幕分辨率 [关闭]