【问题标题】:Capture screenshot of active window?捕获活动窗口的屏幕截图?
【发布时间】:2022-01-22 20:14:24
【问题描述】:

我正在制作一个屏幕捕获应用程序,一切正常。我需要做的就是捕获活动窗口并截取这个活动窗口的屏幕截图。有谁知道我该怎么做?

【问题讨论】:

  • “活动窗口”是指您的应用程序的活动窗口还是在您的应用程序被隐藏时将处于活动状态的窗口?
  • 如果您想要所有显示器的屏幕截图:stackoverflow.com/questions/15847637/…

标签: c# screenshot active-window


【解决方案1】:
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);
}

用于捕获当前窗口使用

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

【讨论】:

【解决方案2】:
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 我在尝试运行上述代码时遇到 GDI+ 错误。错误发生在 ScreenCpature 类中保存图像。
  • 太棒了!我想在我的应用程序中捕获面板的内容。所以我做了 sc.CaptureWindowToFile(panel1.Handle, "c:\temp.jpg", imageformat.jpg) 瞧!
  • 在具有缩放 Windows 界面元素的高清分辨率上,此类不会捕获整个屏幕。
  • 仅运行 Windows Phone 8.1 的移动设备支持 ScreenCapture 功能。 Windows 10 不支持此 API。
【解决方案3】:

我建议下一个解决方案来捕获任何当前活动窗口(不仅是我们的 C# 应用程序)或整个屏幕,并分别确定相对于窗口或屏幕左上角的光标位置:

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

【讨论】:

  • 为什么我们需要声明自己的 Rectangle 结构而不是使用 System.Drawing.Rectangle?仅仅是为了我们可以添加一个属性吗?你知道我们是否也应该对 Point 结构这样做吗?
  • @Alex,我试图用系统Rectangle 替换我的Rect,但之后函数GetWindowRect 返回了错误的矩形。它设置了输出矩形的WidthHeight,而不是RightTop
  • 一个警告:前台窗口不一定是调用它的应用程序的窗口。对于单窗口 WPF 应用程序,您可以改用 var thisWindowHandle = new WindowInteropHelper(Application.Current.MainWindow).Handle;
【解决方案4】:

这是一个捕捉桌面或活动窗口的 sn-p。 它没有引用 Windows 窗体。

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

如何截取整个屏幕:

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

如何捕获活动窗口:

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

最初在这里找到:http://www.snippetsource.net/Snippet/158/capture-screenshot-in-c

【讨论】:

  • 非常感谢 Christian 带来了这么棒的 sn-p。这对我帮助很大!
  • 这很好用,但如果用户在该显示器上设置了缩放比例(即 125% 切断左侧和底部),则不起作用
  • 为此需要库System.Drawing.Common
  • 节省了我的时间。非常感谢。
【解决方案5】:

KvanTTT 的代码运行良好。我对其进行了一些扩展,以便在保存格式上提供更多的灵活性,以及​​通过 hWnd、.NET Control/Form 进行保存的能力。您可以使用一些选项获取位图或保存到文件。

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

【讨论】:

  • 顶级代码,让它正常工作,这正是我想要的。谢谢分享。
【解决方案6】:

我假设你使用Graphics.CopyFromScreen 来获取屏幕截图。

您可以使用P/Invoke to GetForegroundWindow (and then get its position and size) 来确定您需要从哪个区域进行复制。

【讨论】:

    【解决方案7】:

    你可以使用这个问题的代码:How can I save a screenshot directly to a file in Windows?

    只需将WIN32_API.GetDesktopWindow() 更改为您要捕获的窗口的Handle 属性即可。

    【讨论】:

      【解决方案8】:

      如果您想使用托管代码:这将通过 ProcessId 捕获任何窗口。

      我使用以下方法使窗口处于活动状态。

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

      我使用打印屏幕捕获了一个窗口。

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

      【讨论】:

        【解决方案9】:

        使用以下代码:

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

        【讨论】:

          【解决方案10】:

          如果设置了桌面缩放,则可以使用。

          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);
                  GetScale getScale = new GetScale();
                  var bounds = new Rectangle(rect.Left, rect.Top, (int)((rect.Right - rect.Left)* getScale.getScalingFactor()), (int)((rect.Bottom - rect.Top )* getScale.getScalingFactor()));
                  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;
              }
          }
          

          【讨论】:

          • 这里在截屏之前会检查是否设置了缩放。如果设置,则因子乘以矩形的高度和宽度。
          • 似乎缺少GetScale 类。
          【解决方案11】:

          对方法 static void ImageSave() 稍作调整,您就可以选择保存它的位置。归功于 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();
                  }
          
          
          
              }
          

          您的 button_click 事件应该这样编码...

          private void btnScreenShot_Click(object sender, EventArgs e)
              {
          
                  SaveFileDialog saveFileDialog1 = new SaveFileDialog();
          
          
                  ScreenCapturer.CaptureAndSave(filename, mode, format, saveFileDialog1);
          
              }//
          

          【讨论】:

            【解决方案12】:

            基于 ArsenMkrt 的回复,但是这个允许您捕获表单中的控件(例如,我正在编写一个工具,其中包含一个 WebBrowser 控件并且只想捕获它的显示)。注意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);
                }
            
              }
            }
            

            拥有 Bitmap 对象,您可以在其上调用 Save

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

            以上假设 GC 将抓取位图,但也许最好将 someControl.getScreenshot() 的结果分配给位图变量,然后在完成每个图像时手动处理该变量,特别是如果你正在这样做经常抓取(假设您有一个网页列表要加载并保存它们的屏幕截图):

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

            更好的是,可以使用 using 子句,它具有释放位图资源的额外好处,即使在 using(子)块内发生异常的情况下:

            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
            }
            

            更新:

            现在 WebCapture 工具已从 Web 部署 ClickOnce (http://gallery.clipflair.net/WebCapture)(由于 ClickOnce,它还具有很好的自动更新支持),您可以在 https://github.com/Zoomicon/ClipFlair/tree/master/Server/Tools/WebCapture 找到它的源代码

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2013-11-26
              • 1970-01-01
              • 2012-01-04
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多