我想使用 Windows API 为 Windows 编写屏幕保护程序。我怎样才能开始写呢?
3 回答
基本上,屏幕保护程序只是一个普通的应用程序,它接受一些由 Windows 提供的命令行选项,以确定它是应该全屏启动还是在预览窗口中启动。
因此,编写一个普通的 exe 应用程序,它采用以下命令行参数(来自http://msdn.microsoft.com/en-us/library/ms686421(v=vs.85).aspx):
- /s – 以全屏模式启动屏幕保护程序。
- /c – 显示配置设置对话框。
- /p #### – 使用指定的窗口句柄显示屏幕保护程序的预览。
接下来查看一些 DirectX / OpenGL / SDL 教程并写一些吸引眼球的东西。
显然,如果用户醒来,您应该检查鼠标移动和按键并退出您的应用程序。
希望你能很快找到。
如果你能找到真正的屏幕保护程序代码而不是破解一些东西,那就更好了。这样您就可以更快地使用屏幕保护程序。
屏幕保护程序不仅仅是修改过的 exe。你想要双显示器支持吗?你会怎么做?两个屏幕作为一个大屏幕还是两个屏幕作为单独的屏幕?
Opengl 做的事情有点不同,还有其他问题。
Microsoft 有 opengl 和 directx 屏幕保护程序的演示代码,如果您愿意,我可以找到名称,因为我的计算机上的某个地方可能有代码。使用一些项目名称可以使您的搜索更容易。
屏幕保护程序是正常的 *.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;
}
如果你想在多个监视器上显示你的屏幕保护程序,你必须创建多个窗口,每个监视器一个,并分别渲染每个窗口(有时你可以在窗口之间共享资源)。就像在使用多个显示器的正常应用程序中一样。