2010-11-27 43 views
0

Im essayant d'écrire une application qui prend un écran chaque 40ms et il enregistre sur le disque, im obtenir une erreur dans GDIPourquoi suis-je susceptible d'obtenir une erreur dans GDI dans une application d'enregistrement d'écran C#?

Quelqu'un sait si im trop aventureux essayer d'enregistrer une capture d'écran comme jpeg tous les 40ms ou un taux de 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; 
    } 

} 

}

+3

est le message d'erreur un secret? =) – BeemerGuy

+0

Vous devriez écrire votre erreur pour nous sauver de la compiler nous-mêmes. – DMan

Répondre

2

GDI + est pas réentrant ni multithread (ce-un mot). Vous obtenez probablement (votre erreur secrète) parce que deux threads différents sont en concurrence pour les ressources GDI +.

De System.Timers.Timer documentation:

Le basé sur le serveur programmé est conçu pour être utilisé avec les threads de travail dans un environnement multithread. Les temporisateurs de serveur peuvent se déplacer parmi les threads pour gérer l'événement Elapsed déclenché, ce qui se traduit par une précision supérieure à celle des temporisateurs Windows pour élever l'événement à temps.

Utilisez Forms.Timer pour sérialiser votre capture à la file d'attente de messages de la forme, vous aurez beaucoup plus de chance de ne pas le casser. Vous pourriez obtenir ce que vous voulez - mais - utilisez le format PNG au lieu du format JPG. Ce sera plus efficace pour les formes "normales".

Multithreading+GDI+=big NO NO. En outre - vous n'obtiendrez pas vos images à intervalle régulier - mais vous ne devez pas vous inquiéter à ce sujet puisque n'importe quel changement ce qui peut s'attendre à se produire de la boucle de message aussi - ainsi vous ne manquerez pas un peu .

2

Oui, ça va souffler. La méthode Elapsed du temporisateur s'exécutera sur un thread threadpool, que le précédent soit terminé ou non. Votre capture d'écran + sauvegarde bitmap doit durer plus de 40 millisecondes. Tôt ou tard, probablement plus tôt, deux gestionnaires Elapsed vont s'exécuter simultanément, en utilisant la même valeur de la variable screenNumber. Kaboom sur ne pas pouvoir écraser un fichier verrouillé.

Vous devrez utiliser un System.Threading.Timer à la place. Commencez avec une période de 0 afin que le rappel ne s'exécute qu'une seule fois. Après la capture d'écran, redémarrez le chronomètre. Maintenant, vous pouvez être sûr que vous n'aurez jamais plus d'un thread en cours d'exécution en même temps.

+0

Bon point, mais peut-être qu'il * est * assez rapide? En outre, l'utilisation de Threading timer ne l'empêchera pas de tirer sur GDI à partir du thread principal et de son événement 'capture'. –

+0

@Daniel - Je suppose qu'il ne l'est pas, les captures d'écran en plein écran sont de grandes bitmaps. GDI + ne sera pas le problème, il peut être appelé à partir d'un thread de travail. Tant qu'un objet bitmap n'est "touché" que par un fil à la fois. Je ne sais pas si les encodeurs peuvent fonctionner simultanément, mais le but est de ne pas tester cela. –

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

Salut, qu'est-ce Thread t = new Thread (() => SaveImage()); signifier? – Exitos

+0

Il initialise un nouvel objet thread, les threads sont quelque chose comme des processus mais ils travaillent tous ensemble dans un seul processus ou je peux dire qu'ils sont des sous-processus,() => saveimage() est une expression lambda. il montre simplement que la fonction que le thread doit exécuter est saveimage() –