Laden...

Screenshot von Fenster das nicht aktiv ist

Erstellt von Regenwurm vor 15 Jahren Letzter Beitrag vor 15 Jahren 6.863 Views
R
Regenwurm Themenstarter:in
295 Beiträge seit 2008
vor 15 Jahren
Screenshot von Fenster das nicht aktiv ist

Guten Abend

Mein Ziel ist es, ein Screenshot von einem nicht aktiven Fenster zu erstellen.
Das Fenster sollte aber nicht aktiviert werden.
Ebenso soll nur ein bestimmter Bereich ausgewählt werden.

Im Internet bin ich auf folgenden Link gestossen:

http://dotnet-snippets.de/dns/klasse-zum-erstellen-von-screenshots-SID434.aspx

/// <summary>
    /// Erzeugt ein Screenshot vom übergebenen Bereich.
    /// </summary>
    /// <param name="topleft">Punkt des Bereich oben - links</param>
    /// <param name="bottomRight">Punkt des Bereich unten - rechts</param>
    /// <returns>Bitmap</returns>
    public Bitmap UserDefined(Point topleft, Point bottomRight)
    {
        return CreateScreenshot(topleft.X, topleft.Y, bottomRight.X, bottomRight.Y);
    }

Dies wäre ja schon einmal perfekt für den Bereich.

Nur, wo muss ich nun das Handle übergeben?
Und wie kann ich einstellen, wo die Screenshots gespeichert werden?

Regenwurm

ServiceStack & Angular = =)

U
195 Beiträge seit 2006
vor 15 Jahren

Sieht ja so aus, als ob die Funktion dir ein Bitmap zurückgibt (public **BITMAP **UserDefined).

Das du da ein Handle übergeben kannst, sieht nicht so aus. Könntestes dir mal die Funktion "CreateScreenshot" anschauen, ob die noch eine Überladung für das Handle hat, aber ansonsten schießt das Dingen nur einen Screenshot, vom Bildschirm, wie du ihn siehst.

//EDIT:

Steht doch ganz klar auf der Seite, die Funktion:


 public Bitmap UserDefinedWindowHandle(IntPtr windowhandle)
    {
        return CreateScreenshot(windowhandle);
    }

Da kannst du ein Handle übergeben.

 private Bitmap CreateScreenshot(int left, int top, int width, int height)
    {
        Bitmap bmp = new Bitmap(width, height);
        Graphics g = Graphics.FromImage(bmp);
        g.CopyFromScreen(left, top, 0, 0, new Size(width, height));
        g.Dispose();
        return bmp;
    }

    private Bitmap CreateScreenshot(IntPtr windowhandle)
    {
        RECT windowRectangle;
        GetWindowRect(windowhandle, out windowRectangle);
        return CreateScreenshot(windowRectangle.Left, windowRectangle.Top, windowRectangle.Right - windowRectangle.Left, windowRectangle.Bottom - windowRectangle.Top);
    }

Die Funktion CreateScreenshot kann allerdings keine Screenshot anhand eines Handles UND einer Location machen. Dafür musst du dir eine neue Überladung erstellen

R
Regenwurm Themenstarter:in
295 Beiträge seit 2008
vor 15 Jahren
    private Bitmap CreateScreenshot(IntPtr windowhandle)
    {
        RECT windowRectangle;
        GetWindowRect(windowhandle, out windowRectangle);
        return CreateScreenshot(windowRectangle.Left, windowRectangle.Top, windowRectangle.Right - windowRectangle.Left, windowRectangle.Bottom - windowRectangle.Top);
    }

Sowas?

Und, wie kann ich das Bitmap dann speichern?

ServiceStack & Angular = =)

946 Beiträge seit 2008
vor 15 Jahren

Und, wie kann ich das Bitmap dann speichern? Siehe hierzu [ erledigt ]Graphics und Bitmaps speichern.

Vielleicht hilft dir auch Formular/Control drucken oder als Bitmap speichern. Einfach this durch die Form von Handle ersetzen.

R
Regenwurm Themenstarter:in
295 Beiträge seit 2008
vor 15 Jahren

Hallo

Wie meinst du genau, "eine neue Überladung erstellen"? 😃

Noch zum Bitmap Speicher hinweis.

In dem Beispiel wird mit einer PictureBox gearbeitet, nur leider erstelle ich eine Konsolenanwendung.

Grüsse,

Regenwurm

ServiceStack & Angular = =)

946 Beiträge seit 2008
vor 15 Jahren

OK, ich war desorientiert. Die Lösung ist ganz einfach mit

bitmap.Save(@"C:\...");

Zur Überladung:
Gemeint ist, eine Funktion, die als Parameter ein Handle und die Position annimmt. Dadurch kannst du den "Bestimmten Bereich" mit der Fensterposition als Nullpunkt auswählen.

L
770 Beiträge seit 2006
vor 15 Jahren

