截取所有可见应用程序和表单的多个桌面的屏幕截图

mat*_*mat 6 c# screenshot multiple-monitors desktop-application multiscreen

我正在使用一个具有4个输出(监视器)的系统,每个输出有1280x1024像素.我需要整个桌面和所有打开的应用程序的屏幕截图.

我试过GetDesktopWindow()(MSDN)但它无法正常工作.某些表单未显示在捕获的图片上.

Cod*_*ray 25

我尝试了GetDesktopWindow()函数,但它无法正常工作.

当然不是.

GetDesktopWindow函数返回桌面窗口的句柄.它与捕获该窗口的图像没有任何关系.

此外,桌面窗口与"整个屏幕"不同.它特指桌面窗口.有关更多信息以及滥用此函数返回的句柄可能出错的内容,请参阅此文章.

我正在使用一个系统,每个输出有4个输出(监视器)和1280x1024(例如).我需要整个桌面和所有打开的应用程序的截图.

使用该Graphics.CopyFromScreen方法在.NET Framework中执行起来相对简单.你甚至不需要做任何P/Invoke!

在这种情况下唯一的技巧是确保您传递适当的尺寸.由于您有4个监视器,因此仅传递主屏幕的尺寸将不起作用.您需要传递整个虚拟屏幕的尺寸,其中包含所有显示器.通过查询SystemInformation.VirtualScreen属性来检索它,该属性返回虚拟屏幕的边界.如文档所示,这是多监视器系统上整个桌面的界限.

示例代码:

// Determine the size of the "virtual screen", which includes all monitors.
int screenLeft   = SystemInformation.VirtualScreen.Left;
int screenTop    = SystemInformation.VirtualScreen.Top;
int screenWidth  = SystemInformation.VirtualScreen.Width;
int screenHeight = SystemInformation.VirtualScreen.Height;

// Create a bitmap of the appropriate size to receive the screenshot.
using (Bitmap bmp = new Bitmap(screenWidth, screenHeight))
{
    // Draw the screenshot into our bitmap.
    using (Graphics g = Graphics.FromImage(bmp))
    {
        g.CopyFromScreen(screenLeft, screenTop, 0, 0, bmp.Size);
    }

    // Do something with the Bitmap here, like save it to a file:
    bmp.Save(savePath, ImageFormat.Jpeg);
}
Run Code Online (Sandbox Code Playgroud)

编辑:

请在一个不是主线程的线程中使用wpf应用程序检查您的解决方案.我尝试过这个.它不起作用!

嗯,我没有在问题上看到WPF标签,也没有在身体的任何地方提到过.

不过,无论如何.我发布的代码在WPF应用程序中运行得很好,只要添加适当的引用并使用声明即可.你需要System.Windows.FormsSystem.Drawing.可能有更多WPF式的方式,这不需要依赖这些WinForms程序集,但我不知道它是什么.

它甚至适用于另一个线程.这里没有任何东西需要UI线程.

是的,我测试了它.这是我的完整测试代码:

using System.Windows;
using System.Windows.Forms;   // also requires a reference to this assembly
using System.Drawing;         // also requires a reference to this assembly
using System.Drawing.Imaging;
using System.Threading;

public partial class MainWindow : Window
{
   public MainWindow()
   {
      InitializeComponent();
   }

   private void button1_Click(object sender, RoutedEventArgs e)
   {
      // Create a new thread for demonstration purposes.
      Thread thread = new Thread(() =>
      {
         // Determine the size of the "virtual screen", which includes all monitors.
         int screenLeft   = SystemInformation.VirtualScreen.Left;
    int screenTop    = SystemInformation.VirtualScreen.Top;
    int screenWidth  = SystemInformation.VirtualScreen.Width;
    int screenHeight = SystemInformation.VirtualScreen.Height;

         // Create a bitmap of the appropriate size to receive the screenshot.
         using (Bitmap bmp = new Bitmap(screenWidth, screenHeight))
         {
            // Draw the screenshot into our bitmap.
            using (Graphics g = Graphics.FromImage(bmp))
            {
               g.CopyFromScreen(screenLeft, screenTop, 0, 0, bmp.Size);
            }

            // Do something with the Bitmap here, like save it to a file:
            bmp.Save("G:\\TestImage.jpg", ImageFormat.Jpeg);
         }
      });
      thread.SetApartmentState(ApartmentState.STA);
      thread.Start();
   }
}
Run Code Online (Sandbox Code Playgroud)

  • 我建议您使用完整性:g.CopyFromScreen(SystemInformation.VirtualScreen.Left,SystemInformation.VirtualScreen.Top,0,0,bmp.Size); 这样,如果你的监视器是堆叠的,左上角并不总是(0,0)它可能是(-768,0).这应该解释这一点. (3认同)

gur*_*kan 6

我创建了一个小助手,因为我今天需要这个案例并尝试了许多不同的功能。与监视器的数量无关,您可以将其保存为磁盘上的文件或使用以下代码块将其存储在 db 中的二进制字段中。

ScreenShotHelper.cs

using System.ComponentModel;//This namespace is required for only Win32Exception. You can remove it if you are catching exceptions from another layer.
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace Company.Core.Helpers.Win32 {

    public static class ScreenShotHelper {

        private static Bitmap CopyFromScreen(Rectangle bounds) {
            try {
                var image = new Bitmap(bounds.Width, bounds.Height);
                using var graphics = Graphics.FromImage(image);
                graphics.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size);
                return image;
            }
            catch(Win32Exception) {//When screen saver is active
                return null;
            }
        }

        public static Image Take(Rectangle bounds) {
            return CopyFromScreen(bounds);

        }

        public static byte[] TakeAsByteArray(Rectangle bounds) {
            using var image = CopyFromScreen(bounds);
            using var ms = new MemoryStream();
            image.Save(ms, ImageFormat.Png);
            return ms.ToArray();
        }   

        public static void TakeAndSave(string path, Rectangle bounds, ImageFormat imageFormat) {
            using var image = CopyFromScreen(bounds);
            image.Save(path, imageFormat);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法 - 二进制字段

var bounds = new Rectangle();
bounds = Screen.AllScreens.Aggregate(bounds, (current, screen) 
                           => Rectangle.Union(current, screen.Bounds));
_card.ScreenShot = Convert.ToBase64String(ScreenShotHelper.TakeAsByteArray(bounds));
Run Code Online (Sandbox Code Playgroud)

用法 - 磁盘文件

var bounds = new Rectangle();
bounds = Screen.AllScreens.Aggregate(bounds, (current, screen) 
                           => Rectangle.Union(current, screen.Bounds));
ScreenShotHelper.TakeAndSave(@"d:\screenshot.png", bounds, ImageFormat.Png);           
Run Code Online (Sandbox Code Playgroud)