Почему я могу получить ошибку в GDI в приложении записи экрана c #? - PullRequest
0 голосов
/ 27 ноября 2010

Я пытаюсь написать приложение, которое делает снимок экрана каждые 40 мс и сохраняет его на диск, я получаю сообщение об ошибке в GDI

Кто-нибудь знает, если я слишком предприимчив, пытаясь сохранить снимок экрана как jpeg каждые40 мс или 25 кадров в секунду?

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

}

}

Ответы [ 3 ]

2 голосов
/ 27 ноября 2010

Да, это будет удар. Метод Elapsed таймера будет запускаться в потоке потоков независимо от того, завершился ли предыдущий. Ваш снимок экрана + сохранение растрового изображения обязательно займет больше 40 миллисекунд. Рано или поздно, вероятно, рано, два обработчика Elapsed будут работать одновременно, используя одно и то же значение переменной screenNumber. Kaboom не может перезаписать заблокированный файл.

Вместо этого вам нужно использовать System.Threading.Timer. Запустите его с period of 0, чтобы обратный вызов выполнялся только один раз После скриншота перезапустите таймер снова. Теперь вы можете быть уверены, что вы никогда не запустите более одного потока одновременно.

2 голосов
/ 27 ноября 2010

GDI + не является ни реентерабельным, ни многопоточным (что за слово).Вы, вероятно, получаете (ваша секретная ошибка), потому что два разных потока конкурируют за ресурсы GDI +.

Из System.Timers.Timer документации:

Серверный таймер предназначен для использования срабочие потоки в многопоточной среде.Таймеры сервера могут перемещаться между потоками, чтобы обрабатывать возникшее событие Elapsed, что приводит к большей точности, чем таймеры Windows, для своевременного вызова события.

Используйте Forms.Timer для сериализации захвата в очередь сообщений в формевы получите гораздо больше шансов не сломать его.Вы можете получить то, что вы хотите - но - использовать PNG вместо JPG.Это будет более эффективно для «нормальных» форм.

Multithreading + GDI+ = big NO NO.

Кроме того, вы не получите свои изображения на регулярной основеинтервал - но вам не нужно беспокоиться об этом, так как любое ожидаемое изменение должно произойти и из цикла сообщений - так что вы не пропустите ни секунды.

0 голосов
/ 27 ноября 2010
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;
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...