Screenshot – wieso funktioniert das nicht?

DelphiDell

Erfahrenes Mitglied
Hi , hallo miteinander ich habe mier eine Kleine Klasse um einen Screenshot zu machen geschrieben

PHP:
#include <windows.h>
#include <string>
#include <iostream>

using namespace std;

class CStellwerk
{
private:
	HWND m_hDesktop;
public:

	void MakeScreenshot(char SavePath[]);
	void SaveScreen(HWND pScreen, char Path[]);
};

void CStellwerk::MakeScreenshot(char SavePath[])
{
	 m_hDesktop =  GetDesktopWindow();
	 if(m_hDesktop == NULL)
	 {
		 exit(1);
	 }
	 SaveScreen(m_hDesktop,SavePath);
}
	 

int main()
{
	CStellwerk Stellwerk;
	Stellwerk.MakeScreenshot("C:\\screen.bmp");
	
}


void CStellwerk::SaveScreen(HWND pScreen, char Path[])
{
	HDC hdcScreen;
    HBITMAP hbmScreen;


    //---------------Bitmap Informationen
	BITMAPINFO infobmp;
	infobmp.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	infobmp.bmiHeader.biWidth = 1024;
	infobmp.bmiHeader.biHeight = 768;
	infobmp.bmiHeader.biPlanes = 1;
	infobmp.bmiHeader.biBitCount = 24;
	infobmp.bmiHeader.biCompression = 0;
    infobmp.bmiHeader.biSizeImage = 0;
    infobmp.bmiHeader.biXPelsPerMeter = 0;
    infobmp.bmiHeader.biYPelsPerMeter = 0;
    infobmp.bmiHeader.biClrUsed = 0;
    infobmp.bmiHeader.biClrImportant = 0;

	int* bitmap = new int[1024*768*16];

	BITMAPFILEHEADER bfheader;
	
	bfheader.bfType = 19778;
	bfheader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(bitmap)+ sizeof(BITMAPINFOHEADER);
	bfheader.bfReserved1 = 0;
    bfheader.bfReserved2 = 0;
	bfheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    //Bitmap -----------------------      Informationen


    hdcScreen = GetDC(pScreen);
	hbmScreen = CreateCompatibleBitmap(hdcScreen,1024,768);

	GetDIBits(hdcScreen,hbmScreen,0,500,bitmap,&infobmp,DIB_RGB_COLORS);

	HANDLE hfile = CreateFile((LPCWSTR) Path,GENERIC_WRITE,0,0,OPEN_ALWAYS,0,0);

	//Datei Schreiben

	DWORD word;
	WriteFile(hfile,&bfheader,14,&word,NULL);
	WriteFile(hfile,&infobmp,40,&word,NULL);
	WriteFile(hfile,bitmap,750000,&word,NULL);

	ReleaseDC(pScreen,hdcScreen);
	CloseHandle(hfile);
	delete[] bitmap;
}

Das Programm Srarted ohne Fehler und Beendet wieder ohne Fehler, aber am Schluss ist keine Datei zu finden :(

//Edit hab grad gefunden wo die Datein Stecken in meine visual C++ Verzeichniss Wiso denn dass

Aber die Datein sind irgendwie etwas verkrüppelt :-

Kann sie nicht oeffnen und der Dateinahme ist dass ??????p :-

Hoffe Jemand kann mier Helfen
 
Zuletzt bearbeitet:
A) Du hast einen char-Pointer (Path) auf einen Unicode-String gecastet. Das versaut deinen Dateinamen. Wenn du ein Unicode-Projekt hast, musst du auch einen echten Unicode-Dateinamen angeben.

B) Was soll das * 16 bei dem bitmap-new? * 3 reicht für 24bit aus. Was machst du, wenn jemandes Desktop nicht 1024x768 hat (praktisch 99% aller User)?

C) sizeof(bitmap) gibt dir NICHT die Grösse des alloziierten Speicherbereichs zurück, sondern die Grösse eines Pointers.

D) Wenn du die 750000 beim Schreiben auf die echte Grösse des Buffers anpasst (1024*768*3), dann kann man die BMP schon mal laden. Beim Testen habe ich da aber noch nicht den richtigen Inhalt des Desktops gesehen.
 
