为什么我可能会在 ac# 屏幕录制应用程序中的 GDI 中遇到错误?

发布于 2024-10-04 21:57:17 字数 1451 浏览 2 评论 0原文

我正在尝试编写一个应用程序,每 40 毫秒拍摄一次屏幕截图并将其保存到磁盘,我在 GDI 中遇到错误

有人知道我试图每 40 毫秒或以 25 fps 的速率保存屏幕截图为 jpeg 是否太冒险?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using System.Windows.Forms;


namespace ScreenRecorder
{
    class Program
    {

    private static System.Timers.Timer screenTimer;

    private static int screenNumber;


    static void Main(string[] args)
    {
        screenTimer = new System.Timers.Timer(40);

        // Hook up the Elapsed event for the timer.
        screenTimer.Elapsed += new System.Timers.ElapsedEventHandler(screenTimer_Elapsed);

        screenTimer.Enabled = true;

        Console.Read();
    }


    static void screenTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        //get the screen
        Image myImage = CaptureScreen();
        myImage.Save(@"C:\stuff\Development\ScreenRecorder\ScreenImages\img" + screenNumber + ".jpg");
        myImage.Dispose();

        screenNumber++;
    }

    private static Image CaptureScreen()
    {
        Rectangle screenSize = Screen.PrimaryScreen.Bounds;
        Bitmap target = new Bitmap(screenSize.Width, screenSize.Height);
        using (Graphics g = Graphics.FromImage(target))
        {
            g.CopyFromScreen(0, 0, 0, 0, new Size(screenSize.Width, screenSize.Height));
        }
        return target;
    }

}

}

Im trying to write an app that takes a screen shot every 40ms and saves it to disk, im getting an error in GDI

Anybody know if im being too adventurous trying to save a screen shot as jpeg every 40ms or at a rate of 25 fps?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using System.Windows.Forms;


namespace ScreenRecorder
{
    class Program
    {

    private static System.Timers.Timer screenTimer;

    private static int screenNumber;


    static void Main(string[] args)
    {
        screenTimer = new System.Timers.Timer(40);

        // Hook up the Elapsed event for the timer.
        screenTimer.Elapsed += new System.Timers.ElapsedEventHandler(screenTimer_Elapsed);

        screenTimer.Enabled = true;

        Console.Read();
    }


    static void screenTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        //get the screen
        Image myImage = CaptureScreen();
        myImage.Save(@"C:\stuff\Development\ScreenRecorder\ScreenImages\img" + screenNumber + ".jpg");
        myImage.Dispose();

        screenNumber++;
    }

    private static Image CaptureScreen()
    {
        Rectangle screenSize = Screen.PrimaryScreen.Bounds;
        Bitmap target = new Bitmap(screenSize.Width, screenSize.Height);
        using (Graphics g = Graphics.FromImage(target))
        {
            g.CopyFromScreen(0, 0, 0, 0, new Size(screenSize.Width, screenSize.Height));
        }
        return target;
    }

}

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

柠栀 2024-10-11 21:57:17

GDI+ 不可重入,也不可多线程(什么意思)。您可能会收到(您的秘密错误),因为两个不同的线程正在竞争 GDI+ 资源。

来自 System.Timers.Timer 文档:

基于服务器的计时器设计用于多线程环境中的工作线程。服务器计时器可以在线程之间移动以处理引发的 Elapsed 事件,从而在按时引发事件方面比 Windows 计时器更准确。

使用 Forms.Timer 将捕获序列化到表单的消息队列,您将有更好的机会不破坏它。您可能会得到您想要的 - 但是 - 使用 PNG 而不是 JPG。对于“正常”形式来说,它会更有效。

多线程 + GDI+ = 大NO NO

另外 - 您不会定期获取照片 - 但您不必担心这一点,因为任何预期的变化也会从消息循环中发生 - 所以您不会错过任何一点。

GDI+ is not reentrant nor multithreadable (what-a-word). You are probably getting (your secret error) because two different threads are competing for GDI+ resources.

From System.Timers.Timer documentation:

The server-based Timer is designed for use with worker threads in a multithreaded environment. Server timers can move among threads to handle the raised Elapsed event, resulting in more accuracy than Windows timers in raising the event on time.

