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

时间:2011-03-02 08:38:14

标签: c++ windows winapi screensaver windows-screensaver

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

3 个答案:

答案 0 :(得分:16)

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

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

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

接下来查看一些DirectX / OpenGL / SDL教程并写下一些眼睛。

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

答案 1 :(得分:3)

祝你好运。

如果你能找到真正的屏幕保护代码而不是黑客攻击,那就更好了。 这样您就可以更快地处理屏幕保护程序。

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

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

微软有opengl和directx屏幕保护程序的演示代码,如果你愿意我可以找到这些名字,因为我可能在我的电脑上有代码。使用某些项目名称可以使您的搜索更容易。

答案 2 :(得分:0)

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

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