Ich bin mir nicht sicher wie weit du das Code Snippet getestet hast, aber bei mir macht er einfach kein Screenshot eines nicht aktiven Fenster.

Ich meine auch mal gelesen zu haben, dies sei nicht so einfach bzw. unmöglich.
Funktioniert dass den bei dir? Bei mir macht er zwar ein Screenshot mit der richtigen Größe des Screenshots, aber das Bild selbst ist vom aktiven Bild.

Daher würde ich meinen, dass entspricht nicht deinen Anforderungen, oder es funktioniert bei dir 😄

Lion

lg Lion

J
3.331 Beiträge seit 2006
vor 15 Jahren

Ich meine auch mal gelesen zu haben, dies sei nicht so einfach bzw. unmöglich.

So ist es. In Formular/Control drucken oder als Bitmap speichern (oder zumindest in der englischen Version auf CodeProject) habe ich darauf hingewiesen: DrawToBitmap wird nur ausgeführt, wenn Windows tatsächlich etwas zu zeichnen hat. Ein verdecktes Fenster muss nicht gezeichnet werden; also ist auch kein Screenshot möglich. Wenn das nicht aktive Fenster sichtbar ist, kann es auch gezeichnet werden; dann muss ein Verweis auf das betreffende Formular übergeben werden (der z.B. durch Application.OpenForms erhalten werden kann).

Aber das bezieht sich wirklich auf WinForms, nicht auf Konsolenanwendungen. (Ich verstehe sowieso nicht, wieso man mit Console arbeiten und gleichzeitig auf GUI-Elemente zugreifen will.)

Jürgen

1.361 Beiträge seit 2007
vor 15 Jahren

Hallo,

Im Prinzip funktioniert es mit PrintWindow.

Man benötigt nur ein Handle auf das Fenster. Aber das kann man aus _EnumWindows _ermitteln, wenn es den richtigen _GetWindowText _hat.

Hier ein passendes Snippet:


[DllImport("user32.dll", EntryPoint = "PrintWindow")]
public static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, uint nFlags);

[StructLayout(LayoutKind.Sequential)]
public struct RECT {
		public int Left;
		public int Top;
		public int Right;
		public int Bottom;
	}

[DllImport("user32.dll", SetLastError = true)]
static extern bool GetWindowRect(IntPtr hWnd, out RECT rect); 

...
	
private Bitmap captureWindow(IntPtr hWnd)
{
	RECT rc;
	GetWindowRect(hWnd, out rc);
    int width 	= rc.Right - rc.Left;
    int height 	= rc.Bottom - rc.Top;

    if (width == 0 || height == 0)
        return null;
	
	Bitmap bmp = new Bitmap(width, height);
	
	Graphics g 	= Graphics.FromImage(bmp);
	IntPtr gHDC = g.GetHdc();

	PrintWindow(hWnd, gHDC, 0);

	g.ReleaseHdc(gHDC);
	g.Dispose();

	return bmp;
}

beste Grüße
zommi

R
Regenwurm Themenstarter:in
295 Beiträge seit 2008
vor 15 Jahren

Hallo

Ich habe das ganze nun mal ausprobiert.
Es funktioniert an und für sich recht gut, nur wenn ich ein Game client ein Screen schiessen will, ist das Bild am Ende schwarz.

Weiss einer warum?

ServiceStack & Angular = =)

1.361 Beiträge seit 2007
vor 15 Jahren

Mh...
Das Game wird wohl mit DirectX rendern. Da kommt das Bild dann direkt in den Videospeicher der Grafikkarte. Windows bekommt da über seine GDI-API gar nichts mit. Insofern bringt es auch nichts, per PrintWindow (was intern wahrscheinlich eine WM_PRINT-Message schickt) dem Fenster zu sagen, seinen GDI-inhalt zu zeichnen. Weil der nämlich keinen hat. (deshalb schwarz).
Um nun screenshots von DirectX programmen zu machen, müsstest du direkt den Videobuffer auslesen. Aber ob nun minimierte DirectX-Programme überhaupt was in irgendnen Speicher reinrendern... kA... vielleicht weiß das ja wer hier.

beste Grüße
zommi

1.820 Beiträge seit 2005
vor 15 Jahren

Hallo!

@zommi:
Ich denke mal, dass minimierte DirectX-Programme nichts schreiben bzw. die DirectX-Schnittstelle dies unterbindet, da ja sonst etwas auf dem Bildschirm zu sehen wäre (da sie ja wie du schon geschrieben hast direkt in den Videobereich schreiben).

DirectX prüft ja auch, welcher Bereich des Fensters dargestellt werden soll, wenn das Fenster teilweise von anderen Fenstern verdeckt wird.

Evtl. gibt es in der DirectX-Schnittstelle ein Äquivalent zu PrintWindow.

Nobody is perfect. I'm sad, i'm not nobody 🙁