Use Forms.Timer to serialize your capturing to message queue of the form, you'll get much better chance of not breaking it. You might get what you want - but - use PNG instead of JPG. It will be more efficient for 'normal' forms.

Multithreading + GDI+ = big NO NO.

Also - you won't get your pictures at regular interval - but you don't have to worry about that since any change what can expected is to occur from the message loop also - so you won't miss a bit.

太阳公公是暖光 2024-10-11 21:57:17

是的,那会爆炸。计时器的 Elapsed 方法将在线程池线程上运行,无论前一个线程是否已完成。您的屏幕捕获 + 位图保存必然需要超过 40 毫秒的时间。迟早,可能更快,两个 Elapsed 处理程序将使用相同的 screenNumber 变量值同时运行。 Kaboom 无法覆盖锁定的文件。

您需要使用 System.Threading.Timer 来代替。以 0 的周期开始,以便回调仅运行一次。截图后,再次重新启动计时器。现在您可以确定永远不会有多个线程同时运行。

Yeah, that's going to blow. The timer's Elapsed method will run on a threadpool thread, regardless whether the previous one has finished. Your screen capture + bitmap save is bound to take longer than 40 milliseconds. Sooner or later, probably sooner, two Elapsed handlers are going to run concurrently, using the same screenNumber variable value. Kaboom on not being able to overwrite a locked file.

You'll need to use a System.Threading.Timer instead. Start it with a period of 0 so the callback only runs once. After the screenshot, restart the timer again. Now you can be sure that you'll never get more than one thread running at the same time.

美人迟暮 2024-10-11 21:57:17
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using System.Diagnostics;

namespace ScreenRecorder
{
    class Program
    {
        private static System.Timers.Timer screenTimer;
        private static int screenNumber;
        private static Mutex m = new Mutex();

        static void Main(string[] args)
        {
            screenTimer = new System.Timers.Timer(40);

            // Hook up the Elapsed event for the timer.
            screenTimer.Elapsed += new System.Timers.ElapsedEventHandler(screenTimer_Elapsed);
            screenTimer.Enabled = true;

            Console.Read();
        }


        static void screenTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Thread t = new Thread(()=>SaveImage());
            t.Start();
        }

        private static void SaveImage()
        {
            m.WaitOne();
            Image myImage = CaptureScreen();
            myImage.Save(@"C:\stuff\Development\ScreenRecorder\ScreenImages\img" + screenNumber + ".png");
            myImage.Dispose();
            screenNumber++;
            m.ReleaseMutex();
        }

        private static Image CaptureScreen()
        {
            Rectangle screenSize = Screen.PrimaryScreen.Bounds;
            Bitmap target = new Bitmap(screenSize.Width, screenSize.Height);
            using (Graphics g = Graphics.FromImage(target))
            {
                g.CopyFromScreen(0, 0, 0, 0, new Size(screenSize.Width, screenSize.Height));
            }
            return target;
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using System.Diagnostics;

namespace ScreenRecorder
{
    class Program
    {
        private static System.Timers.Timer screenTimer;
        private static int screenNumber;
        private static Mutex m = new Mutex();

        static void Main(string[] args)
        {
            screenTimer = new System.Timers.Timer(40);

            // Hook up the Elapsed event for the timer.
            screenTimer.Elapsed += new System.Timers.ElapsedEventHandler(screenTimer_Elapsed);
            screenTimer.Enabled = true;

            Console.Read();
        }


        static void screenTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Thread t = new Thread(()=>SaveImage());
            t.Start();
        }

        private static void SaveImage()
        {
            m.WaitOne();
            Image myImage = CaptureScreen();
            myImage.Save(@"C:\stuff\Development\ScreenRecorder\ScreenImages\img" + screenNumber + ".png");
            myImage.Dispose();
            screenNumber++;
            m.ReleaseMutex();
        }

        private static Image CaptureScreen()
        {
            Rectangle screenSize = Screen.PrimaryScreen.Bounds;
            Bitmap target = new Bitmap(screenSize.Width, screenSize.Height);
            using (Graphics g = Graphics.FromImage(target))
            {
                g.CopyFromScreen(0, 0, 0, 0, new Size(screenSize.Width, screenSize.Height));
            }
            return target;
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文