Hi dass Bild sieht irgendwie besch.... aus :(

im oberen Drittel ist es grau im unteren drittel Schwart :(


//Edit liefert

GetDesktopWindow(); ueberhaubt ein Handel auf den Bildschirm zurück oder bloss auf dass fenseter ?
 
Zuletzt bearbeitet:
So, habe den Rest auch raus: Scheinbar klappt GetDIBits nicht auf dem HDC vom Desktop, dann legt man eben einen Zwischenschritt ein:


C++:
void SaveScreen(HWND pScreen, char Path[]) 
{ 
    HDC hdcScreen; 
    HBITMAP hbmScreen; 


    //---------------Bitmap Informationen 
    BITMAPINFO infobmp; 
    infobmp.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
    infobmp.bmiHeader.biWidth = 1024; 
    infobmp.bmiHeader.biHeight = 768; 
    infobmp.bmiHeader.biPlanes = 1; 
    infobmp.bmiHeader.biBitCount = 24; 
    infobmp.bmiHeader.biCompression = 0; 
    infobmp.bmiHeader.biSizeImage = 0; 
    infobmp.bmiHeader.biXPelsPerMeter = 0; 
    infobmp.bmiHeader.biYPelsPerMeter = 0; 
    infobmp.bmiHeader.biClrUsed = 0; 
    infobmp.bmiHeader.biClrImportant = 0; 

    int* bitmap = new int[1024*768*3]; 

    BITMAPFILEHEADER bfheader; 
     
    bfheader.bfType = 19778; 
    bfheader.bfSize = sizeof(BITMAPFILEHEADER) + 1024*768*3 + sizeof(BITMAPINFOHEADER); 
    bfheader.bfReserved1 = 0; 
    bfheader.bfReserved2 = 0; 
    bfheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); 
    //Bitmap -----------------------      Informationen 


    hdcScreen = GetWindowDC(pScreen); 
    hbmScreen = CreateCompatibleBitmap(hdcScreen,1024,768); 

    // temporärer DC
    HDC hdcTemp = CreateCompatibleDC( hdcScreen );

    // Bitmap reinselektieren
    HBITMAP hbmOld = (HBITMAP)SelectObject( hdcTemp, hbmScreen );

    // Inhalt von Desktop übertragen
    BitBlt( hdcTemp, 0, 0, 1024, 768, hdcScreen, 0, 0, SRCCOPY );

    int iResult = GetDIBits(hdcTemp,hbmScreen,0,768,bitmap,&infobmp,DIB_RGB_COLORS); 

    // aufräumen
    SelectObject( hdcTemp, hbmOld );
    DeleteObject( hbmScreen );
    DeleteDC( hdcTemp );

    HANDLE hfile = CreateFile( Path,GENERIC_WRITE,0,0,OPEN_ALWAYS,0,0); 

    //Datei Schreiben 

    DWORD word; 
    WriteFile(hfile,&bfheader,14,&word,NULL); 
    WriteFile(hfile,&infobmp,40,&word,NULL); 
    WriteFile(hfile,bitmap,1024*768*3,&word,NULL); 

    ReleaseDC(pScreen,hdcScreen); 
    CloseHandle(hfile); 
    delete[] bitmap; 
}
 
Hallo,

dein Code enthält einige Fehler, vergleiche mal mit meiner Variante.

Gruß
MCoder
Code:
int     nWidth  = GetSystemMetrics(SM_CXSCREEN);
int     nHeight = GetSystemMetrics(SM_CYSCREEN);

HWND    hWnd    = ::GetDesktopWindow();
HDC     hdc     = ::GetDC(hWnd);
HDC     memDC   = ::CreateCompatibleDC(hdc);
HBITMAP hbm     = ::CreateCompatibleBitmap(hdc, nWidth, nHeight);
HBITMAP hbmOld  = (HBITMAP)::SelectObject(memDC, hbm);

::BitBlt(memDC, 0, 0, nWidth, nHeight, hdc, 0, 0, SRCCOPY);
                                 
BITMAPINFO bmi;

ZeroMemory(&bmi, sizeof(bmi));

bmi.bmiHeader.biSize         = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth        = nWidth;
bmi.bmiHeader.biHeight       = nHeight;
bmi.bmiHeader.biBitCount     = 24;
bmi.bmiHeader.biPlanes       = 1;
bmi.bmiHeader.biCompression  = BI_RGB;
bmi.bmiHeader.biSizeImage    = 32 * nWidth * nHeight / 8;

BYTE *pbBits = new BYTE[bmi.bmiHeader.biSizeImage];

::GetDIBits( memDC, 
             hbm,
             0,
             bmi.bmiHeader.biHeight,
             pbBits,
             &bmi,
             DIB_RGB_COLORS );

BITMAPFILEHEADER bfh;

bfh.bfType      = ('M' << 8) + 'B';
bfh.bfSize      = sizeof(BITMAPFILEHEADER)  +
                  bmi.bmiHeader.biSizeImage +
                  sizeof(BITMAPINFOHEADER); 
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfOffBits   = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

HANDLE hfile = CreateFile( _T("c:\\temp\\screen.bmp"),
                           GENERIC_WRITE,
                           0,
                           0,
                           OPEN_ALWAYS,
                           0,
                           0 ); 

DWORD dwWritten;
 
WriteFile(hfile,&bfh,           sizeof(bfh),               &dwWritten, NULL); 
WriteFile(hfile,&bmi.bmiHeader, sizeof(BITMAPINFOHEADER),  &dwWritten, NULL); 
WriteFile(hfile,pbBits,         bmi.bmiHeader.biSizeImage, &dwWritten, NULL); 

CloseHandle(hfile); 

::SelectObject(memDC, hbmOld);
::DeleteDC(memDC);
::ReleaseDC(hWnd,hdc); 
::DeleteObject(hbm);

delete[] pbBits;
 
Der Code von MCoder ist natürlich noch besser, der geht auf die aktuelle Auflösung des Desktops ein. Nur ein kleiner Meckerer: Wieso bei biSIzeImage 32 / 8? Da ist doch ein viertel überflüssig belegt?
 
Endurion hat gesagt.:
Nur ein kleiner Meckerer: Wieso bei biSIzeImage 32 / 8? Da ist doch ein viertel überflüssig belegt?
Vollkommen richtig, sollte eigentlich auch so aussehen:
Code:
bmi.bmiHeader.biSizeImage = bmi.bmiHeader.biBitCount * nWidth * nHeight / 8;
 
Danke Funktioniert toll kann mier jemand dass noch genau erklären

PHP:
HDC hdcTemp = CreateCompatibleDC( hdcScreen );   

HBITMAP hbmOld = (HBITMAP)SelectObject( hdcTemp, hbmScreen );   
 
BitBlt( hdcTemp, 0, 0, 1024, 768, hdcScreen, 0, 0, SRCCOPY );    

int iResult = GetDIBits(hdcTemp,hbmScreen,0,768,bitmap,&infobmp,DIB_RGB_COLORS);
 
HDC hdcTemp = CreateCompatibleDC( hdcScreen );
Erstellt einen Speicher-Device-Kontext, der die gleichen Eigenschaften wie der des Desktop-Fensters hat.

HBITMAP hbmOld = (HBITMAP)SelectObject( hdcTemp, hbmScreen );
Grafikausgabe bei Windows funktioniert mit sogenannten GDI-Objekten, die vor ihrer Verwendung ausgewählt werden müssen. Dabei muss das vorher (von Windows) verwendete Objekt gespeichert und am Schluss wiederhergestellt werden. Sonst gibt's ein Speicherloch, weil Windows dieses Objekt nicht mehr verwenden und ggf. freigeben kann.
In unserem Fall benötigen wir ein Bitmap als GDI-Objekt, um die entsprechenden Daten daraus zu extrahieren.
Dieses Bitmap bekommen wir nicht vom Device-Kontext des Desktop-Fensters, sondern müssen es Speicher-Kontext selber erzeugen und dem Speicher-Kontext zuordnen.

BitBlt( hdcTemp, 0, 0, 1024, 768, hdcScreen, 0, 0, SRCCOPY );
Kopiert das Bild vom Desktop in den Speicher-Kontext.

int iResult = GetDIBits(hdcTemp,hbmScreen,0,768,bitmap,&infobmp,DIB_RGB_COLORS);
Kopiert die Bilddaten in einen Buffer. Diese Bildaten werden dann, mit einem entsprechenden Dateikopf versehen, dann als Bitmap-Datei gespeichert.

Gruß
MCoder
 

Neue Beiträge

Zurück