为什么我可能会在 ac# 屏幕录制应用程序中的 GDI 中遇到错误?
我正在尝试编写一个应用程序,每 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
GDI+ 不可重入,也不可多线程(什么意思)。您可能会收到(您的秘密错误),因为两个不同的线程正在竞争 GDI+ 资源。
来自 System.Timers.Timer 文档:
使用
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: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.
是的,那会爆炸。计时器的 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.