Zrobić zrzut ekranu aktywnego okna?

175

Tworzę aplikację do przechwytywania ekranu i wszystko idzie dobrze. Wszystko, co muszę zrobić, to przechwycić aktywne okno i zrobić zrzut ekranu tego aktywnego okna. Czy ktoś wie, jak mogę to zrobić?

użytkownik
źródło
17
Czy „aktywne okno” oznacza aktywne okno TWOJEJ aplikacji, czy też okno, które byłoby aktywne, gdyby Twoja aplikacja była ukryta?
Corey Trager

Odpowiedzi:

151
ScreenCapture sc = new ScreenCapture();
// capture entire screen, and save it to a file
Image img = sc.CaptureScreen();
// display image in a Picture control named imageDisplay
this.imageDisplay.Image = img;
// capture this window, and save it
sc.CaptureWindowToFile(this.Handle,"C:\\temp2.gif",ImageFormat.Gif);

http://www.developerfusion.com/code/4630/capture-a-screen-shot/

joe
źródło
7
Wiem już, jak zrobić standardowy zrzut ekranu. Muszę tylko wiedzieć, jak przechwycić aktywne okno.
użytkownik
3
Świetne rozwiązanie! Dziękuję za link.
Matthew M.
6
@joe Otrzymuję błąd GDI + podczas próby uruchomienia powyższego kodu. błąd występuje w klasie ScreenCpature podczas zapisywania obrazów.
hurnhu
2
Doskonały! Chciałem przechwycić zawartość panelu w mojej aplikacji. Zrobiłem więc sc.CaptureWindowToFile (panel1.Handle, "c: \ temp.jpg", imageformat.jpg) i voila!
D. Kermott,
2
W rozdzielczości HD z powiększonymi elementami interfejsu Windows ta klasa nie przechwytuje całego ekranu.
Yeronimo
223
Rectangle bounds = Screen.GetBounds(Point.Empty);
using(Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
{
    using(Graphics g = Graphics.FromImage(bitmap))
    {
         g.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size);
    }
    bitmap.Save("test.jpg", ImageFormat.Jpeg);
}

do przechwytywania bieżącego wykorzystania okna

 Rectangle bounds = this.Bounds;
 using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
 {
    using (Graphics g = Graphics.FromImage(bitmap))
    {
        g.CopyFromScreen(new Point(bounds.Left,bounds.Top), Point.Empty, bounds.Size);
    }
    bitmap.Save("C://test.jpg", ImageFormat.Jpeg);
 }
Arsen Mkrtchyan
źródło
Schludny! Pomyślałem, że będziesz musiał użyć WinAPI. Czy wiesz, czy jest to zaimplementowane w Mono?
Lucas Jones
To tylko daje mi normalny zrzut ekranu. Czy nie muszę używać WinAPI?
użytkownik
Cześć, czy mogę wiedzieć, jak zrobić zrzut ekranu w formularzu internetowym VB aspx?
beny lim
Cześć @benylim, spójrz na to ( stackoverflow.com/questions/701798/… ) i to ( social.msdn.microsoft.com/Forums/en/netfxjscript/thread/… ), mam nadzieję, że to pomoże
Arsen Mkrtchyan
Czy Isitz może zrobić tylko ze skryptem java?
beny lim
29

