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+
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)) { }
}
The problem is that your code is already opening the file by calling File.Create
, which returns an open file stream. Depending on timing, the garbage collector may have noticed that the returned stream is unused and put it on the finalizer queue, and then the finalizer thread may have cleaned things up up already before you start writing to the file again. But this is not guarantueed, as you noticed.
To fix it, you can either close the file again immediately like File.Create(...).Dispose()
. Alternatively, wrap the stream in a using statement, and write to it.
using (FileStream stream = 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(stream, ImageFormat.Jpeg);
}
You could use a lock statement with a Dummy variable, and it seems to work great.
Check here.
There is no function out there which will allow you to wait on a particular handle / file system location to be available for writing. Sadly, all you can do is poll the handle for writing.
Using @Gordon Thompson 's answer, you have to create a loop such as the code below:
public static bool IsFileReady(string sFilename)
{
try
{
using (FileStream inputStream = File.Open(sFilename, FileMode.Open, FileAccess.Read, FileShare.None))
return inputStream.Length > 0;
}
catch (Exception)
{
return false;
}
}
while (!IsFileReady(yourFileName)) ;
I found an optimized way that doesn't cause CPU overhead:
public static bool IsFileReady(this string sFilename)
{
try
{
using (FileStream inputStream = File.Open(sFilename, FileMode.Open, FileAccess.Read, FileShare.None))
return inputStream.Length > 0;
}
catch (Exception)
{
return false;
}
}
SpinWait.SpinUntil(yourFileName.IsFileReady);
One practice I use is to write a specific word at the end of the string on the file. Type "Exit". Then checking if the string read ends with the word "Exit" means that the file has been read completely.