M
Ich möchte hier kruz die Informationen zusammentragen, die man benötigt, um ein eigenes Screensaver-Projekt mit Hilfe der scrnsave.lib zu erstellen.
Warum scrnsave.lib?: Theoretisch kann man auch einfach nur eine gewöhnliche .exe entwickeln und diese in .scr umbenennen. Das funktioniert zwar, aber es werden dann nicht von Haus aus alle Besonderheiten berücksichtigt, wie das Füllen des Vorschaufensters, der Anzeigename im Auswahldialog, etc. Hierfür bietet die Umgebung des VS eine scrnsave.h nebst scrnsave.lib an (unter VS 2005 sind sie nicht direkt dokumentierte; nur über die msdn - siehe die Links am Ende des Beitrags). Das folgende Projekt nutzt diese Vorlage. Die lib enthält die main und den Rumpf der Anwendung. Wird sie eingebunden, wird aus dem folgenden Code die Funktion ScreenSaverProc() geladen, sobald der Bildschirmschoner gestartet werden soll, oder die Funktion ScreenSaverConfigureDialog(), sobald der Konfigurationsdialog des Bildschirmschoners angezeigt werden soll. Der Beispielcode zeigt den grundlegenden Umgang damit.
Funktionsumfang: Der folgende Bildschirmschoner macht nichts weiter, als den Monitor auszuschalten (genau genommen wird die Bidlschirmausgabe abgeschaltet; aktuelle Monitore gehen daraufhin in den Standbymodus); eine Mausbewegung oder eine Tasteneingabe schaltet den Monitor wieder ein:
1. Ein leeres Projekt erstellen (VS .NET 2005 starten / Datei / neu / Win32 / Win32-Projekt; Anwendungseinstellung: „Windows Anwendung“ und „Leeres Projekt“ auswählen).
2. main.h mit folgendem Inhalt anlegen
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
#include <scrnsave.h>
#ifdef UNICODE
#pragma comment(lib, "Scrnsavw.lib")
#else
#pragma comment(lib, "scrnsave.lib")
#endif
#pragma comment(lib, "comctl32.lib")
#include "resource.h"
3. main.cpp mit folgendem Inhalt anlegen
#include "Main.h"
LRESULT WINAPI
ScreenSaverProc( HWND hWnd, UINT uiMessage, WPARAM wParam, LPARAM lParam ){
bool bCallDefaultScrMsgProvider=true;
static POINT ptSavedMousePos;
static UINT_PTR uipTimer=NULL;
static int iPowerMode=2; //1 oder 2=Power off; -1=Power on
static bool bIgnoreMouse=false; //true=Mausbewegung unterbricht den Screensaver nicht
static bool bScreenSaverIsRunning=false;
switch( uiMessage ) {
case WM_PAINT: { //Fenster wird gezeichnet
bCallDefaultScrMsgProvider=false;
PAINTSTRUCT ps={ NULL };
HDC hWndDC=BeginPaint( hWnd, &ps ); //Begin Paint Procedure
if( fChildPreview ) { //Desktop / Einstellungen / Bilschrimschonerdialog -> wir befinden uns im kleinen Vorschaubildschirm
//Beispielcode, um dort ein Bild zu laden:
BITMAP BitMap;
HBITMAP hBitMap=LoadBitmap( hMainInstance, MAKEINTRESOURCE( IDB_PREVIEW_BITMAP ) ); //Bild aus der Resource laden
HDC hBitMapDC=CreateCompatibleDC( NULL );
HBITMAP hBitMapFromDC=SelectBitmap( hBitMapDC, hBitMap );
GetObject( hBitMap, sizeof( BitMap ), &BitMap );
int iPosX=0;
int iPosY=0;
BitBlt( hWndDC, iPosX, iPosY, BitMap.bmWidth, BitMap.bmHeight, hBitMapDC, 0, 0, SRCCOPY ); //Bild anzeigen
DeleteDC( hBitMapDC );
CloseHandle( hBitMap );
CloseHandle( hBitMapDC );
} else { //Der Bildschirmschoner soll angezeigt werden
Sleep(1000); //sonst WM_DESTROY (warum auch immer)
if ( !uipTimer) { uipTimer=SetTimer( hWnd, 555, 0, NULL ); } //Send WM_TIMER now; =>Bildschirm ausschalten
}
CloseHandle(hWndDC);
EndPaint( hWnd, &ps ); //End Paint Procedure
}
break; //EndCase WM_PAINT of uiMessage
case WM_TIMER: //Windows schaltet den Monitor gelegentlich wieder ein. Daher: So lange der Bildschirmschoner läuft, wird der Monitor hier immer wieder ausgeschaltet...
bCallDefaultScrMsgProvider=false;
uipTimer=SetTimer( hWnd, 555, 10000, NULL ); //WM_TIMER alle 10 Sekunden
if ( !bScreenSaverIsRunning ) {
bScreenSaverIsRunning=true;
GetCursorPos(&ptSavedMousePos); //Mauspositionsdaten sichern
}
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, iPowerMode); //Bildschirm ausschalten
break; //EndCase WM_TIMER of uiMessage
case WM_KEYDOWN: //Eine Taste wurde betätigt; Beispiel zum Testen ob die Leertaste betätigt wurde: if (wParam == VK_SPACE) { ... }
bCallDefaultScrMsgProvider=false;
DestroyWindow(hWnd); //Das Programm beenden
PostQuitMessage(0);
break; //EndCase WM_KEYDOWN of uiMessage
case WM_MOUSEMOVE: //Die Maus wurde bewegt
bCallDefaultScrMsgProvider=false;
if (bScreenSaverIsRunning && bIgnoreMouse) { //Soll ignoriert werden? Dann Monitor wieder ausschalten...
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, iPowerMode);
} else if (bScreenSaverIsRunning) {
if ( (abs(ptSavedMousePos.x - GET_Y_LPARAM(lParam)) > 50) || (abs(ptSavedMousePos.y - GET_Y_LPARAM(lParam)) > 50) ) {
DestroyWindow(hWnd);
PostQuitMessage(0);
} else {//Bewegung zu klein; Bildschirm wieder ausschalten...
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, iPowerMode);
}
} //EndIf( bScreenSaverIsRunning )
break; //EndCase WM_MOUSEMOVE of uiMessage
case WM_CLOSE: //Fenster soll geschlossen werden
if ( bScreenSaverIsRunning ) {
//Wenn der Monitor ausgeschaltet wird per
// SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, iPowerMode);
//(siehe unter case WM_TIMER), dann wird automatisch WM_CLOSE ausgelöst (warum auch immer). Damit sich der
//Bildschirmschoner nicht beendet, wird die Nachricht in diesem Fall hier abgefangen und ignoriert.
bCallDefaultScrMsgProvider=false;
} else {
if( uipTimer ) { KillTimer( hWnd, uipTimer ); }
}
break; //EndCase WM_CLOSE of uiMessage
case WM_DESTROY: //Fenster wurde geschlossen
if( uipTimer ) { KillTimer( hWnd, uipTimer ); }
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, -1); //Send Monitor-Power-ON
break; //EndCase WM_DESTROY of uiMessage
default:
break;
} //EndOf switch( uiMessage )
if ( bCallDefaultScrMsgProvider ) {
return ( DefScreenSaverProc( hWnd, uiMessage, wParam, lParam ) );
} else {
return ( FALSE );
}
}
BOOL WINAPI
ScreenSaverConfigureDialog( HWND hDlg, UINT uiMessage, WPARAM wParam, LPARAM lParam ) {
switch( uiMessage ) {
case WM_INITDIALOG: //Dialog-Initialisierung
SetWindowText(hDlg, _T("Fenstertitel"));
break; //EndCase WM_INITDIALOG of uiMessage
case WM_COMMAND: //Ein Ereignis, wie OnCancel, ist aufgetreten
switch ( LOWORD( wParam ) ) {
case IDCANCEL: //On Cancel oder Close
EndDialog(hDlg, TRUE);
return TRUE;
break; //EndCase IDCANCEL of wParam
} //EndOf switch( wParam )
break; //EndCase WM_COMMAND of uiMessage
} //EndOf switch( uiMessage )
return FALSE;
}
BOOL WINAPI
RegisterDialogClasses( HANDLE hInst ){
return TRUE;
}
4. Die Dateien im Projekt einbinden (Projektmappen-Explorer / rechte Maustaste auf Quelldateien / hinzufügen / vorhandenes Element: main.h, main.cpp).
5. Die Projekteigenschaften anpassen (Alt+F7, hier „Alle Konfigurationen“ auswählen, da diese Einstellungen gemeinsam für den Debug- und Release-Modus bestimmt sind; Konfigurationseigenschaften / C++ / Codegenerierung / Laufzeitbibliothek: Multithreaded (/MT)).
6. Den Konfigurationsdialog erzeugen (Projektmappen-Explorer / rechte Maustaste auf Ressourcendateien / Hinzufügen / Ressource / Dialog / neu; es wird dadurch automatisch ein Standarddialog angelegt mit OK und Abbrechen-Taste). Das Projekt muss nun gespeichert werden. Nun lässt sich die Datei resource.h öffnen (zu finden unter Projektmappen-Explorer / Headerdateien). Hier muss der Eintrag „#define IDD_DIALOG1 101“ geändert werden in „#define IDD_DIALOG1 2003“ (Hintergrund: der Screensaver sucht später nach dieser ID-Nummer, um den Konfigurationsdialog zu starten).
7. Den Namen festlegen, der im Auswahldialog für den Bildschirmschoner angezeigt werden soll: Die Datei resource.h öffnen und den Eintrag "#define IDS_DESCRIPTION 1" hinzufügen. Die Datei <projektname>.rc in einem Editor öffnen (Projektmappen-Explorer / Ressourcendateien / rechte Maustaste auf <projektname>.rc / Code anzeigen), dort nach der Zeile „#endif // Deutsch (Deutschland) resources“ suchen und darüber die folgenden Codezeilen einfügen:
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE
BEGIN
IDS_DESCRIPTION "<gewünschter Anzeigename Deines Screensavers>"
END
Hinweis: Der Dateiname (<name>.scr) darf nicht länger als 8 Zeichen lang sein, sonst wird der oben gewählte Titel nicht angezeigt.
8. Das kleine Vorschaubild erzeugen: Eine 152 x 111 große BMP-Datei mit 256 optimierten Farben erstellen und im Projektverzeichnis unter Vorschau.bmp ablegen. Die Datei <projektname>.rc abermals öffnen und direkt über den oben eingefügten Code („String Table“) den folgenden Code hinzufügen:
/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//
IDB_PREVIEW_BITMAP BITMAP "Vorschau.bmp"
Dann die Datei resource.h öffnen und dort den Eintrag „#define IDB_PREVIEW_BITMAP 100“ hinzufügen.
Jetzt sollte sich der Bildschirmschoner erstellen lassen. Startet ihr den Aufruf ohne Parameter oder mit dem Parameter „/c“, so sollte der Konfigurationsdialog erscheinen. Mit dem Parameter „/s“ schaltet er den (oder die) Monitor(e) ab.
Benennt die .exe einfach in .scr um und kopiert die Datei in das System32-Verzeichnis von Windows. Dann sollte der Bildschirmschoner in eurem Auswahldialog erscheinen.
Hinweis: Dies ist eine reine Win32-Anwendung; die MFC wird nicht unterstützt.
Viel Spaß damit, MarviESC
Hilfreiche Links:
http://msdn.microsoft.com/en-us/library/cc144066(VS.85).aspx
http://support.microsoft.com/kb/126239/en-us/
http://msdn.microsoft.com/en-us/library/bb762098(VS.85).aspx
http://msdn.microsoft.com/en-us/library/bb762097(VS.85).aspx