Proponuję następne rozwiązanie do przechwytywania dowolnego bieżącego aktywnego okna (nie tylko naszej aplikacji C #) lub całego ekranu z określeniem pozycji kursora odpowiednio względem lewego górnego rogu okna lub ekranu:

public enum enmScreenCaptureMode
{
    Screen,
    Window
}

class ScreenCapturer
{
    [DllImport("user32.dll")]
    private static extern IntPtr GetForegroundWindow();

    [DllImport("user32.dll")]
    private static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);

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

    public Bitmap Capture(enmScreenCaptureMode screenCaptureMode = enmScreenCaptureMode.Window)
    {
        Rectangle bounds;

        if (screenCaptureMode == enmScreenCaptureMode.Screen)
        {
            bounds = Screen.GetBounds(Point.Empty);
            CursorPosition = Cursor.Position;
        }
        else
        {
            var foregroundWindowsHandle = GetForegroundWindow();
            var rect = new Rect();
            GetWindowRect(foregroundWindowsHandle, ref rect);
            bounds = new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
            CursorPosition = new Point(Cursor.Position.X - rect.Left, Cursor.Position.Y - rect.Top);
        }

        var result = new Bitmap(bounds.Width, bounds.Height);

        using (var g = Graphics.FromImage(result))
        {
            g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
        }

        return result;
    }

    public Point CursorPosition
    {
        get;
        protected set;
    }
}
Ivan Kochurkin
źródło
Dlaczego musimy zadeklarować własną strukturę Rectangle, zamiast używać System.Drawing.Rectangle? Czy tylko po to, abyśmy mogli dodać atrybut? Czy wiesz, czy powinniśmy to zrobić również dla struktury Point?
Caster Troy
@Alex, próbowałem zamienić mój na RectSystem Rectangle, ale po tym funkcja GetWindowRectzwróciła nieprawidłowy prostokąt. Zamiast Righti Top, ustawia Widthi Heightwyjściowy prostokąt.
Ivan Kochurkin
1
Jedno zastrzeżenie: okno pierwszego planu niekoniecznie jest oknem aplikacji, z której jest wywoływane. W przypadku aplikacji WPF z jednym oknem, której var thisWindowHandle = new WindowInteropHelper(Application.Current.MainWindow).Handle;
używałbyś
23

Oto fragment umożliwiający przechwycenie pulpitu lub aktywnego okna. Nie ma odniesienia do Windows Forms.

public class ScreenCapture
{
    [DllImport("user32.dll")]
    private static extern IntPtr GetForegroundWindow();

    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    public static extern IntPtr GetDesktopWindow();

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

    [DllImport("user32.dll")]
    private static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);

    public static Image CaptureDesktop()
    {
        return CaptureWindow(GetDesktopWindow());
    }

    public static Bitmap CaptureActiveWindow()
    {
        return CaptureWindow(GetForegroundWindow());
    }

    public static Bitmap CaptureWindow(IntPtr handle)
    {
        var rect = new Rect();
        GetWindowRect(handle, ref rect);
        var bounds = new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
        var result = new Bitmap(bounds.Width, bounds.Height);

        using (var graphics = Graphics.FromImage(result))
        {
            graphics.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
        }

        return result;
    }
}

Jak zrobić zrzut całego ekranu:

var image = ScreenCapture.CaptureDesktop();
image.Save(@"C:\temp\snippetsource.jpg", ImageFormat.Jpeg);

Jak przechwycić aktywne okno:

var image = ScreenCapture.CaptureActiveWindow();
image.Save(@"C:\temp\snippetsource.jpg", ImageFormat.Jpeg);

Pierwotnie znaleziony tutaj: http://www.snippetsource.net/Snippet/158/capture-screenshot-in-c

Christian Moser
źródło
Wielkie dzięki, Christian za ten wspaniały fragment. Bardzo mi to pomogło!
casaout
To działa świetnie, ale nie działa, jeśli użytkownik ma ustawione skalowanie na tym ekranie (tj. 125% odcina lewą i dolną stronę)
russelrillema
12

