Probleme mit GDIplus und image.save



  • Heyho,

    ich habe folgendes Problem: ich schreibe eine Anwendung, die in bestimmten Abständen von einer anderen Anwendung Screenshots erstellt und diese speichert. Die Synchronisation erfolgt mit Hilfe der API dieser besagten Anwendung.
    Zum Erstellen des Screenshots und Speichern desselben habe ich eine Klasse "Screenshot" geschrieben.

    Nun habe ich also eine Schleife, in der ich alle 0.002 Sekunden (änderbarer Time-Intervall) das Bild speichere. Merkwürdigerweise aber werden nur 69 Bilder gespeichert. Der Schleifeninhalt ist (vereinfacht):

    record = true;
    for(int count = Anzahl;Anzahl!=0;Anzahl--){
    	if(record){
    		Screenshot* oScreenshot = new Screenshot();
    		oScreenshot->dump(targetFile.str().c_str());
    		delete oScreenshot;
    	}
    }
    

    Die Schleife läuft korrekt durch.
    Die weiter unten sichtbare Funktion "save" wird ausgeführt, das habe ich überprüft. Übrigens ist es egal, welchen image-encoder ich verwende, bmp, gif, png, alle liefern dasselbe ergebnis.
    Da ich absoluter C++-Neuling bin (zuvor java, .net) bitte ich Euch, mal zu schauen ob ihr eine Idee habt. Und wenn ich grob fahrlässige, oder nicht den C++ Konventionen entsprechende Code-Fehler gemacht habe, bitte sagt es mir, ich lerne wie gesagt noch! Vielen Dank.

    Die Klasse Screenshot seht ihr hier:

    #include <stdafx.h>
    #include <iostream>
    #include <atlbase.h>
    #include <string.h>
    
    #using <mscorlib.dll>
    
    #include "../inc/Screenshot.h"
    #include <gdiplus.h>
    
    using namespace std;
    using namespace Gdiplus;
    using namespace System;
    
    HBITMAP hBitmap;
    HPALETTE hPalette;
    
    Screenshot::Screenshot(){
    }
    
    Screenshot::Screenshot( const Screenshot &right ){
    
    }
    
    Screenshot::~Screenshot(){
    
    }
    
    const Screenshot& Screenshot::operator=(const Screenshot &right){
    
    	return *this;
    }
    
    // Fkt. Erstellt Screenshot
    void Screenshot::dump(CString fileName)
    {
    	HDC hdcSrc = GetWindowDC(GetDesktopWindow()),
    	hdcDest = CreateCompatibleDC(hdcSrc);
    	HBITMAP hBitmap = CreateCompatibleBitmap(hdcSrc,
    	GetDeviceCaps(hdcSrc,8),GetDeviceCaps(hdcSrc,10));
    	SelectObject(hdcDest,hBitmap);
    	BitBlt(hdcDest,0,0,GetDeviceCaps(hdcSrc,8),
    					GetDeviceCaps(hdcSrc,10),
    					hdcSrc,0,0,0x00CC0020);
    	save(hBitmap,fileName);
    
    };
    
    // Fkt. speichert übergebenen Bitmap-Handler ab
    void Screenshot::save(HBITMAP hBitmap, CString fileName)
    {
    	CDC MemDC;
    	CRect rcSel;
    
    	//GID+
    	GdiplusStartupInput gdiplusStartupInput;
    	ULONG_PTR gdiplusToken;
    	GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    
    	Bitmap *pBitmap = Bitmap::FromHBITMAP(
                (HBITMAP)hBitmap,
                (HPALETTE)hPalette);	
    
    	CLSID Clsid;
    	GetEncoderClsid(L"image/png", &Clsid);
    
    	long nCompress = 0;	
    	EncoderParameters encoderParameters[2];
    	encoderParameters[0].Count = 2;
    	encoderParameters[0].Parameter[0].Guid = EncoderQuality;
    	encoderParameters[0].Parameter[0].NumberOfValues = 1;
    	encoderParameters[0].Parameter[0].Type = EncoderParameterValueTypeLong;
    	encoderParameters[0].Parameter[0].Value = &nCompress;
    
    	int nDep = 1;
    	encoderParameters[1].Parameter[0].Guid = EncoderColorDepth;
    	encoderParameters[1].Parameter[0].NumberOfValues = 1;
    	encoderParameters[1].Parameter[0].Type = EncoderParameterValueTypeLong;
    	encoderParameters[1].Parameter[0].Value = &nDep;
    
    	//save to file with current color
    	BSTR strSave = fileName.AllocSysString();
    
    	//pBitmap->Save(bstrFileName, &Clsid, encoderParameters);
    	uiWriteMsg("Speichern beginnt"); // Uiwritemsg ist aus der Anwendungs-API
    
    // Der Folgende Befehl wird anscheinend nicht korrekt ausgeführt. 
    // Die Save-Funktion wird aber korrekt aufgerufen, und auch ausgeführt!
    	pBitmap->Save(strSave, &Clsid, NULL);
    	uiWriteMsg("Speichern beendet"); 
    
    	//shutdown GDI+
    	GdiplusShutdown(gdiplusToken);
    
    	cout << error_name << strSave << endl;
    
    };
    
    // Fkt. sucht verfügbare Image-Encoder
    int Screenshot::GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
    {
    	UINT  num = 0;          // Encoder-Anzahl
    	UINT  size = 0;         // Größe des Encoder Arrays
    
    	ImageCodecInfo* pImageCodecInfo = NULL;
    
    	GetImageEncodersSize(&num, &size);
    	if(size == 0)
    		return -1;  
    
    	pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
    	if(pImageCodecInfo == NULL)
    		return -1;  
    
    	GetImageEncoders(num, size, pImageCodecInfo);
    
    	for(UINT j = 0; j < num; ++j)
    	{
    		if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
    		{
    			*pClsid = pImageCodecInfo[j].Clsid;
    			free(pImageCodecInfo);
    			return j;  // gefunden
    		}    
    	}
    
    	free(pImageCodecInfo);
    	return -1;  
    }
    


  • keiner eine idee? 😞



  • DrOetker schrieb:

    Nun habe ich also eine Schleife, in der ich alle 0.002 Sekunden (änderbarer Time-Intervall) das Bild speichere. Merkwürdigerweise aber werden nur 69 Bilder gespeichert. Der Schleifeninhalt ist (vereinfacht):

    record = true;
    for(int count = Anzahl;Anzahl!=0;Anzahl--){
    	if(record){
    		Screenshot* oScreenshot = new Screenshot();
    		oScreenshot->dump(targetFile.str().c_str());
    		delete oScreenshot;
    	}
    }
    

    Damit ich dich richtig verstehe: Wir in jedem Schleifendurchlauf ein Bild gespeichert? Oder wird diese for()-Schleife alle x Sekunden vom Timer neu gestartet?
    (Zusatzfrage: Was steht überhaupt in 'Anzahl'?)



  • Ja, das hast Du richtig verstanden. Die Schleife ist ja wie gesagt vereinfacht - letztendlich wird aber bei jedem Durchlauf ein Bild gespeichert.
    In Anzahl steht die Anzahl der Frames, die meine Anwendung durchläuft. Das berechne ich in einem anderen Teil, ist aber irrelevant, da Anzahl richtig ist (im konkreten Beispiel, das ich verwende, ist Anzahl immer 138...

    hola, genau die hälfte... aber das kann damit ja nichts zu tun haben...



  • Zwei Ideen:
    a) Wird der Wert von 'record' irgendwo in der Schleife verändert?
    b) Werden eigentlich immer die ersten 69 Bilder gespeichert? Oder jedes zweite Bild? (und wie sieht es aus, wenn du für Anzahl einen höheren Wert festlegst?)

    PS: und woher bekommst du den Dateinamen?



  • okay, also

    zu a) nein, der wert wird nicht verändert. ich habe ja in meiner klasse in der funktion Screenshot::save die ausgabe

    cout << error_name << strSave << endl;
    

    diese wird auch korrekt ausgegeben.

    zu b) ja, es werden die ersten 69 Bilder gespeichert. Das weiss ich, da die anwendung einen Roboter visuell bewegt, und dieser fährt einmal hin und einmal zurück. Daher kann ich das recht einfach kontrollieren.

    Dateiname: der ergibt sich in der Schleife aus

    std::ostringstream targetFile;
    	targetFile << "C:/Temp/test" << pic_nr << ".png";
    
    //(...) <- oben geposteter Code
            pic_nr++;
    


  • ich habe jetzt nochmal das ganze ausgelagert, und nicht mit der anwendung verbunden. ich habe also nur einen button, bei dem folgende schleife ausgeführt wird:

    void CVidExpWin32Dlg::OnBnClickedButton2()
    {
    
    	int pic_nr = 1000000, anzahl = 200;
    
    	BOOL record = true;
    
    	for(int i=0;i<anzahl;i++){
    		std::ostringstream targetFile;	
    		targetFile << "C:/Temp/test" << pic_nr << ".png";
    
    		if(record){
    			Screenshot* oScreenshot = new Screenshot();
    			oScreenshot->dump(targetFile.str().c_str());
    			delete oScreenshot;
    		}
    	pic_nr++;
    	}
    }
    

    es werden exakt nur 66 Bilder gespeichert!! 👎 👎 😞

    EDIT: ich habe jetzt mal nur jedes zehnte Bild abgespeichert. Auch dann tritt das Problem auf (Anzahl = 1000, nur jedes 10. Bild, ergibt 66 Bilder...).

    Aber hier ist etwas im Debugger-Fenster, das wohl damit zu tun hat!

    zuvor, wo speichern klappt, steht dort sehr viele Male:

    The thread 'Win32 Thread' (0x7f8) has exited with code 1 (0x1).
    

    und dann später:

    The thread 'Win32 Thread' (0x1c0) has exited with code 0 (0x0).
    

    Kann jemand damit was anfangen und hat einen Lösungsvorschlag? Da habe ich dann wohl doch irgendwie Ressourcenmäßig geschlampt bei meiner Klasse?



  • kann vielleicht noch irgend jemand schauen, wo ich Ressourcen nicht freigegeben / verschwendet habe? Vielleicht liegt da ja das Problem...

    Ich bettel ja ungern, aber ich bin nicht faul - versuche und versuche, aber es haut nicht hin 😞



  • Kannst du mal mit dem Debugger reingehen und nachschauen, was dort im Detail abläuft? Eventuell findest du dabei den Fehler.



  • Ja! Eben gerade habe ich herausgefunden, dass das Speichern nicht mehr funktioniert, da hBitmap in der Funktion Screenshot::Dump NULL ist! Das bringt mich dem Ganzen jetzt etwas näher - nur warum ist es NULL??

    void Screenshot::dump(CString fileName)
    { 
    	HDC hdcSrc = GetWindowDC(GetDesktopWindow()),
    	hdcDest = CreateCompatibleDC(hdcSrc);
    	HBITMAP hBitmap = CreateCompatibleBitmap(hdcSrc,
    	GetDeviceCaps(hdcSrc,8),GetDeviceCaps(hdcSrc,10));
    	SelectObject(hdcDest,hBitmap);
    	BitBlt(hdcDest,0,0,GetDeviceCaps(hdcSrc,8),
    					GetDeviceCaps(hdcSrc,10),
    					hdcSrc,0,0,0x00CC0020);
    
    	std::ostringstream oss;
    	oss << "X: " << GetDeviceCaps(hdcSrc, HORZRES) << "Y: " << GetDeviceCaps(hdcSrc, VERTRES) << "\r\n";
    	writeDebug(oss.str().c_str());
    	if(hBitmap == NULL)
    		writeDebug("hBitmap ist NULL!");
    	save(hBitmap,fileName);
    
    	DeleteDC(hdcSrc);
    	DeleteDC(hdcDest);
    	DeleteDC((HDC)hBitmap);
    }
    

    hdcSrc und hdcDest sind nicht NULL.



  • Siehe Antwort in
    news:microsoft.public.de.vc

    Das wichtigest ist eigentlich nur, dass Du die Doku richtig liest...
    Und da steht dass man bei einem "CreateCompatibleBitmap" ein "DeleteObject" aufrufen muss und bei einem "GetWindowDC" ein "ReleaseDC"...

    Also:
    DeleteObject(hBitmap);
    ReleaseDC(hDesktop, hdcSrc);



  • Ach, dass Du hier auch unterwegs bist 😉 Hier stand es zuerst, aber ich bekam keine Hilfe. Werde ich morgen gleich testen, danke soweit.

    Ich habe auch in die Hilfe geschaut, leider ist das Problem nur dass die nur Links für die MFC Komponenten bereithält, aber wie ich nun bemerkt habe die Funktionen von GDIPlus bei mir irgendwie nicht aufgelistet werden. Nunja, morgen bin ich schlauer! Danke soweit.


Anmelden zum Antworten