Ingame Screenshot speichern möglich? Wie?



  • Hallo,

    dies ist mein ersten Eintrag und ich bin noch ziemlicher Neuling, deswegen bitte ich um Rücksicht. In der Suche und bei Google habe ich leider noch nicht die richtige Lösung gefunden.

    Mein Problem:
    Ich möchte zyklisch von einem Programm oder Game einen Screenshot machen. Erstens ist das Spiel aber kein offenes Fenster, lässt sich per FindWindow also nicht ausmachen und zweitens bekomme ich die Zwischenablage nicht abgespeichert.

    Mit folgendem Code generiere ich mir einen Screenshot in der Zwischenablage. Das funktioniert soweit auch, da ich mit Paint daraus ein Bild erzeugen kann.

    void makescreenshot()
    {
    	INPUT ip;
    
    	// Set up a generic keyboard event.
    	ip.type = INPUT_KEYBOARD;
    	ip.ki.wScan = 0; // hardware scan code for key
    	ip.ki.time = 0;
    	ip.ki.dwExtraInfo = 0;
    
    	ip.ki.wVk = 0x2C; // virtual-key code for the "SNAPSHOT" key - https://msdn.microsoft.com/en-us/library/dd375731(v=vs.85).aspx
    	ip.ki.dwFlags = 0; // 0 for key press
    	SendInput(1, &ip, sizeof(INPUT));
    
    	ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
    	SendInput(1, &ip, sizeof(INPUT));
    }
    

    Nehme ich nun folgenden Code, erzeugt mit C++ zwar ein Bild, aber nicht von dem Inhalt der Zwischenablage sondern von dem Desktop dahinter.

    void TakeScreenShot()
    {
    	int horizontal = 0;
    	int vertical = 0;
    
    	// Kopiere Screen in Bitmap
    	RECT screenshot;
    	const HWND hDesktop= GetDesktopWindow();
    	if (hDesktop == NULL)
    	{
    	cout << "Kein Inhalt in der Zwischenablage!" << endl;
    	}
    	GetWindowRect(hDesktop, &screenshot);
    
    	horizontal = screenshot.right;
    	vertical = screenshot.bottom;
    
    	POINT a, b;
    	a.x = 0;
    	a.y = 0;
    
    	b.x = horizontal;
    	b.y = vertical;
    
    	HWND hClipboard = (HWND)GetClipboardData(CF_BITMAP);
    
    	HDC     hScreen = GetDC(hClipboard); // Handle to clipboard
    	HDC     hDC = CreateCompatibleDC(hScreen);
    	HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, abs(b.x - a.x), abs(b.y - a.y));
    	HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
    	BOOL    bRet = BitBlt(hDC, 0, 0, abs(b.x - a.x), abs(b.y - a.y), hScreen, a.x, a.y, SRCCOPY);
    
    	// Bitmap speichern
    	TCHAR szName[MAX_PATH] = { 0 };
    	SYSTEMTIME st = { 0 };
    	::GetLocalTime(&st);
    	_stprintf_s(szName, _T("X_%02d-%02d-%02d-%03d.jpg"), st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
    	CImage image;
    	image.Attach(hBitmap);
    	image.Save(szName, Gdiplus::ImageFormatJPEG);
    
    	// Aufräumen
    	SelectObject(hDC, old_obj);
    	DeleteDC(hDC);
    	ReleaseDC(NULL, hScreen);
    	DeleteObject(hBitmap);
    
    }
    

    Ich habe schon diverse Sachen die ich gefunden habe ausprobiert, bekomme es aber nicht hin das mir die Zwischenablage abgespeichert wird.

    Da ich mein Wissen bzgl. C++ ausbauen möchte, würde ich mich freuen wenn mir diesbezüglich jemand helfen könnte. Ich weiß nicht mehr wo und nach was ich noch suchen muss/soll.

    Ich habe es auch schon mit diesem Snippet versucht:

    const HWND hWnd = GetDesktopWindow();
    
    	HBITMAP hBitmap = ImageFromClipboard(hWnd);
    
    	CImage image;
    
    	image.Attach(hBitmap1);
    
    	image.Save(_T("test.bmp"), Gdiplus::ImageFormatBMP);
    
    	DeleteObject(hBitmap1);
    

    Aber auch das führte nicht zum Erfolg.

    Mit freundlichem Gruss
    Christian 'eXc'



  • Kannst du uns sagen um welches Programm/Spiel es sich handelt? Kein Fenster? Irgendwie komisch.

    Abgesehen davon vermute ich Folgendes:

    Dein "makescreenshot" funktioniert und du hast den Code wohl ausgeführt, als gerade der Desktop sichtbar war. Oder das Programm/Spiel benutzt eine seltsame Render-Methode, die von der "Print Screen"-Taste nicht erfasst wird. Könnte auch an DWM liegen.

    Und dein "TakeScreenShot" speichert dir doch schon das Bild im Clipboard ab! Bloß hat doch "GetDesktopWindow" nichts mit dem Clipboard zu tun (und kann auch glaube ich nie 0 zurückgeben).



  • Dieser Thread wurde von Moderator/in SeppJ aus dem Forum C++ (alle ISO-Standards) in das Forum WinAPI verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.


  • Mod

    Hi schrieb:

    Oder das Programm/Spiel benutzt eine seltsame Render-Methode, die von der "Print Screen"-Taste nicht erfasst wird.

    Ist das nicht der Normalfall bei Spielen?



  • SeppJ schrieb:

    Hi schrieb:

    Oder das Programm/Spiel benutzt eine seltsame Render-Methode, die von der "Print Screen"-Taste nicht erfasst wird.

    Ist das nicht der Normalfall bei Spielen?

    Seltsame Rendermethoden ohne Fenster? Oder dass Screenshots mit der Druck-Taste nicht funktionieren?
    Jedenfalls kenne ich kein Spiel ohne Fenster und ich konnte in Spielen auch schon immer ganz normal Screenshots machen. Sowohl mit Win XP (ohne DWM) als auch mit Win 7 (mit DWM).

    Was aber mit DWM glaube ich nicht geht ist GetDesktopWindow + GetDC + BitBlt. Da erhält man entweder ein schwarzes Bild oder den Desktopinhalt.
    Wobei ich mich gerade frage, ob dann nicht FindWindow (anstatt GetDesktopWindow) eine Alternative wäre (sofern es ein Fenster gibt..).

    Hm ich seh grade im Code oben.. GetClipboardData mit CF_BITMAP müsste doch ein HBITMAP zurückgeben, nicht HWND..


  • Mod

    Ich meinte, dass die Druck-Taste nicht (korrekt) funktioniert. Viele Spiele rendern hardwarebeschleunigt direkt auf den Bildschirm. Wenn man die normale Print Screen Funktion von Windows benutzt, erhält man an der Stelle des zu sehenden Bildes dann nur ein schwarzes Bild. Es gibt Third-Party Werkzeuge dafür (Fraps und ähnliche), aber wenn man es selber machen will, muss man weiter gehen als nur die normale Print Screen Funktion zu kapern.



  • Danke erstmal schon für die vielen Antworten und Entschuldigung für die Fehlpositionierung im falschen Forum.

    @Hi: Aber zu der Frage:
    Es handelt sich um CS:GO, kann aber auch jedes andere Spiel aus der Steam-Familie sein.

    Komischerweise Print-Screen bzw. die Taste Druck funktionieren ja. Starte ich nur die Funktion "makescreenshot" kann ich aus dem Spiel tabben und per Paint den Screen abspeichern, der müsste dann doch irgendwo liegen. Ich schaffe es nur nicht, den Screen aus der Zwischenablage direkt abzuspeichern, da macht er nur den "normalen" Desktopinhalt der quasi im Hintergrund liegt.

    GetClipboardData sollte eigentlich ein HBITMAP zurückgeben, das stimmt wohl. Schreibe ich das aber so das ich das hier einbaue

    CImage image;
        image.Attach(hBitmap);
        image.Save(szName, Gdiplus::ImageFormatJPEG);
    

    Dann gibt es Fehlermeldungen zu atlimage.h das hBitmap != NULL wäre. Das verstehe ich nicht und habe deswegen eine andere Methode probiert.



  • Ich habe gerade nochmal etwas anderes probiert, das Spiel "windowed" laufen lassen und die Funktionen gestartet. Der Screenshot der dann erzeugt wird enthält auch den Inhalt des Spiels.

    http://imgur.com/lF62wcz

    Wie zuvor schon gesagt, die Zwischenablage enthält ja die Informationen mit dem "Full"-Bild des Spiels, bekomme diese nur nicht abgespeichert.



  • eXc schrieb:

    GetClipboardData sollte eigentlich ein HBITMAP zurückgeben, das stimmt wohl. Schreibe ich das aber so das ich das hier einbaue

    CImage image;
        image.Attach(hBitmap);
        image.Save(szName, Gdiplus::ImageFormatJPEG);
    

    Dann gibt es Fehlermeldungen zu atlimage.h das hBitmap != NULL wäre. Das verstehe ich nicht und habe deswegen eine andere Methode probiert.

    HWND hClipboard = (HWND)GetClipboardData(CF_BITMAP);
    
        HDC     hScreen = GetDC(hClipboard); // Handle to clipboard
    

    Du benutzt das API falsch. Es ist zB. noch OpenClipboard() nötig.
    Mindestens deshalb gibt dir GetClipboardData() 0 zurück.
    Die 0 übergibst du an GetDC() und deshalb bekommst du das Desktopfenster ("entire screen") zurück. Steht so in der Doku.

    Ich empfehle wie immer die Grundlagen :p



  • Aber was müsste ich denn dem OpenClipboard() für eine Handle übergeben das das richtige geöffnet wird? Habe es mit GetDesktopWindow()versucht, aber das funktioniert nicht.

    Werde mal weiter lesen. Vielleicht finde ich da in der API ja was.



  • Gib NULL als Parameter an, wie es auch in OpenClipboard steht.



  • MSDN OpenClipboard schrieb:

    If this parameter is NULL, the open clipboard is associated with the current task.

    Was ist eigentlich ein "task"? Ein Win 3.1 Prozess? 🤡



  • Das mit Handle=NULL funktioniert leider auch nicht.

    Folgende Funktion funktioniert auf jeden Fall schonmal und macht Bilder sofern Daten vorhanden sind:

    void TakeScreenShot()
    {
    
    	HBITMAP hBitmap;
    	OpenClipboard(NULL);
    	hBitmap = (HBITMAP)GetClipboardData(CF_BITMAP);
    	CloseClipboard();
    
    	if (hBitmap == NULL)
    	{
    		cout << "Kein Bild-Inhalt in der Zwischenablage!" << endl;
    		return;
    	}
    
    	// Bitmap speichern
    	TCHAR szName[MAX_PATH] = { 0 };
    	SYSTEMTIME st = { 0 };
    	::GetLocalTime(&st);
    	_stprintf_s(szName, _T("X_%02d-%02d-%02d-%03d.jpg"), st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
    	CImage image;
    	image.Attach(hBitmap);
    	image.Save(szName, Gdiplus::ImageFormatJPEG);
    
    	// Aufräumen
    	DeleteObject(hBitmap);
    

    Drücke ich nun irgendwann die "Print-Screen" oder "Druck(en)" Taste, dann wird das Bild abgespeichert, auch ein Bild Ingame.

    Nutze ich nun folgende Funktion zum Erstellen eines Screenshots per Fake-Keypress, funktioniert das so nicht mehr.

    void makescreenshot()
    {
    	INPUT ip;
    
    	// Set up a generic keyboard event.
    	ip.type = INPUT_KEYBOARD;
    	ip.ki.wScan = 0; // hardware scan code for key
    	ip.ki.time = 0;
    	ip.ki.dwExtraInfo = 0;
    
    	ip.ki.wVk = 0x2C; // virtual-key code for the "SNAPSHOT" key - https://msdn.microsoft.com/en-us/library/dd375731(v=vs.85).aspx
    	ip.ki.dwFlags = 0; // 0 for key press
    	SendInput(1, &ip, sizeof(INPUT));
    
    	ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
    	SendInput(1, &ip, sizeof(INPUT));
    
    	if (IsClipboardFormatAvailable(CF_BITMAP))
    	{
    		cout << "CF_BITMAP vorhanden!" << endl;
    	}
    	if (IsClipboardFormatAvailable(CF_TEXT))
    	{
    		cout << "CF_TEXT vorhanden!" << endl;
    	}
    }
    

    Es wird wohl ein Screenshot gemacht, da ich den Inhalt per Paste in Paint importieren kann, komischerweise wird mir aber weder CF_TEXT, noch CF_BITMAP angezeigt und die Funktion TakeScreenShot() wird deshalb nicht ausgeführt. Gibt es vielleicht verschiedene Clipboards parallel?

    Programm gestartet, Timer läuft, vor dem Aufruf der beiden Funktionen:

    http://imgur.com/pTrDmXP

    Programm und Timer laufen weiter, beide Funktionen wurden aufgerufen, Bild lässt sich in Paint importieren:

    http://imgur.com/voRqVSM



  • Nehme ich übrigens folgende Funktion macht er auch Bilder, leider sind die Bilder aus einem Spiel heraus aber schwarz.

    void screenshot()
    {
    	int horizontal = 0;
    	int vertical = 0;
    
    	RECT desktop;
    	HWND hDesktop = GetDesktopWindow();
    	// Größe des Rechtecks berechnen
    	GetWindowRect(hDesktop, &desktop);
    	horizontal = desktop.right;
    	vertical = desktop.bottom;
    
    	POINT a, b;
    	a.x = 0;
    	a.y = 0;
    
    	b.x = horizontal;
    	b.y = vertical;
    
    	// Kopiere Screen in Bitmap
    	HDC     hScreen = GetDC(hDesktop);
    	HDC     hDC = CreateCompatibleDC(hScreen);
    	HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, abs(b.x - a.x), abs(b.y - a.y));
    	HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
    	BOOL    bRet = BitBlt(hDC, 0, 0, abs(b.x - a.x), abs(b.y - a.y), hScreen, a.x, a.y, SRCCOPY);
    
    	// Bitmap in Zwischenspeicher
    	OpenClipboard(NULL);
    	EmptyClipboard();
    	SetClipboardData(CF_BITMAP, hBitmap);
    	CloseClipboard();
    
    	// Bitmap speichern
    	TCHAR szName[MAX_PATH] = { 0 };
    	SYSTEMTIME st = { 0 };
    	::GetLocalTime(&st);
    	_stprintf_s(szName, _T("%02d-%02d-%02d-%03d.jpg"), st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
    	CImage image;
    	image.Attach(hBitmap);
    	image.Save(szName, Gdiplus::ImageFormatJPEG);
    
    	// Aufräumen
    	SelectObject(hDC, old_obj);
    	DeleteDC(hDC);
    	ReleaseDC(NULL, hScreen);
    	DeleteObject(hBitmap);
    }
    


  • Man man man.. Liest du die Doku überhaupt? Kannst du Englisch?

    MSDN GetClipboardData schrieb:

    The application must not free the handle nor leave it locked. The application must not use the handle after the EmptyClipboard or CloseClipboard function is called

    Beides machst du falsch.

    Vor IsClipboardFormatAvailable() brauchst du wahrscheinlich auch ein OpenClipboard().

    Warum der Screen bei deinem letzten Code schwarz ist, hab ich schon erklärt.
    Versuch's mit dem Fensterhandle des Spiels anstatt dem Desktophandle.
    In deinem Screenshot vorher sah man doch das Fenster des Spiels. Warum denkst du, dass da keines ist? Such mal mit Spy++ (ist bei Visual Studio dabei).

    Ansonsten ist die sauberste Methode per Direct3D-Hook. Aber da könnte ein ins Spiel integrierter AntiCheat was dagegen haben. Tja.



  • Hallo,

    Entschuldigung das ich mich vielleicht dumm anstelle, aber bin kein Pro was programmieren angeht.

    Du benutzt das API falsch. Es ist zB. noch OpenClipboard() nötig.
    Mindestens deshalb gibt dir GetClipboardData() 0 zurück.

    Bin davon ausgegangen das du das damit meintest, habe das dann wohl falsch verstanden.

    Vor IsClipboardFormatAvailable() brauchst du wahrscheinlich auch ein OpenClipboard().

    Nein brauche ich nicht. Ich habe es hinbekommen und auch so wird mir jetzt die korrekte Auswahl angezeigt. Musste wirklich nur explizit das eine Fenster angeben, ansonsten aber alles ok.

    Spy++ kenne ich natürlich auch nicht, werde es aber nachlesen.

    DANKE aber für Eure Hilfe!

    mfg Christian 'eXc'



  • Ich möchte zyklisch von einem Programm oder Game einen Screenshot machen.

    ..schau ob damit schlauer wirst:
    https://github.com/nektra/AVRecorderTool/
    http://blog.nektra.com/main/2013/07/23/instrumenting-direct3d-applications-to-capture-video-and-calculate-frames-per-second/


Anmelden zum Antworten