【发布时间】:2013-03-28 16:15:57
【问题描述】:
我正在使用具有 4 个输出(监视器)的系统,例如每个输出 1280x1024 像素。我需要整个桌面和上面所有打开的应用程序的屏幕截图。
我试过GetDesktopWindow() (MSDN),但它不能正常工作。某些表格未显示在捕获的图片上。
【问题讨论】:
标签: c# screenshot desktop-application multiple-monitors multiscreen
我正在使用具有 4 个输出(监视器)的系统,例如每个输出 1280x1024 像素。我需要整个桌面和上面所有打开的应用程序的屏幕截图。
我试过GetDesktopWindow() (MSDN),但它不能正常工作。某些表格未显示在捕获的图片上。
【问题讨论】:
标签: c# screenshot desktop-application multiple-monitors multiscreen
我尝试了 GetDesktopWindow() 函数,但它不能正常工作。
当然不是。
GetDesktopWindow 函数向桌面窗口返回一个句柄。它与捕获该窗口的图像没有任何关系。
此外,桌面窗口与“整个屏幕”不是一回事。它特指桌面窗口。有关更多信息以及滥用此函数返回的句柄时可能出现的问题,请参阅this article。
我正在使用具有 4 个输出(监视器)的系统,每个输出的分辨率为 1280x1024(例如)。我需要整个桌面和上面所有打开的应用程序的屏幕截图。
这在 .NET Framework 中使用Graphics.CopyFromScreen 方法相对简单。你甚至不需要做任何 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);
}
编辑:
请在不是您的主线程的线程中使用 wpf 应用程序检查您的解决方案。我尝试过这个。它不起作用!
嗯,我没有在问题上看到 WPF 标记,也没有在正文中的任何地方提到。
不过,没关系。只要您添加适当的引用并使用声明,我发布的代码在 WPF 应用程序中就可以正常工作。您将需要System.Windows.Forms 和System.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();
}
}
【讨论】:
我创建了一个小助手,因为我今天需要这个案例并尝试了许多不同的功能。与监视器的数量无关,您可以将其保存为磁盘上的文件或使用以下代码块将其存储在 db 中的二进制字段中。
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);
}
}
}
var bounds = new Rectangle();
bounds = Screen.AllScreens.Aggregate(bounds, (current, screen)
=> Rectangle.Union(current, screen.Bounds));
_card.ScreenShot = Convert.ToBase64String(ScreenShotHelper.TakeAsByteArray(bounds));
var bounds = new Rectangle();
bounds = Screen.AllScreens.Aggregate(bounds, (current, screen)
=> Rectangle.Union(current, screen.Bounds));
ScreenShotHelper.TakeAndSave(@"d:\screenshot.png", bounds, ImageFormat.Png);
【讨论】: