Screen-Shots

Um einen Screen-Shot zu erhalten sind die folgenden drei Schritte durchzuführen:

  • den aktuellen Fensterinhalt in eine DBB (device dependend bitmap) kopieren.
  • die so erhaltene DBB in eine DIB (device independend bitmap) konvertieren.
  • die DIB in eine Datei schreiben.

Wir werden hier nur auf den ersten Schritt näher eingehen. Die beiden anderen Schritte wurden, mit kleinen Anpassungen, von der Web-Seite www.codeguru.com übernommen. Diese Web-Seite ist übrigens des Mekka der MFC-Programmierung. Sie enthält zu fast jedem Thema der MFC-Programmierung eine Unmenge an Tipps. Allerdings sind diese Tipps mehr für den fortgeschrittenen Programmierer. Aber das wollen Sie ja werden, sonst würden Sie sich nun vermutlich lieber Ihrer Lieblings-Freizeitbeschäftigung widmen anstatt diesem Kurs durchzuackern.

Aber sehen wir uns nun an, wie Sie den Inhalt eines Fensters ein eine Bitmap kopieren. Dazu benötigen Sie zuerst einmal wieder einen zum Fenster-DC kompatiblen Speicher-DC.   Im Anschluss daran ist eine Bitmap zu erstellen, die die gleiche Auflösung und Farbtiefe wie das Fenster besitzt, von dem ein Screen-Shot erstellt werden soll. Eine solche Bitmap erhalten Sie mittels der CBitmap Methode CreateCompatibleBitmap(...). Die so erstellte Bitmap wird dann im Speicher-DC selektiert und zum Schluss braucht dann 'nur' noch der Inhalt des Fenster in diese Bitmap mittels der bereits bekannten CDC-Methode BitBlt(...) kopiert werden. Fertig, die Bitmap enthält nun ein Abbild des Fensterinhalts. Der nachfolgende Auszug aus einen Listing zeigt diese Schritte nochmals auf:

bool ScreenShot (CWnd* pWnd, CArchive &ar)
{
   
// DC des Fensters holen
    CWindowDC CWndDC(pWnd);
   
// Speicher-DC erstellen
    CDC CMemDC;
    CMemDC.CreateCompatibleDC(&CWndDC);
    // Groesse des Fensters holen
    CRect CClientRect;
    if (pWnd == NULL)
    {
        CClientRect.left = CClientRect.top = 0;
        CClientRect.right = ::GetSystemMetrics(SM_CXSCREEN);
        CClientRect.bottom = ::GetSystemMetrics(SM_CYSCREEN);
    }
    else
        pWnd->GetClientRect(&CClientRect);
   
// Entsprechende Bitmap erstellen
    CBitmap CShotBmp;
    CShotBmp.CreateCompatibleBitmap(&CWndDC, CClientRect.Width(),CClientRect.Height());
   
// Screenshot-Bitmap in Speicher-DC selektieren
    CMemDC.SelectObject(&CShotBmp);
   
// Fensterinhalt in Bitmap kopieren
    CMemDC.BitBlt(0,0,CClientRect.Width(),CClientRect.Height(),&CWndDC,0,0,SRCCOPY);
    ....
    return true;
}

Nun muss diese Bitmap noch in eine Datei abgespeichert werden. Zum Einlesen von Bitmaps haben Sie in vorherigen Lektionen die Funktion LoadImage(...) verwendet. Doch leider gibt keine Funktion SaveImage(...) um eine Bitmap in eine Datei zu schreiben. Dies muss von Hand durchgeführt werden. Damit der Fensterinhalt auch immer farbgetreu angezeigt wird, muss die Bitmap zunächst in eine DIB umgewandelt werden. Diese DIB wird dann in eine Datei geschrieben. Diese beiden Schritte werden im Beispiel durch die Funktion DBBToDIB(...) und WriteDIB(...) durchgeführt. Damit ergibt sich folgendes endgültige Listing:

