将屏幕截图转换为位图

74

我想在我的代码中捕获屏幕以获得一张图片 - 就像使用键盘上的“打印屏幕”按钮。

有人知道如何实现吗?我没有任何起点。

6个回答

122
你可以使用 Graphics.CopyFromScreen() 方法。
//Create a new bitmap.
var bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
                               Screen.PrimaryScreen.Bounds.Height,
                               PixelFormat.Format32bppArgb);

// Create a graphics object from the bitmap.
var gfxScreenshot = Graphics.FromImage(bmpScreenshot);

// Take the screenshot from the upper left corner to the right bottom corner.
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
                            Screen.PrimaryScreen.Bounds.Y,
                            0,
                            0,
                            Screen.PrimaryScreen.Bounds.Size,
                            CopyPixelOperation.SourceCopy);

// Save the screenshot to the specified path that the user has chosen.
bmpScreenshot.Save("Screenshot.png", ImageFormat.Png);

1
你的答案不正确,请使用 (g.CopyFromScreen( 0, 0, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);) 进行更新。 - Mohsen Afshin
3
请看一下 .NET 的 Screen 对象,以获取其他屏幕。注意在上面的调用中,我使用 Screen.PrimaryScreen 来获取主屏幕吗?您可以使用 Screen.AllScreens 获取它们的数组。Screen.AllScreens[n].Bounds.Width 等等。 - Gary Willoughby
我们已经到了2016年,这仍然完美地运行。谢谢! - Erick G. Hagstrom
3
若将显示设置中的“更改所有项目的大小”调整为“较大”,代码仅能捕捉屏幕的部分内容。如何解决? - Lei Yang

12
我对接受的答案有两个问题。
1. 它不能捕捉到多显示器设置中的所有屏幕。 2. 当使用显示缩放并且您的应用程序没有声明为每个监视器的dpiAware时,Screen类返回的宽度和高度是不正确的。
这是我更新后的解决方案,使用Screen.AllScreens静态属性,并使用p/invoke调用EnumDisplaySettings来获取真实的屏幕分辨率。
using System.Runtime.InteropServices;

const int ENUM_CURRENT_SETTINGS = -1;

foreach (Screen screen in Screen.AllScreens)
{
    var dm = new DEVMODE();
    dm.dmSize = (short)Marshal.SizeOf(typeof(DEVMODE));
    EnumDisplaySettings(screen.DeviceName, ENUM_CURRENT_SETTINGS, ref dm);

    using var bmp = new Bitmap(dm.dmPelsWidth, dm.dmPelsHeight);
    using var g = Graphics.FromImage(bmp);

    g.CopyFromScreen(dm.dmPositionX, dm.dmPositionY, 0, 0, bmp.Size);
    bmp.Save(screen.DeviceName.Split('\\').Last() + ".png");
}

[DllImport("user32.dll")]
static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode);

[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
    private const int CCHDEVICENAME = 0x20;
    private const int CCHFORMNAME = 0x20;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
    public string dmDeviceName;
    public short dmSpecVersion;
    public short dmDriverVersion;
    public short dmSize;
    public short dmDriverExtra;
    public int dmFields;
    public int dmPositionX;
    public int dmPositionY;
    public ScreenOrientation dmDisplayOrientation;
    public int dmDisplayFixedOutput;
    public short dmColor;
    public short dmDuplex;
    public short dmYResolution;
    public short dmTTOption;
    public short dmCollate;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
    public string dmFormName;
    public short dmLogPixels;
    public int dmBitsPerPel;
    public int dmPelsWidth;
    public int dmPelsHeight;
    public int dmDisplayFlags;
    public int dmDisplayFrequency;
    public int dmICMMethod;
    public int dmICMIntent;
    public int dmMediaType;
    public int dmDitherType;
    public int dmReserved1;
    public int dmReserved2;
    public int dmPanningWidth;
    public int dmPanningHeight;
}

参考资料:

https://dev59.com/t1wX5IYBdhLWcg3wrRI5#36864741 http://pinvoke.net/default.aspx/user32/EnumDisplaySettings.html?diff=y


8
// Use this version to capture the full extended desktop (i.e. multiple screens)

Bitmap screenshot = new Bitmap(SystemInformation.VirtualScreen.Width, 
                               SystemInformation.VirtualScreen.Height, 
                               PixelFormat.Format32bppArgb);
Graphics screenGraph = Graphics.FromImage(screenshot);
screenGraph.CopyFromScreen(SystemInformation.VirtualScreen.X, 
                           SystemInformation.VirtualScreen.Y, 
                           0, 
                           0, 
                           SystemInformation.VirtualScreen.Size, 
                           CopyPixelOperation.SourceCopy);

screenshot.Save("Screenshot.png", System.Drawing.Imaging.ImageFormat.Png);

你的代码没有输出正确的图像。它应该是JPEG还是PNG格式? - Jason Foglia
3
功能正常,但用户界面总是会卡顿。对于需要每秒分析10-20次屏幕的项目(如我的项目)不兼容。 - Jurion

1
尝试使用this代码。
Bitmap bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics gr = Graphics.FromImage(bmp);
gr.CopyFromScreen(0, 0, 0, 0, bmp.Size);
pictureBox1.Image = bmp;
bmp.Save("img.png",System.Drawing.Imaging.ImageFormat.Png);

0

我一直在想如何在FORTRAN中做同样的事情,以节省我大量使用PrintScreen、粘贴到Paint等手动操作的时间。我刚刚找到了方法:我使用GETPIXEL_RGB读取我想要的屏幕任何部分的像素,并准备好.bmp头并将其写入.bmp文件,然后写入像素数据。但是,我不认为它能够捕获10到20个屏幕。


0
Bitmap memoryImage;
//Set full width, height for image
memoryImage = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
                       Screen.PrimaryScreen.Bounds.Height,
                       PixelFormat.Format32bppArgb);
Size s = new Size(memoryImage.Width, memoryImage.Height);
Graphics memoryGraphics = Graphics.FromImage(memoryImage);
memoryGraphics.CopyFromScreen(0, 0, 0, 0, s);
string str = "";
try
{
    str = string.Format(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) +
          @"\Screenshot.png");//Set folder to save image
}
catch { };
memoryImage.save(str);

建议您在代码中添加一些解释,这样阅读您的答案的人就能更好、更容易地理解。 - Ibo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接