Screenshot mit GDI in Endlosschleife beendet sich nach x durchläufen


  • Mod

    Mit vector wär das nicht passiert
    [/heisetrollmodus]



  • Wärs wohl doch. Auch die von Windows angeforderten Ressourcen (via GetDC, CreateCompatibleBitmap usw.) müssen wieder freigegeben werden.



  • Ich glaube, dass die Anzahl der GDI-Objekte hier eher zum Absturz führt. Es fehlen ReleaseDC und DeleteDC.
    Natürlich muss das Erwähnte auch behoben werden (std::vector!).



  • Nachtrag: Wie Belli geschrieben hat, fehlt auch noch DeleteObject für die Bitmap.

    Das Beste wäre wohl, wenn du dir kleine Wrapperklassen schreiben würdest, die das automatisch erledigen (RAII).



  • Danke schonmal für die Antworten. So schnell hatte ich garnicht mit gerechnet 🙂

    Also den Speicher löschen bringt nix.

    Also ist es sicher das was mapper und Belli sagen.

    Leider ist es das erste Programm was ich mit GDI versuche.
    Demzufolge weiß ich jetzt nicht ganz wie die Syntax aussehen sollte ReleaseDC erwartet ganz andere Datenstrukturen als die meinen.

    das ist mein aktueller code:

    #include <iostream>
    #include <fstream>
    #include <cstdint>
    #include <cassert>
    
    #include <Windows.h>
    
    void capture();
    char c;
    
    void main (){
    
    	int i =0;
    	while(1){
    	i++;
    
    	std::cout<<i<<std::endl;
    
    	capture();
    	remove("file.bmp");
    	}
    
    }
    void capture()
    {
    	HWND hWnd = FindWindow(NULL, NULL);
        HDC screen = GetDC(NULL);
        HDC target = CreateCompatibleDC(screen);
    
        /*uint32_t width = GetSystemMetrics(SM_CXSCREEN);
        uint32_t height = GetSystemMetrics(SM_CYSCREEN);*/
    
    	uint32_t width = 640;
    	uint32_t height= 480;
    
        HBITMAP bmp = CreateCompatibleBitmap(screen, width, height);
    
        SelectObject(target, bmp);
    
        int x = 0;
        x = BitBlt(target, 0, 0, width, height, screen, 10,10, SRCCOPY | CAPTUREBLT);
        assert(x);
    
    	BITMAPINFO bminfo;
        bminfo.bmiHeader.biBitCount = 16;
        bminfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    	bminfo.bmiHeader.biCompression = BI_RGB;
        bminfo.bmiHeader.biPlanes = 1;
        bminfo.bmiHeader.biWidth = width;
        bminfo.bmiHeader.biHeight = height;
        bminfo.bmiHeader.biSizeImage = width * 4 * height; // must be DWORD aligned
        bminfo.bmiHeader.biXPelsPerMeter = 0;
        bminfo.bmiHeader.biYPelsPerMeter = 0;
        bminfo.bmiHeader.biClrUsed = 0;
        bminfo.bmiHeader.biClrImportant = 0;
    
        uint8_t *bmpBuffer = new uint8_t[bminfo.bmiHeader.biSizeImage];
    
    	x = GetDIBits(target, bmp, 0, height, bmpBuffer, &bminfo, DIB_RGB_COLORS);
        assert(x);
    
        BITMAPFILEHEADER fheader;
        fheader.bfType = 0x4D42;
        fheader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + bminfo.bmiHeader.biSizeImage;
        fheader.bfReserved1 = 0;
        fheader.bfReserved2 = 0;
        fheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    
        std::ofstream bmp_file("file.bmp", std::ios_base::binary);
    
        bmp_file.write((const char *) &fheader, sizeof(fheader));
        bmp_file.write((const char *) &(bminfo.bmiHeader), sizeof(bminfo.bmiHeader));
        bmp_file.write((const char *) bmpBuffer, bminfo.bmiHeader.biSizeImage);
        bmp_file.close();
    	DeleteObject(bmp_file);
    
    	DeleteDC(target);
    	DeleteDC(screen);
    	ReleaseDC(hWnd, target);
    	ReleaseDC(hWnd, screen);
    }
    

    Evtl kann sich jemand die mühe mache und kurz ein Bsp posten.

    mit dem hier

    free(&fheader);
    

    stürzt das Programm nach dem ersten Schleifendurchlauf ab

    vielen Dank 😕



  • uint8_t *bmpBuffer = new uint8_t
    hast du wohl vergessen...

    free brauchst du in C++ nicht, dafür gibts delete. Außerdem kannst du keine Stackobjekte free'n. Du solltest dir nochmal genau anschauen, wie die Speicherverwaltung bei C++ funktioniert.

    greetz KN4CK3R



  • Du brauchst auf jeden Fall mindestens (hab nur flüchtig drübergeschaut) an geeigneter Stelle:

    ReleaseDC(0, screen);
    DeleteObject(bmp);
    DeleteDC(target);
    delete []bmpBuffer;

    Die ersten drei sind für Windows-Ressourcen, die Du anforderst (dazu empfiehlt sich immer ein Blick in die MSDN!), das letzte für dynamisch via new angeforderten Speicher.



  • Also entweder ReleaseDC (auf das Handle, das du per GetDC erhalten hast) oder DeleteDC (CreateCompatibleDC).
    Mit vector ist es auch nicht komplizierter:

    // ab deiner Zeile, die zu ersetzen ist:
    //uint8_t *bmpBuffer = new uint8_t[bminfo.bmiHeader.biSizeImage];
    vector<uint8_t> bmpBuffer(bminfo.bmiHeader.biSizeImage); 
    
    x = GetDIBits(target, bmp, 0, height, &bmpBuffer[0], &bminfo, DIB_RGB_COLORS); 
    assert(x); 
    
    BITMAPFILEHEADER fheader; 
    fheader.bfType = 0x4D42; 
    fheader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + bminfo.bmiHeader.biSizeImage; 
    fheader.bfReserved1 = 0; 
    fheader.bfReserved2 = 0; 
    fheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); 
    
    std::ofstream bmp_file("file.bmp", std::ios_base::binary); 
    
    bmp_file.write((const char *) &fheader, sizeof(fheader)); 
    bmp_file.write((const char *) &(bminfo.bmiHeader), sizeof(bminfo.bmiHeader)); 
    bmp_file.write((const char *) &bmpBuffer[0], bminfo.bmiHeader.biSizeImage); 
    
    DeleteObject(bmp);
    DeleteDC(target);
    ReleaseDC(NULL, screen);
    

    Aber wie geschrieben, kleine Wrapperklassen sind dem vorzuziehen und schnell geschrieben:

    struct HBitmapWrapper
    {
    	HBitmapWrapper(HBITMAP bm) : bm(bm) {}
    	~HBitmapWrapper() {DeleteObject(bm);}
    	operator HBITMAP() {return bm;} 
    private:
    	HBITMAP bm;
    	HBitmapWrapper(const HBitmapWrapper&);
    	HBitmapWrapper& operator=(const HBitmapWrapper& b);
    };
    

    Und im Code

    HBitmapWrapper bmp=CreateCompatibleBitmap(screen, width, height);
    

    Natürlich kann das noch weiter ausgebaut werden: Template um auch andere Handles zu verwalten (HFONT, HBRUSH, HPEn usf.), Funktion zum Freigeben mitliefern etc.



  • Nachtrag:
    Da Deine Funktion ja dann jede Sekunde aufgerufen wird, solltest Du im Taskmanager beobachten können, ob die Anzahl der benötigten GDI-Objekte immer größer wird. Falls nicht, gibst Du sie wohl alle säuberlich wieder frei, falls doch, hast Du entweder eins vergessen, oder beim Freigeben etwas falsch gemacht.



  • Und, nicht vergessen: Bei voneinander abhängigen Windows-Ressourcen immer in umgekehrter Reihenfolge löschen: Wenn du eine Bitmap mit einem HDC verknüpfst, dann muss am Ende erst der HDC gelöscht werden, dann die Bitmap.

    Und der Screen wird nicht gelöscht, sondern muss freigegeben werden:
    CreateCompatibleDC <--> DeleteDC
    GetDC <--> ReleaseDC



  • Auf gültige Rückgabewerte zu prüfen ist auch immer eine gute Idee...



  • Danke erstmal für die Hinweise.

    Das mit dem Wrapper und dem vector hat nicht funktioniert.

    Scheinbar bin ich zu unwissend was das ganze angeht.

    In Java hat das alles funktioniert 😉 (wahrscheinlich darf man das Wort hier nicht nennen)

    so sieht mein Code aus

    #include <iostream>
    #include <fstream>
    #include <cstdint>
    #include <cassert>
    
    #include <Windows.h>
    
    void capture();
    char c;
    
    void main (){
    
    	int i =0;
    	while(1){
    	i++;
    
    	std::cout<<i<<std::endl;
    
    	capture();
    	remove("file.bmp");
    	}
    
    }
    void capture()
    {
    	HWND hWnd = FindWindow(NULL, NULL);
        HDC screen = GetDC(NULL);
        HDC target = CreateCompatibleDC(screen);
    
        /*uint32_t width = GetSystemMetrics(SM_CXSCREEN);
        uint32_t height = GetSystemMetrics(SM_CYSCREEN);*/
    
    	uint32_t width = 640;
    	uint32_t height= 480;
    
        HBITMAP bmp = CreateCompatibleBitmap(screen, width, height);
    
        SelectObject(target, bmp);
    
        int x = 0;
        x = BitBlt(target, 0, 0, width, height, screen, 10,10, SRCCOPY | CAPTUREBLT);
        assert(x);
    
    	BITMAPINFO bminfo;
        bminfo.bmiHeader.biBitCount = 16;
        bminfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    	bminfo.bmiHeader.biCompression = BI_RGB;
        bminfo.bmiHeader.biPlanes = 1;
        bminfo.bmiHeader.biWidth = width;
        bminfo.bmiHeader.biHeight = height;
        bminfo.bmiHeader.biSizeImage = width * 4 * height; // must be DWORD aligned
        bminfo.bmiHeader.biXPelsPerMeter = 0;
        bminfo.bmiHeader.biYPelsPerMeter = 0;
        bminfo.bmiHeader.biClrUsed = 0;
        bminfo.bmiHeader.biClrImportant = 0;
    
        uint8_t *bmpBuffer = new uint8_t[bminfo.bmiHeader.biSizeImage];
    
    	x = GetDIBits(target, bmp, 0, height, bmpBuffer, &bminfo, DIB_RGB_COLORS);
        assert(x);
    
        BITMAPFILEHEADER fheader;
        fheader.bfType = 0x4D42;
        fheader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + bminfo.bmiHeader.biSizeImage;
        fheader.bfReserved1 = 0;
        fheader.bfReserved2 = 0;
        fheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    
        std::ofstream bmp_file("file.bmp", std::ios_base::binary);
    
        bmp_file.write((const char *) &fheader, sizeof(fheader));
        bmp_file.write((const char *) &(bminfo.bmiHeader), sizeof(bminfo.bmiHeader));
        bmp_file.write((const char *) bmpBuffer, bminfo.bmiHeader.biSizeImage);
        bmp_file.close();
    	ReleaseDC(NULL, screen);
    	DeleteDC(target); 
    	DeleteObject(bmp);
    
    }
    

    So habe ich es jetzt.
    Er läuft jetzt schon deutlich länger.

    Nur ich kann quasi zusehen, wie er den RAM voll schreibt und nicht mehr entleert.

    Evtl. kann mir jemand noch einen Hint geben 😕

    Danke schonmal



  • Es fehlt immer noch
    delete [] bmpBuffer;



  • das sieht gut aus...
    danke...

    ich werde jetzt die nacht über testen;)


Anmelden zum Antworten