如何使用Mono C#截屏?

5

我正在尝试使用Mono C#中的代码获取屏幕截图,但是当我调用CopyFromScreen时,会出现System.NotImplementedException异常。我的代码在.NET上运行正常,那么是否有其他方法可以在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);

我正在使用Mono JIT编译器版本2.4.2.3(Debian 2.4.2.3+dfsg-2)

更新:Mono无法截屏。 遗憾。 糟糕。


1
请在你的问题中指明你当前使用的Mono版本。 - Bozhidar Batsov
我现在不在电脑旁,但如果你想让我指定版本号,这是否意味着问题已经在比我使用的版本更新的版本中得到了解决? - vagabond
我正在使用Mono JIT编译器版本2.4.2.3(Debian 2.4.2.3+dfsg-2)。 - vagabond
@vagabond,你的“更新:”似乎是错误的?有人在回答中发布了一条消息,说现在可以在没有GTK#的情况下进行屏幕截图? - Ian Grainger
3个回答

6

我想另一种选择是使用gtk#来获取屏幕截图。您需要创建一个带有GTK#支持的mono项目,之后以下代码应该会执行:

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

您还可以使用P\Invoke并直接调用GTK函数。

希望这能帮到您,祝好。


谢谢。看起来Mono本身无法截屏。我真的很失望。好在我在项目早期就发现了这个问题,看来我要转向Java了...该死。 - vagabond
如果你正在寻找一个跨平台的开发框架,你可能也想看看C++/Python + QT。 - serge_gubenko

2

当我们需要在Linux和OS X上使用Mono拍摄截屏时,我们遇到了同样的问题。

实际上,在Linux上可以使用CopyFromScreen。但是您需要安装mono-complete软件包,其中包括System.Drawing。

对于OS X,最可靠的方法是启动screencapture命令行工具。它默认存在。

对于Linux,为了使其更加可靠,可以使用ImageMagic中的import命令行(在这种情况下,您需要使它成为依赖项),或强制依赖于包括System.Drawing的mono-complete软件包。根据我们的测试,Gtk#方法不可靠,并且可能会提供空白屏幕或失败并出现异常(我们每天在各种机器上拍摄数千张截图)。

另一件事是,当您需要在一个截图中捕获多个显示器时,您必须在单独通过CopyFromScreen拍摄其后正确对齐单独的截图。

因此,我们目前有这个解决方案:

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

或者通过NuGet安装它(免责声明:我是作者):

PM> Install-Package Pranas.ScreenshotCapture

我们在产品中广泛使用它,并在许多设置上进行定期的代码改进,并将注释放入博客中。

0

使用Mono 2.4.4,您可以在没有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); } } } }


你确定这段代码没问题吗?我也遇到了同样的 System.NotImplementedException 异常。 - mjsr
你必须在Linux上安装System.Drawing.mono-complete包。在Mac OS X上,你必须使用PInvoke(不可靠和复杂),或者简单地启动screencapture工具。有关详细信息,请参见我的答案。 - Evgenyt

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