Problem mit Screenshot speichern BitBlt / StretchBlt



  • Ist es nicht ratsamer statt GetDC (Client-Bereich) lieber GetWindowDC (gesamtes Fenster) zu verwenden ? Bist Du mit dem Ergebnis zufrieden ?



  • Also bisher war ich mit dem was die Funktion mit GetDC() macht zufrieden...
    Da ich ja auch den gesamten Desktop als Screenshot haben will (der hat ja nur einen Client-Bereich), macht das meiner Meinung nach keinen Unterschied (wenn ich das von der MSDN Library jetzt auf die Schnelle richtig verstanden habe)...

    Aber ich werde das morgen mal ausprobieren und gucken, ob es da einen Unterschied gibt...



  • So ich habe das jetzt drei Varianten ( GetDC(GetDesktopWindow()), GetWindowDC(GetDesktopWindow()) und CreateDC("DISPLAY", NULL, NULL, NULL) )ausprobiert, den HDC zu ermitteln und alle liefern das gleiche Ergebnis.

    Das Ergebnis ist (für meine Zwecke zumindest) ausreichend, außer dass ich feststellen musste, das mit dieser Methode keine Fenster auf dem Screenshot zu sehen sind, die Transparenz verwenden (z.B. WinAMP oder Miranda IM)...

    Kann mir jemand sagen, warum das so ist und wie man diese Fenster mit auf den Screenshot bekommt? Wenn man die DRUCK-Taste verwendet klappts ja schließlich auch 😕 ...

    Gibt es eine (einfachere) Möglichkeit, als einen Tastendruck zu simulieren (das würde ich noch relativ schnell hinbekommen mit SendInput() ) und aus dem Clipboard ein HBITMAP rauszuholen (das wird für mich schon wesentlich problematischer...).

    Vielen Dank schon mal



  • Was ist an GetDC(0) verkehrt?
    Probier das mal...



  • @hustbaer
    Ja das hab ich auch probiert (war die vierte in Vergessenheit geratene Variante GetDC(NULL) )... aber danke

    @all
    Und nun bin ich richtig verwirrt... ich habe mir das jetzt hingebastelt mit dem simulierten Tastendruck auf DRUCKEN und dem speichern der Daten aus dem Clipboard (siehe Code unten)... und immer noch werden die transparenten Fenster nicht angezeigt... Wenn ich das manuell mache und das Clipboard in Paint einfüge sind die Daten da (auch ohne dass ich noch mal DRUCKEN drücke)!!! 😕 (habe auch mal zwei Screenshots angefügt, damit man sieht was ich meine... beide vom selben Clipboard!)...

    Und jetzt stehe ich wirklich wie der Ochs vorm Berg und weiß nicht mehr weiter... Hat irgendjemand eine Idee was da falsch sein kann? Kann ja jetzt nur noch das BitBlt / StretchBlt oder das Speichern als Datei sein... Aber da habe ich keine Ahnung was da nicht stimmen sollte 😞

    Oder sollte es letzten Endes daran liegen, dass ich den gcc aus dem MinGW-Paket verwende? Aber das kann ich mir auch nicht vorstellen, sind ja die selben DLLs die aufgerufen werden, egal welcher Compiler...

    Hier die Screenshots:
    Manuell mit Paint:
    www.kevin-hallbauer.de/download/clipbrd_manuell.png

    Mit der Funktion:
    http://www.kevin-hallbauer.de/download/clipbrd_Function.png

    Und hier der Code mit dem simulierten Tastendruck.
    (nicht über die while-Schleife wundern, ist nur eine quick&dirty Lösung für eine Wartezeit, da ohne die Schleife immer die MessageBox gekommen ist... schöner wär natürlich den Thread ein paar Millisekunden schlafen zu legen, aber das geht noch weit über meine Fähigkeiten hinaus (bzw. hab ich mich noch nicht drum gekümmert))

    void ClipboardSaveWindowBitmap(HWND hWnd, LPCTSTR lpszFile) {
        // Simulate PRINTSCR
        INPUT inp[2];
    	memset(inp,0,sizeof(INPUT));
    	inp[0].type = INPUT_KEYBOARD;
    	inp[0].ki.wVk       = VK_SNAPSHOT;
    	inp[1] = inp[0];
    	inp[1].ki.dwFlags |= KEYEVENTF_KEYUP;
    
        ::SendInput(2, inp, sizeof(INPUT));
    
        int i = 0;
        while(i++ < 1000);  // warten bis Windows Clipboard Daten hat
    
        /////////////////////////////////////////////////////////////////
        // Bild aus Clipboard holen
        /////////////////////////////////////////////////////////////////
        BITMAPINFO *hBmpInfo;
        //Prüfen ob ein Bitmap eingelesen wurde
    	OpenClipboard(hWnd);
        hBmpInfo = (BITMAPINFO *) GetClipboardData( CF_DIB);
    	CloseClipboard();
    
        if(hBmpInfo == NULL) {
            char buffer[1024];
            sprintf(buffer, "HBITMAPINFO from clipboard is NULL: %i", hBmpInfo);
            MessageBox(NULL, buffer, "Fehler", MB_OK);
            return;
        }
    
        // Änderung: Adresse der Pixeldaten
        void *vpBitmap = &( hBmpInfo->bmiColors[0] );
        int nWidth  = hBmpInfo->bmiHeader.biWidth;
        int nHeight = hBmpInfo->bmiHeader.biHeight;
    
        HDC     hdc     = ::GetDC(NULL);
        HDC     memDC   = ::CreateCompatibleDC(hdc);
        HBITMAP hbm     = CreateDIBitmap(   hdc,
                                            (BITMAPINFOHEADER*) hBmpInfo,
                                            CBM_INIT,
                                            vpBitmap,
                                            hBmpInfo,
                                            0 );
     //   HBITMAP hbm    = ::CreateCompatibleBitmap(hdc, nWidth, nHeight);
        HBITMAP hbmOld = (HBITMAP) ::SelectObject(memDC, hbm);
    
        ::BitBlt(memDC, 0, 0, nWidth,  nHeight, hdc, 0, 0, SRCCOPY);
        /////////////////////////////////////////////////////////////////
    
        // Bilddaten ermitteln
        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);
    
        /////////////////////////////////////////////////////////////////
        // Bilddaten speichern
        HANDLE hfile = CreateFile(  lpszFile,
                                    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);
        ::DeleteObject(hbm);
        delete[] pbBits;
    }
    


  • Bist du sicher dass es nicht daran liegt dass du das Programm mit "Play" startest, und WinAmp dadurch in den Hintergrund geht? 🙂

    Davon abgesehen solltest du schon länger als "int i = 0; while(i++ < 1000);" warten, der Compiler optimiert dir das im Release komplett weg. Angebracht wäre wohl eher "Sleep(100);" oder sowas.



  • Ja ich bin mir seeehr sicher, dass WinAMP "Always on top" ist... die Option ist nämlich aktiviert und WinAMP auch sichtbar, wenn ich im Quellcode rumwurschtel... 😉

    Das mit der while()-Schleife habe ich mir schon fast gedacht, hatte halt gestern keine Zeit (und Lust) so spät abends mich noch mit dem Threads zu beschäftigten (auch wenns wohl mit Sleep(n) wohl ganz einfach ist)... War dann wohl eher Glück, dass die MessageBox dann nicht aufgetaucht ist 😉



  • Hallo,

    eins würde ich auf jeden Fall gerne wissen, und zwar ob mein Problem schon von anderen beobachtet wurde.
    Irgendwie fühle ich mich mit dem Problem allein auf weiter Flur, weil überall im Internet immer diese Lösung für einen Screenshot mit C/C++ präsentiert wird... Und nirgends wird irgendwas von Problemen mit transparenten Fenstern erwähnt.

    Nicht mal die Screenshot-Demo von QT4 macht bei mir richtige Screenshots...

    Weil wenn bei anderen der oben gepostete Code funktioniert, dann müsste ich mal meinen PC untersuchen...

    Wäre wirklich dankbar, wenn sich hier wer melden würde, der das gleiche Problem hat...

    Danke..



  • Dein StretchedSaveWindowBitmap funktioniert bei mir auch nicht mit halbdurchsichtigen Fenstern.



  • Immerhin stehe ich dann nicht ganz alleine da... Aber wissen woran das liegt würde ich schon sehr gerne...
    Hat denn hier wirklich niemand eine Idee, warum das nicht mit transparenten Fenstern funktioniert?

    Wäre wirklich für jeden Hinweis dankbar, auch wenns nur ein Stichwort ist... Dann wüsste ich wenigstens, wonach ich weiter suchen kann...


Anmelden zum Antworten