Kod KvanTTT działał świetnie. Rozszerzyłem go nieco, aby umożliwić trochę większą elastyczność w zapisywaniu formatu, a także możliwość zapisywania przez hWnd, .NET Control / Form. Możesz pobrać mapę bitową lub zapisać do pliku za pomocą kilku opcji.

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace MosaiqPerformanceMonitor {
     public enum CaptureMode {
          Screen, Window
     }

     public static class ScreenCapturer {
          [DllImport("user32.dll")]
          private static extern IntPtr GetForegroundWindow();

          [DllImport("user32.dll")]
          private static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);

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

          [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
          public static extern IntPtr GetDesktopWindow();

          /// <summary> Capture Active Window, Desktop, Window or Control by hWnd or .NET Contro/Form and save it to a specified file.  </summary>
          /// <param name="filename">Filename.
          /// <para>* If extension is omitted, it's calculated from the type of file</para>
          /// <para>* If path is omitted, defaults to %TEMP%</para>
          /// <para>* Use %NOW% to put a timestamp in the filename</para></param>
          /// <param name="mode">Optional. The default value is CaptureMode.Window.</param>
          /// <param name="format">Optional file save mode.  Default is PNG</param>
          public static void CaptureAndSave(string filename, CaptureMode mode = CaptureMode.Window, ImageFormat format = null) {
                ImageSave(filename, format, Capture(mode));
          }

          /// <summary> Capture a specific window (or control) and save it to a specified file.  </summary>
          /// <param name="filename">Filename.
          /// <para>* If extension is omitted, it's calculated from the type of file</para>
          /// <para>* If path is omitted, defaults to %TEMP%</para>
          /// <para>* Use %NOW% to put a timestamp in the filename</para></param>
          /// <param name="handle">hWnd (handle) of the window to capture</param>
          /// <param name="format">Optional file save mode.  Default is PNG</param>
          public static void CaptureAndSave(string filename, IntPtr handle, ImageFormat format = null) {
                ImageSave(filename, format, Capture(handle));
          }

          /// <summary> Capture a specific window (or control) and save it to a specified file.  </summary>
          /// <param name="filename">Filename.
          /// <para>* If extension is omitted, it's calculated from the type of file</para>
          /// <para>* If path is omitted, defaults to %TEMP%</para>
          /// <para>* Use %NOW% to put a timestamp in the filename</para></param>
          /// <param name="c">Object to capture</param>
          /// <param name="format">Optional file save mode.  Default is PNG</param>
          public static void CaptureAndSave(string filename, Control c, ImageFormat format = null) {
                ImageSave(filename, format, Capture(c));
          }
          /// <summary> Capture the active window (default) or the desktop and return it as a bitmap </summary>
          /// <param name="mode">Optional. The default value is CaptureMode.Window.</param>
          public static Bitmap Capture(CaptureMode mode = CaptureMode.Window) {
                return Capture(mode == CaptureMode.Screen ? GetDesktopWindow() : GetForegroundWindow());
          }

          /// <summary> Capture a .NET Control, Form, UserControl, etc. </summary>
          /// <param name="c">Object to capture</param>
          /// <returns> Bitmap of control's area </returns>
          public static Bitmap Capture(Control c) {
                return Capture(c.Handle);
          }


          /// <summary> Capture a specific window and return it as a bitmap </summary>
          /// <param name="handle">hWnd (handle) of the window to capture</param>
          public static Bitmap Capture(IntPtr handle) {
                Rectangle bounds;
                var rect = new Rect();
                GetWindowRect(handle, ref rect);
                bounds = new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
                CursorPosition = new Point(Cursor.Position.X - rect.Left, Cursor.Position.Y - rect.Top);

                var result = new Bitmap(bounds.Width, bounds.Height);
                using (var g = Graphics.FromImage(result))
                     g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);

                return result;
          }

          /// <summary> Position of the cursor relative to the start of the capture </summary>
          public static Point CursorPosition;


          /// <summary> Save an image to a specific file </summary>
          /// <param name="filename">Filename.
          /// <para>* If extension is omitted, it's calculated from the type of file</para>
          /// <para>* If path is omitted, defaults to %TEMP%</para>
          /// <para>* Use %NOW% to put a timestamp in the filename</para></param>
          /// <param name="format">Optional file save mode.  Default is PNG</param>
          /// <param name="image">Image to save.  Usually a BitMap, but can be any
          /// Image.</param>
          static void ImageSave(string filename, ImageFormat format, Image image) {
                format = format ?? ImageFormat.Png;
                if (!filename.Contains("."))
                     filename = filename.Trim() + "." + format.ToString().ToLower();

                if (!filename.Contains(@"\"))
                     filename = Path.Combine(Environment.GetEnvironmentVariable("TEMP") ?? @"C:\Temp", filename);

                filename = filename.Replace("%NOW%", DateTime.Now.ToString("[email protected]"));
                image.Save(filename, format);
          }
     }
}
Wade Hatler
źródło
Świetne umiejętności refrakcyjne!
Ahmed Alejo
Najlepszy fragment kodu, działał bez problemów i właśnie tego szukałem. Dziękuję za udostępnienie.
Hugo Yates
1

Niewielka zmiana w metodzie static void ImageSave () da ci opcję zapisania go. Kredyt trafia do firmy Microsoft (http://msdn.microsoft.com/en-us/library/sfezx97z.aspx)

static void ImageSave(string filename, ImageFormat format, Image image, SaveFileDialog saveFileDialog1)
    { 
        saveFileDialog1.Filter = "JPeg Image|*.jpg|Bitmap Image|*.bmp|Gif Image|*.gif";
        saveFileDialog1.Title = "Enregistrer un image";
        saveFileDialog1.ShowDialog();

        // If the file name is not an empty string open it for saving.
        if (saveFileDialog1.FileName != "")
        {
            // Saves the Image via a FileStream created by the OpenFile method.
            System.IO.FileStream fs =
               (System.IO.FileStream)saveFileDialog1.OpenFile();
            // Saves the Image in the appropriate ImageFormat based upon the
            // File type selected in the dialog box.
            // NOTE that the FilterIndex property is one-based.
            switch (saveFileDialog1.FilterIndex)
            {
                case 1:
                    image.Save(fs,
                       System.Drawing.Imaging.ImageFormat.Jpeg);
                    break;

                case 2:
                    image.Save(fs,
                       System.Drawing.Imaging.ImageFormat.Bmp);
                    break;

                case 3:
                    image.Save(fs,
                       System.Drawing.Imaging.ImageFormat.Gif);
                    break;
            }

            fs.Close();
        }



    }

Twoje zdarzenie button_click powinno mieć kod podobny do tego ...

private void btnScreenShot_Click(object sender, EventArgs e)
    {

        SaveFileDialog saveFileDialog1 = new SaveFileDialog();


        ScreenCapturer.CaptureAndSave(filename, mode, format, saveFileDialog1);

    }//
Junior CSharp
źródło
1

Jeśli chcesz użyć kodu zarządzanego: spowoduje to przechwycenie dowolnego okna za pośrednictwem ProcessId.

Użyłem następujących poleceń, aby aktywować okno.

Microsoft.VisualBasic.Interaction.AppActivate(ProcessId);
Threading.Thread.Sleep(20);

Użyłem ekranu drukowania, aby przechwycić okno.

SendKeys.SendWait("%{PRTSC}");
Threading.Thread.Sleep(40);
IDataObject objData = Clipboard.GetDataObject();
Klaus Heinrich
źródło
1

Użyj poniższego kodu:

            // Shot size = screen size
            Size shotSize = Screen.PrimaryScreen.Bounds.Size;

            // the upper left point in the screen to start shot
            // 0,0 to get the shot from upper left point
            Point upperScreenPoint = new Point(0, 0);

            // the upper left point in the image to put the shot
            Point upperDestinationPoint = new Point(0, 0);

            // create image to get the shot in it
            Bitmap shot = new Bitmap(shotSize.Width, shotSize.Height);

            // new Graphics instance 
            Graphics graphics = Graphics.FromImage(shot);

            // get the shot by Graphics class 
            graphics.CopyFromScreen(upperScreenPoint, upperDestinationPoint, shotSize);

            // return the image
            pictureBox1.Image = shot;
Muhanned F.Obaid
źródło
0

Na podstawie odpowiedzi ArsenMkrt, ale ta umożliwia przechwycenie kontrolki w formularzu (piszę na przykład narzędzie, które ma w sobie kontrolkę WebBrowser i chce przechwycić tylko jej wyświetlanie). Zwróć uwagę na użycie metody PointToScreen:

//Project: WebCapture
//Filename: ScreenshotUtils.cs
//Author: George Birbilis (http://zoomicon.com)
//Version: 20130820

using System.Drawing;
using System.Windows.Forms;

namespace WebCapture
{
  public static class ScreenshotUtils
  {

    public static Rectangle Offseted(this Rectangle r, Point p)
    {
      r.Offset(p);
      return r;
    }

    public static Bitmap GetScreenshot(this Control c)
    {
      return GetScreenshot(new Rectangle(c.PointToScreen(Point.Empty), c.Size));
    }

    public static Bitmap GetScreenshot(Rectangle bounds)
    {
      Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
      using (Graphics g = Graphics.FromImage(bitmap))
        g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
      return bitmap;
    }

    public const string DEFAULT_IMAGESAVEFILEDIALOG_TITLE = "Save image";
    public const string DEFAULT_IMAGESAVEFILEDIALOG_FILTER = "PNG Image (*.png)|*.png|JPEG Image (*.jpg)|*.jpg|Bitmap Image (*.bmp)|*.bmp|GIF Image (*.gif)|*.gif";

    public const string CUSTOMPLACES_COMPUTER = "0AC0837C-BBF8-452A-850D-79D08E667CA7";
    public const string CUSTOMPLACES_DESKTOP = "B4BFCC3A-DB2C-424C-B029-7FE99A87C641";
    public const string CUSTOMPLACES_DOCUMENTS = "FDD39AD0-238F-46AF-ADB4-6C85480369C7";
    public const string CUSTOMPLACES_PICTURES = "33E28130-4E1E-4676-835A-98395C3BC3BB";
    public const string CUSTOMPLACES_PUBLICPICTURES = "B6EBFB86-6907-413C-9AF7-4FC2ABF07CC5";
    public const string CUSTOMPLACES_RECENT = "AE50C081-EBD2-438A-8655-8A092E34987A";

    public static SaveFileDialog GetImageSaveFileDialog(
      string title = DEFAULT_IMAGESAVEFILEDIALOG_TITLE, 
      string filter = DEFAULT_IMAGESAVEFILEDIALOG_FILTER)
    {
      SaveFileDialog dialog = new SaveFileDialog();

      dialog.Title = title;
      dialog.Filter = filter;


      /* //this seems to throw error on Windows Server 2008 R2, must be for Windows Vista only
      dialog.CustomPlaces.Add(CUSTOMPLACES_COMPUTER);
      dialog.CustomPlaces.Add(CUSTOMPLACES_DESKTOP);
      dialog.CustomPlaces.Add(CUSTOMPLACES_DOCUMENTS);
      dialog.CustomPlaces.Add(CUSTOMPLACES_PICTURES);
      dialog.CustomPlaces.Add(CUSTOMPLACES_PUBLICPICTURES);
      dialog.CustomPlaces.Add(CUSTOMPLACES_RECENT);
      */

      return dialog;
    }

    public static void ShowSaveFileDialog(this Image image, IWin32Window owner = null)
    {
      using (SaveFileDialog dlg = GetImageSaveFileDialog())
        if (dlg.ShowDialog(owner) == DialogResult.OK)
          image.Save(dlg.FileName);
    }

  }
}

Mając obiekt Bitmap, możesz po prostu wywołać na nim Save

private void btnCapture_Click(object sender, EventArgs e)
{
  webBrowser.GetScreenshot().Save("C://test.jpg", ImageFormat.Jpeg);
}

Powyższe zakłada, że ​​GC pobierze bitmapę, ale może lepiej jest przypisać wynik someControl.getScreenshot () do zmiennej Bitmap, a następnie usunąć tę zmienną ręcznie po zakończeniu z każdym obrazem, zwłaszcza jeśli robisz to często przechwytywanie ( powiedz, że masz listę stron internetowych, które chcesz załadować i zapisać ich zrzuty ekranu):

private void btnCapture_Click(object sender, EventArgs e)
{
  Bitmap bitmap = webBrowser.GetScreenshot();
  bitmap.ShowSaveFileDialog();
  bitmap.Dispose(); //release bitmap resources
}

Jeszcze lepiej, można by zastosować klauzulę using, która ma dodatkową zaletę zwalniania zasobów bitmapy nawet w przypadku wystąpienia wyjątku wewnątrz bloku using (potomnego):

private void btnCapture_Click(object sender, EventArgs e)
{
  using(Bitmap bitmap = webBrowser.GetScreenshot())
    bitmap.ShowSaveFileDialog();
  //exit from using block will release bitmap resources even if exception occured
}

Aktualizacja:

Teraz narzędzie WebCapture jest wdrażane za pomocą technologii ClickOnce ( http://gallery.clipflair.net/WebCapture ) z Internetu (ma również dobrą obsługę automatycznej aktualizacji dzięki ClickOnce), a jego kod źródłowy można znaleźć na https://github.com/Zoomicon / ClipFlair / tree / master / Server / Tools / WebCapture

George Birbilis
źródło