TUT Bildschirmschoner mit VS 2005 erstellen



  • 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


Log in to reply