How to take a screenshot with Mono C#?

2019-01-19 07:08发布

I'm trying to use use code get a screenshot in Mono C# but I'm getting a System.NotImplementedException when I call CopyFromScreen. My code works with .NET, so is there an alternate way of getting a screenshot using Mono?

Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap as Image);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();
bitmap.Save(memoryStream, imageFormat);
bitmap.Save(@"\tmp\screenshot.png", ImageFormat.Png);

I am using Mono JIT compiler version 2.4.2.3 (Debian 2.4.2.3+dfsg-2)

UPDATE: Mono cannot take screenshots. Sad. Lame.

标签: c# mono
3条回答
劫难
2楼-- · 2019-01-19 07:22

With Mono 2.4.4 you can get the whole screen without GTK#:

public static class MonoScreenShooter { public static void TakeScreenshot(string filePath) { using (Bitmap bmpScreenCapture = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height)) { using (Graphics g = Graphics.FromImage(bmpScreenCapture)) { g.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, bmpScreenCapture.Size, CopyPixelOperation.SourceCopy); } bmpScreenCapture.Save(filePath, System.Drawing.Imaging.ImageFormat.Jpeg); } } } }

查看更多
一夜七次
3楼-- · 2019-01-19 07:33

I guess an alternative would be using gtk# to get a screenshot. You would need to create a mono project with GTK# support, after that following code should execute:

Gdk.Window window = Gdk.Global.DefaultRootWindow;
if (window!=null)
{           
    Gdk.Pixbuf pixBuf = new Gdk.Pixbuf(Gdk.Colorspace.Rgb, false, 8, 
                                       window.Screen.Width, window.Screen.Height);          
    pixBuf.GetFromDrawable(window, Gdk.Colormap.System, 0, 0, 0, 0, 
                           window.Screen.Width, window.Screen.Height);          
    pixBuf.ScaleSimple(400, 300, Gdk.InterpType.Bilinear);
    pixBuf.Save("screenshot0.jpeg", "jpeg");
}

you could also use P\Invoke and call GTK functions directly.

hope this helps, regards

查看更多
迷人小祖宗
4楼-- · 2019-01-19 07:39

We had the same problem when we needed to take screenshots with Mono on Linux and OS X in the same software.

Actually, you can use CopyFromScreen on Mono on Linux. However you need to install mono-complete package, which includes System.Drawing.

For OS X the most reliable way is to Process.Start the screencapture command line tool. It is present there by default.

For Linux, to make it reliable, one can use either import command line from ImageMagic (you'll need to make it a dependency in this case), or force dependency on mono-complete package which includes System.Drawing. Gtk# approach is unreliable according to our tests and may provide blank screens or fail with exception (and we take thousands of screenshots on various machines every day).

Another thing is that when you need to capture multiple displays in one screenshot, you have to properly align separate screenshots after taking those separately via CopyFromScreen.

So we have this solution so far:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Windows.Forms;

namespace Pranas
{
    /// <summary>
    ///     ScreenshotCapture
    /// </summary>
    public static class ScreenshotCapture
    {
        #region Public static methods

        /// <summary>
        ///     Capture screenshot to Image object
        /// </summary>
        /// <param name="onlyPrimaryScreen">Create screen only from primary screen</param>
        /// <returns></returns>
        public static Image TakeScreenshot(bool onlyPrimaryScreen = false)
        {
            try
            {
                return WindowsCapture(onlyPrimaryScreen);
            }
            catch (Exception)
            {
                return OsXCapture(onlyPrimaryScreen);
            }
        }

        #endregion

        #region  Private static methods

        //private static Image ImageMagicCapture(bool onlyPrimaryScreen)
        //{
        //  return ExecuteCaptureProcess("import", "-window root ");
        //}

        private static Image OsXCapture(bool onlyPrimaryScreen)
        {
            var data = ExecuteCaptureProcess(
                "screencapture",
                string.Format("{0} -T0 -tpng -S -x", onlyPrimaryScreen ? "-m" : ""));
            return data;
        }


        /// <summary>
        ///     Start execute process with parameters
        /// </summary>
        /// <param name="execModule">Application name</param>
        /// <param name="parameters">Command line parameters</param>
        /// <returns>Bytes for destination image</returns>
        private static Image ExecuteCaptureProcess(string execModule, string parameters)
        {
            var imageFileName = Path.Combine(Path.GetTempPath(), string.Format("screenshot_{0}.jpg", Guid.NewGuid()));

            var process = Process.Start(execModule, string.Format("{0} {1}", parameters, imageFileName));
            if (process == null)
            {
                throw new InvalidOperationException(string.Format("Executable of '{0}' was not found", execModule));
            }
            process.WaitForExit();

            if (!File.Exists(imageFileName))
            {
                throw new InvalidOperationException(string.Format("Failed to capture screenshot using {0}", execModule));
            }

            try
            {
                return Image.FromFile(imageFileName);
            }
            finally
            {
                File.Delete(imageFileName);
            }
        }

        /// <summary>
        ///     Capture screenshot with .NET standard implementation
        /// </summary>
        /// <param name="onlyPrimaryScreen"></param>
        /// <returns>Return bytes of screenshot image</returns>
        private static Image WindowsCapture(bool onlyPrimaryScreen)
        {
            if (onlyPrimaryScreen) return ScreenCapture(Screen.PrimaryScreen);
            var bitmaps = (Screen.AllScreens.OrderBy(s => s.Bounds.Left).Select(ScreenCapture)).ToArray();
            return CombineBitmap(bitmaps);
        }

        /// <summary>
        ///     Create screenshot of single display
        /// </summary>
        /// <param name="screen"></param>
        /// <returns></returns>
        private static Bitmap ScreenCapture(Screen screen)
        {
            var bitmap = new Bitmap(screen.Bounds.Width, screen.Bounds.Height, PixelFormat.Format32bppArgb);

            using (var graphics = Graphics.FromImage(bitmap))
            {
                graphics.CopyFromScreen(
                    screen.Bounds.X,
                    screen.Bounds.Y,
                    0,
                    0,
                    screen.Bounds.Size,
                    CopyPixelOperation.SourceCopy);
            }

            return bitmap;
        }

        /// <summary>
        ///     Combine images into one bitmap
        /// </summary>
        /// <param name="images"></param>
        /// <returns>Combined image</returns>
        private static Image CombineBitmap(ICollection<Image> images)
        {
            Image finalImage = null;

            try
            {
                var width = 0;
                var height = 0;

                foreach (var image in images)
                {
                    width += image.Width;
                    height = image.Height > height ? image.Height : height;
                }

                finalImage = new Bitmap(width, height);

                using (var g = Graphics.FromImage(finalImage))
                {
                    g.Clear(Color.Black);

                    var offset = 0;
                    foreach (var image in images)
                    {
                        g.DrawImage(image,
                            new Rectangle(offset, 0, image.Width, image.Height));
                        offset += image.Width;
                    }
                }
            }
            catch (Exception ex)
            {
                if (finalImage != null)
                    finalImage.Dispose();
                throw ex;
            }
            finally
            {
                //clean up memory
                foreach (var image in images)
                {
                    image.Dispose();
                }
            }

            return finalImage;
        }

        #endregion
    }
}

Or install it via NuGet (disclaimer: I'm the author):

PM> Install-Package Pranas.ScreenshotCapture

We use it heavily in our product on many setups, so we periodically improve the code and put notes into blog.

查看更多
登录 后发表回答