我想使用Windows API编写Windows屏幕保护程序。我应该从哪里开始写呢?
基本上,屏幕保护程序只是一个接受一些由Windows提供的命令行选项的普通应用程序,以确定它是否应该以全屏或预览窗口启动。
因此,请编写一个普通的exe应用程序,它可以接受以下命令行参数(来自http://msdn.microsoft.com/en-us/library/ms686421(v=vs.85).aspx):
接下来,请查看一些DirectX / OpenGL / SDL教程并编写一些华丽的效果。
显然,您应该检测鼠标移动和按键操作,并在用户唤醒时退出应用程序。
祝你找到心仪的内容。
最好能找到真正的屏幕保护程序代码,而不是自己编写。 这样你就能更快地开发屏幕保护程序。
屏幕保护程序还有其他方面需要考虑。 你想要双显示器支持吗? 你会怎么做呢? 两个屏幕合并成一个大屏幕还是分别显示?
OpenGL 的处理方式与其他问题略有不同。
微软有 OpenGL 和 DirectX 屏幕保护程序的演示代码,如果你需要,我可以找出这些项目的名称,因为我可能在我的电脑上保存了这些代码。这些项目名称可以帮助你更轻松地进行搜索。
#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;
}