bool ScreenShot (CWnd* pWnd, CArchive &ar)
{
    ....
   
// Fensterinhalt in Bitmap kopieren
    CMemDC.BitBlt(0,0,CClientRect.Width(),CClientRect.Height(),&CWndDC,0,0,SRCCOPY);

   
// Convert the bitmap to a DIB
    CPalette pal;
    HANDLE hDIB = DDBToDIB( CShotBmp, BI_RGB, &pal );
    if( hDIB == NULL )
        return false;
   
// Write it to file
    WriteDIB( ar, hDIB );
   
// Free the memory allocated by DDBToDIB for the DIB
    GlobalFree( hDIB );

    return true;
}

Das Beispiel hierzu finden Sie unter 05GDI\ScreenShot. Als Basis diente das Beispiel GDIFunc zum Darstellen einer Messkurve. Die Screen-Shot Funktionen finden Sie in der  Datei ShootIt.cpp. Wollen Sie in einer Ihrer Anwendungen einen Screen-Shot durchführen, so fügen Sie einfach diese Datei zu Ihrem Projekt hinzu und rufen dann die Funktion ScreenShot(...) auf. ScreenShot(...) erhält als ersten Parameter einen Zeiger auf das Fensterobjekt, von dem Sie einen Screen-Shot durchführen wollen. Als zweiten Parameter erwartet die Funktion eine Referenz auf ein CArchive-Objekt, das mit der entsprechenden Datei zur Abspeicherung des Screen-Shot verknüpft ist.

Im Beispiel wird ein Screen-Shot vom aktuellen Fensterinhalt durchgeführt, wenn Sie den Menüpunkt Datei-Speichern... oder Datei-Speicher unter... auswählen. Nach der Auswahl des Menüpunktes wird durch das Rahmenprogramm der MFC die Methode Serialize(...) des Dokuments aufgerufen. Innerhalb dieser Methode wird zunächst der CWnd-Zeiger auf das Rahmenfenster der Anwendung geholt und dann der Zeiger auf das aktive Ansichtsobjekt. Dieser Zeiger wird dann an die Funktion Screen-Shot(...) übergeben.

void CGDIFuncDoc::Serialize(CArchive& ar)
{
    if (ar.IsStoring())
    {
        // ZU ERLEDIGEN: Hier Code zum Speichern einfügen
        // Zeiger auf den View holen

        CFrameWnd* pFWnd = (CFrameWnd*)(AfxGetApp()->m_pMainWnd);
        CWnd *pWnd = pFWnd->GetActiveView();
        // UpdateWindow() aufrufen damit Reste des Datei-Dialogs
        // entfernt werden!

        pWnd->UpdateWindow();
 
       // Nun ScreenShot in Datei schreiben
        ScreenShot(pWnd,ar);
    }
    else
    {
        // ZU ERLEDIGEN: Hier Code zum Laden einfügen
    }
}
Wenn Sie das Beispiel übersetzen und starten und dann den Menüpunkt Datei-Speichern... auswählen wird ein Screen-Shot des Views in der angegebenen Datei abgespeichert. Sie können auch einmal den Aufruf der Methode UpdateWindow(...) aus der Serialize(...) Methode entfernen um dessen Wirkungsweise zu erkennen.
Wollen Sie nicht nur den View sondern das gesamte Fenster als Screen-Shot abspeichern, dann übergeben Sie der Funktion ScreenShot(...) anstelle des Zeigers auf das Ansichtsobjekt einfach den Zeiger auf das Rahmenfenster, im Beispiel also den Zeiger pFWnd.
Doch das ist noch nicht alles, was die Funktion ScreenShot(...) zu leisten vermag. Sie können die Funktion ScreenShot(...) auch dazu verwenden, um vom aktuellen Desktop einen Screen-Shot zu erstellen. Dazu übergeben Sie der Funktion anstelle eines Zeigers auf ein Fensterobjekt einfach die Konstante NULL

Damit sind wir am Ende der Tipps&Tricks Reihe zum GDI angekommen und es normal mit dem Kurs weiter.



Copyright © 2004

Senden Sie Emails mit Fragen oder Kommentaren zu dieser Website an: mailto:info@cpp-tutor.de
 Wolfgang Schröder, Lerchenweg 23, D-72805 Lichtenstein. Tel: +49 7129 6470