Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for file to be freed by process

How do I wait for the file to be free so that ss.Save() can overwrite it with a new one? If I run this twice close together(ish), I get a generic GDI+ error.

///<summary> /// Grabs a screen shot of the App and saves it to the C drive in jpg ///</summary> private static String GetDesktopImage(DevExpress.XtraEditors.XtraForm whichForm) {     Rectangle bounds = whichForm.Bounds;      // This solves my problem but creates a clutter issue     // var timeStamp = DateTime.Now.ToString("ddd-MMM-dd-yyyy-hh-mm-ss");     // var fileName = "C:\\HelpMe" + timeStamp + ".jpg";      var fileName = "C:\\HelpMe.jpg";     File.Create(fileName);     using (Bitmap ss = new Bitmap(bounds.Width, bounds.Height))     using (Graphics g = Graphics.FromImage(ss))     {         g.CopyFromScreen(whichForm.Location, Point.Empty, bounds.Size);         ss.Save(fileName, ImageFormat.Jpeg);     }      return fileName; } 
like image 269
Refracted Paladin Avatar asked Sep 10 '09 18:09

Refracted Paladin


2 Answers

If you check access before writing to the file some other process might snatch the access again before you manage to do your write. Therefor I would suggest one of the following two:

  1. Wrap what you want to do in a retry scope that won't hide any other error
  2. Create a wrapper method that waits until you can get a stream and use that stream

getting a stream

private FileStream GetWriteStream(string path, int timeoutMs) {     var time = Stopwatch.StartNew();     while (time.ElapsedMilliseconds < timeoutMs)     {         try         {             return new FileStream(path, FileMode.Create, FileAccess.Write);         }         catch (IOException e)         {             // access error             if (e.HResult != -2147024864)                 throw;         }     }      throw new TimeoutException($"Failed to get a write handle to {path} within {timeoutMs}ms."); } 

then use it like this:

using (var stream = GetWriteStream("path")) {     using (var writer = new StreamWriter(stream))         writer.Write("test"); } 

retry scope

private void WithRetry(Action action, int timeoutMs = 1000) {     var time = Stopwatch.StartNew();     while(time.ElapsedMilliseconds < timeoutMs)     {         try         {             action();             return;         }         catch (IOException e)         {             // access error             if (e.HResult != -2147024864)                 throw;         }     }     throw new Exception("Failed perform action within allotted time."); } 

and then use WithRetry(() => File.WriteAllText(Path.Combine(_directory, name), contents));

like image 28
Almund Avatar answered Sep 22 '22 07:09

Almund


A function like this will do it:

public static bool IsFileReady(string filename) {     // If the file can be opened for exclusive access it means that the file     // is no longer locked by another process.     try     {         using (FileStream inputStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.None))             return inputStream.Length > 0;     }     catch (Exception)     {         return false;     } } 

Stick it in a while loop and you have something which will block until the file is accessible:

public static void WaitForFile(string filename) {     //This will lock the execution until the file is ready     //TODO: Add some logic to make it async and cancelable     while (!IsFileReady(filename)) { } } 
like image 106
Gordon Thompson Avatar answered Sep 22 '22 07:09

Gordon Thompson