Hypercell ein ] Hypercell aus ] Zeige Navigation ] Verstecke Navigation ]
c++.net  
   

Die mobilen Seiten von c++.net:
https://m.c-plusplus.net

  
C++ Forum :: WinAPI ::  Ingame Screenshot speichern möglich? Wie?     Zeige alle Beiträge auf einer Seite Auf Beitrag antworten
Autor Nachricht
eXc
Mitglied

Benutzerprofil
Anmeldungsdatum: 19.12.2016
Beiträge: 24
Beitrag eXc Mitglied 13:28:13 21.12.2016   Titel:   Ingame Screenshot speichern möglich? Wie?            Zitieren

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.

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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.

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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:
C++:
1
2
3
4
5
6
7
8
9
10
11
    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'
Hi
Unregistrierter




Beitrag Hi Unregistrierter 14:26:36 21.12.2016   Titel:              Zitieren

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).
C++ Forumbot
Forumbot

Benutzerprofil
Anmeldungsdatum: 29.02.2004
Beiträge: 20036
Beitrag C++ Forumbot Forumbot 14:28:33 21.12.2016   Titel:              Zitieren

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.

_________________
Besuchen Sie unsere Bücherecke.
http://www.c-plusplus.de/bucher.php
Mit jeder Bestellung unterstützen Sie das Forum.
SeppJ
Global Moderator

Benutzerprofil
Anmeldungsdatum: 10.06.2008
Beiträge: 28191
Beitrag SeppJ Global Moderator 14:30:52 21.12.2016   Titel:              Zitieren

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?

_________________
Korrekte Rechtschreibung und Grammatik sind das sprachliche Äquivalent zu einer Dusche und gepflegter Kleidung.
Hi
Unregistrierter




Beitrag Hi Unregistrierter 15:08:37 21.12.2016   Titel:              Zitieren

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..
SeppJ
Global Moderator

Benutzerprofil
Anmeldungsdatum: 10.06.2008
Beiträge: 28191
Beitrag SeppJ Global Moderator 15:20:45 21.12.2016   Titel:              Zitieren

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.

_________________
Korrekte Rechtschreibung und Grammatik sind das sprachliche Äquivalent zu einer Dusche und gepflegter Kleidung.
eXc
Mitglied

Benutzerprofil
Anmeldungsdatum: 19.12.2016
Beiträge: 24
Beitrag eXc Mitglied 15:22:35 21.12.2016   Titel:              Zitieren

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

C++:
    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.


Zuletzt bearbeitet von eXc am 15:24:04 21.12.2016, insgesamt 1-mal bearbeitet
eXc
Mitglied

Benutzerprofil
Anmeldungsdatum: 19.12.2016
Beiträge: 24
Beitrag eXc Mitglied 15:48:47 21.12.2016   Titel:              Zitieren

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.
Hi
Unregistrierter




Beitrag Hi Unregistrierter 16:30:47 21.12.2016   Titel:              Zitieren

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

C++:
    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.


C++:
 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
eXc
Mitglied

Benutzerprofil
Anmeldungsdatum: 19.12.2016
Beiträge: 24
Beitrag eXc Mitglied 16:40:53 21.12.2016   Titel:              Zitieren

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.
Th69
Mitglied

Benutzerprofil
Anmeldungsdatum: 25.03.2008
Beiträge: 4690
Beitrag Th69 Mitglied 19:27:35 21.12.2016   Titel:              Zitieren

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




Beitrag Hi Unregistrierter 20:01:57 21.12.2016   Titel:              Zitieren

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? :o)
eXc
Mitglied

Benutzerprofil
Anmeldungsdatum: 19.12.2016
Beiträge: 24
Beitrag eXc Mitglied 20:42:42 21.12.2016   Titel:              Zitieren

Das mit Handle=NULL funktioniert leider auch nicht.

Folgende Funktion funktioniert auf jeden Fall schonmal und macht Bilder sofern Daten vorhanden sind:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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.

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
eXc
Mitglied

Benutzerprofil
Anmeldungsdatum: 19.12.2016
Beiträge: 24
Beitrag eXc Mitglied 20:52:27 21.12.2016   Titel:              Zitieren

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

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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);
}
Hi
Unregistrierter




Beitrag Hi Unregistrierter 22:17:52 21.12.2016   Titel:              Zitieren

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.
eXc
Mitglied

Benutzerprofil
Anmeldungsdatum: 19.12.2016
Beiträge: 24
Beitrag eXc Mitglied 19:20:34 22.12.2016   Titel:              Zitieren

Hallo,

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

Zitat:
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.

Zitat:
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'
Mr C
Unregistrierter




Beitrag Mr C Unregistrierter 20:04:59 22.12.2016   Titel:              Zitieren

Zitat:
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/ma ....... culate-frames-per-second/
C++ Forum :: WinAPI ::  Ingame Screenshot speichern möglich? Wie?   Auf Beitrag antworten

Zeige alle Beiträge auf einer Seite




Nächstes Thema anzeigen
Vorheriges Thema anzeigen
Sie können Beiträge in dieses Forum schreiben.
Sie können auf Beiträge in diesem Forum antworten.
Sie können Ihre Beiträge in diesem Forum nicht bearbeiten.
Sie können Ihre Beiträge in diesem Forum nicht löschen.
Sie können an Umfragen in diesem Forum nicht mitmachen.

Powered by phpBB © 2001, 2002 phpBB Group :: FI Theme

c++.net ist Teilnehmer des Partnerprogramms von Amazon Europe S.à.r.l. und Partner des Werbeprogramms, das zur Bereitstellung eines Mediums für Websites konzipiert wurde, mittels dessen durch die Platzierung von Werbeanzeigen und Links zu amazon.de Werbekostenerstattung verdient werden kann.

Die Vervielfältigung der auf den Seiten www.c-plusplus.de, www.c-plusplus.info und www.c-plusplus.net enthaltenen Informationen ohne eine schriftliche Genehmigung des Seitenbetreibers ist untersagt (vgl. §4 Urheberrechtsgesetz). Die Nutzung und Änderung der vorgestellten Strukturen und Verfahren in privaten und kommerziellen Softwareanwendungen ist ausdrücklich erlaubt, soweit keine Rechte Dritter verletzt werden. Der Seitenbetreiber übernimmt keine Gewähr für die Funktion einzelner Beiträge oder Programmfragmente, insbesondere übernimmt er keine Haftung für eventuelle aus dem Gebrauch entstehenden Folgeschäden.