简体   繁体   English

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

[英]How can I write a screen saver for Windows in C++?

I want to write a screen saver for Windows using the Windows API.我想使用 Windows API 为 Windows 编写屏幕保护程序。 How can I start to write it?我怎样才能开始写呢?

Basically a screensaver is just a normal application that accepts a few command line options, provided by windows, to determine if it should start fullscreen or in a preview window.基本上,屏幕保护程序只是一个普通的应用程序,它接受一些由 Windows 提供的命令行选项,以确定它是应该全屏启动还是在预览窗口中启动。

So, write a normal exe-application that takes the following command line arguments (from http://msdn.microsoft.com/en-us/library/ms686421(v=vs.85).aspx ):因此,编写一个普通的 exe 应用程序,它采用以下命令行参数(来自http://msdn.microsoft.com/en-us/library/ms686421(v=vs.85).aspx ):

  • /s – Start the screensaver in full-screen mode. /s – 以全屏模式启动屏幕保护程序。
  • /c – Show the configuration settings dialog box. /c – 显示配置设置对话框。
  • /p #### – Display a preview of the screensaver using the specified window handle. /p #### – 使用指定的窗口句柄显示屏幕保护程序的预览。

Next check out some DirectX / OpenGL / SDL tutorials and write some eye candy.接下来查看一些 DirectX / OpenGL / SDL 教程并写一些养眼的东西。

Obviously you should check for mouse movements and key presses and exit your application if the user wakes up.显然,您应该检查鼠标移动和按键,并在用户醒来时退出您的应用程序。

Good luck on your search.希望你能很快找到。

Its better if you can find real screensaver code instead of hacking up something.如果您能找到真正的屏幕保护程序代码而不是破解某些东西,那就更好了。 This way you can work on the screensaver faster.这样您就可以更快地处理屏幕保护程序。

There is more to screensavers than just modified exes.屏幕保护程序不仅仅是修改过的 exe。 Do you want dual monitor support?你想要双显示器支持吗? How would you do it?你会怎么做? Both screens as one big screen or both screens as separate screens?两个屏幕作为一个大屏幕还是两个屏幕作为单独的屏幕?

Opengl does things a bit differently and there are other issues. Opengl 做的事情有点不同,还有其他问题。

Microsoft had demo code for opengl and directx screensavers, if you want I can find the names because I might have the code on my computer somewhere.微软有 opengl 和 directx 屏保的演示代码,如果你愿意,我可以找到名称,因为我的电脑上可能有代码。 Could make your search easier with some project names.可以使用一些项目名称使您的搜索更容易。

Screensaver is normal *.exe file.屏保是正常的*.exe文件。 Just change extension to *.scr, and you will be able to install it.只需将扩展名更改为 *.scr,您就可以安装它。 However you should handle 3 command line arguments to show it correctly in settings.但是,您应该处理 3 个命令行参数以在设置中正确显示它。

\\s - Start screen saver in full screen (usual case). \\s - 全屏启动屏幕保护程序(通常情况下)。 This is when you don't move your mouse and keyboard for a while, or if user click preview in screen saver settings, or if user double click screen saver file.这是当您一段时间不移动鼠标和键盘时,或者如果用户在屏幕保护程序设置中单击预览,或者如果用户双击屏幕保护程序文件。

\\c or no argument at all - Show settings window specific for your screen saver. \\c或根本没有参数 - 显示特定于您的屏幕保护程序的设置窗口。 This is when user will click "settings" button in "Screen Saver Settings" window.这是用户将在“屏幕保护程序设置”窗口中单击“设置”按钮的时间。 You can just display MessageBox saying that there are no settings.您可以只显示 MessageBox 说没有设置。

\\p 1234 - Run screen saver in preview window. \\p 1234 - 在预览窗口中运行屏幕保护程序。 This is when user will open Screen Saver Settings window, and select your screen saver.此时用户将打开“屏幕保护程序设置”窗口,然后选择您的屏幕保护程序。 In this case you should display your screen saver in window 1234 (this number is just example).在这种情况下,您应该在窗口 1234 中显示您的屏幕保护程序(这个数字只是示例)。 Number will be in decimal.数字将以十进制表示。 Convert it to int, and cast to HWND, and use for rendering.将其转换为 int,并转换为 HWND,并用于渲染。 Some people like to create they own window, and place it over window give to you by Windows.有些人喜欢创建他们自己的窗口,并将其放置在 Windows 提供给您的窗口上。 Both options have some downsides.这两种选择都有一些缺点。

Example.例子。 For simplicity I didn't do any error checking, and you should really do error checking:为简单起见,我没有做任何错误检查,你真的应该做错误检查:

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

If you want to display your screensaver on multiple monitors you have to create multiple windows, one for each monitor, and render each window separately (sometimes you can share resources between windows).如果您想在多个显示器上显示您的屏幕保护程序,您必须创建多个窗口,每个窗口一个,并分别渲染每个窗口(有时您可以在窗口之间共享资源)。 Exactly like in normal application using multiple monitors.与使用多台显示器的普通应用程序完全一样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM