Cattura screenshot della finestra attiva?


175

Sto realizzando un'applicazione di cattura dello schermo e tutto sta andando bene. Tutto quello che devo fare è catturare la finestra attiva e fare uno screenshot di questa finestra attiva. Qualcuno sa come posso farlo?


17
"Finestra attiva" indica la finestra attiva della TUA app o la finestra che sarebbe attiva se l'app fosse nascosta?
Corey Trager

Risposte:


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/


7
So già come catturare uno screenshot standard. Devo solo sapere come catturare la finestra attiva.
utente

3
Ottima soluzione! Grazie per il link.
Matthew M.

6
@joe ottengo un errore GDI + quando provo a eseguire il codice sopra. l'errore si verifica nella classe ScreenCpature durante il salvataggio delle immagini.
hurnhu,

2
Eccellente! Volevo catturare il contenuto di un pannello nella mia app. Così ho fatto sc.CaptureWindowToFile (panel1.Handle, "c: \ temp.jpg", imageformat.jpg) e voilà!
D. Kermott,

2
Sulla risoluzione HD con elementi dell'interfaccia di Windows ingranditi, questa classe non acquisisce l'intero schermo.
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);
}

per catturare l'uso della finestra corrente

 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);
 }

! Neat Pensavo che dovresti usare WinAPI. Sai se questo è implementato su Mono?
Lucas Jones,

Questo mi dà solo uno screenshot normale. Non devo usare WinAPI?
utente

Ciao, posso sapere come eseguire l'acquisizione dello schermo in aspx?
beny lim,

Ciao @benylim, dai un'occhiata a questo ( stackoverflow.com/questions/701798/… ) e questo ( social.msdn.microsoft.com/Forums/en/netfxjscript/thread/… ), spero che questo aiuti
Arsen Mkrtchyan

Isitz può fare solo con java script?
beny lim,

29

Suggerisco la prossima soluzione per catturare qualsiasi finestra attiva corrente (non solo la nostra applicazione C #) o l'intero schermo con la determinazione della posizione del cursore relativamente all'angolo superiore sinistro della finestra o dello schermo rispettivamente:

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;
    }
}

Perché dobbiamo dichiarare la nostra struttura Rectangle anziché utilizzare System.Drawing.Rectangle? È solo per poter aggiungere un attributo? Sai se dovremmo farlo anche per la struttura Point?
Caster Troy,

@Alex, ho provato a sostituire il mio Rectcon System Rectangle, ma successivamente la funzione ha GetWindowRectrestituito un rettangolo errato. Invece di Righte Top, imposta Widthe Heightdel rettangolo di output.
Ivan Kochurkin,

1
Un avvertimento: la finestra in primo piano non è necessariamente la finestra dell'applicazione da cui viene chiamata. var thisWindowHandle = new WindowInteropHelper(Application.Current.MainWindow).Handle;Invece, per un'app WPF a finestra singola che
useresti

23

Ecco uno snippet per acquisire il desktop o la finestra attiva. Non ha riferimenti a Windows Form.

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;
    }
}

Come catturare l'intero schermo:

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

Come catturare la finestra attiva:

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

Originariamente trovato qui: http://www.snippetsource.net/Snippet/158/capture-screenshot-in-c


Grazie mille Christian per questo fantastico frammento. Mi ha aiutato molto!
casaout,

Funziona benissimo ma non funziona se l'utente ha impostato il ridimensionamento su quel display (ovvero, il 125% taglia i lati sinistro e inferiore)
russelrillema,

12

Il codice di KvanTTT ha funzionato alla grande. L'ho esteso un po 'per consentire un po' più di flessibilità sul formato di salvataggio, nonché la possibilità di salvare da hWnd, .NET Control / Form. È possibile ottenere una bitmap o salvare su file, con alcune opzioni.

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("yyyy-MM-dd@hh.mm.ss"));
                image.Save(filename, format);
          }
     }
}

Grandi capacità di recupero!
Ahmed Alejo,

Top bit di codice, ha funzionato senza problemi ed era proprio quello che stavo cercando. Grazie per aver condiviso.
Hugo Yates,



1

Un piccolo accorgimento per il metodo del vuoto statico ImageSave () ti darà la possibilità di salvarlo. Il credito va a 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();
        }



    }

Il tuo evento button_click dovrebbe essere codificato in questo modo ...

private void btnScreenShot_Click(object sender, EventArgs e)
    {

        SaveFileDialog saveFileDialog1 = new SaveFileDialog();


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

    }//

1

Se si desidera utilizzare il codice gestito: questo acquisirà qualsiasi finestra tramite ProcessId.

Ho usato quanto segue per rendere attiva la finestra.

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

Ho usato la schermata di stampa per catturare una finestra.

SendKeys.SendWait("%{PRTSC}");
Threading.Thread.Sleep(40);
IDataObject objData = Clipboard.GetDataObject();

1

Usa il seguente codice:

            // 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;

0

Basato sulla risposta di ArsenMkrt, ma questo ti permette di catturare un controllo nel tuo modulo (sto scrivendo uno strumento per esempio che ha un controllo WebBrowser e voglio catturare solo il suo display). Nota l'uso del metodo 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);
    }

  }
}

Avendo l'oggetto Bitmap puoi semplicemente chiamare Salva su di esso

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

Quanto sopra presuppone che il GC prenderà la bitmap, ma forse è meglio assegnare il risultato di someControl.getScreenshot () a una variabile Bitmap, quindi disporre quella variabile manualmente al termine di ogni immagine, soprattutto se si esegue questa acquisizione spesso ( supponiamo di avere un elenco di pagine Web che si desidera caricare e salvarne delle schermate):

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

Ancora meglio, potrebbe impiegare una clausola using, che ha l'ulteriore vantaggio di rilasciare le risorse bitmap anche nel caso in cui si verifichi un'eccezione all'interno del blocco using (child):

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
}

Aggiornare:

Ora lo strumento WebCapture è implementato da ClickOnce ( http://gallery.clipflair.net/WebCapture ) dal Web (ha anche un buon supporto di aggiornamento automatico grazie a ClickOnce) e puoi trovare il suo codice sorgente su https://github.com/Zoomicon / ClipFlair / albero / master / Server / strumenti / WebCapture

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.