Reputation: 653
I have a solution with two projects. In a library project i added a public static bool variable and i set it to true. Then in the windows forms project i'm using the flag. In the windows forms project designer i added a timer set it's interval to 1000.
I'm starting the timer in the constructor. And then in the timer tick even i'm doing:
private void timer1_Tick(object sender, EventArgs e)
{
if (SDKHandler.Saved == true)
{
timer1.Stop();
DisplayLastTakenPhoto();
TakePhotoButton.Enabled = true;
SDKHandler.Saved = false;
timer1.Start();
}
}
And the DisplayLastTakenPhoto() method
private void DisplayLastTakenPhoto()
{
string mypath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "RemotePhoto");
var directory = new DirectoryInfo(mypath);
var myFile = directory.EnumerateFiles()
.Where(f => f.Extension.Equals(".jpg", StringComparison.CurrentCultureIgnoreCase) || f.Extension.Equals("raw", StringComparison.CurrentCultureIgnoreCase))
.OrderByDescending(f => f.LastWriteTime)
.First();
if (WaitForFile(myFile.FullName) == true) LiveViewPicBox.Load(myFile.FullName);
}
And the WaitForFile method
bool WaitForFile(string fullPath)
{
int numTries = 0;
while (true)
{
++numTries;
try
{
using (FileStream fs = new FileStream(fullPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None, 100))
{
fs.ReadByte();
break;
}
}
catch (Exception ex)
{
if (numTries > 10)
{
return false;
}
System.Threading.Thread.Sleep(500);
}
}
return true;
}
Somtimes not all the time but in some cases when it's calling the method DisplayLastTakenPhoto(); twice in a row. Even if i stopped the timer first i'm doing timer1.Stop(); but still in some cases i see the method called twice.
And on the second time it's making the program hang/freeze sometimes even for a 1-3 seconds.
Upvotes: 4
Views: 1864
Reputation: 941257
What you see is entirely normal. That one second is over really quickly, especially so when you debug. And then your code will try to load the same image file again. Except that this second time your WaitForFile() will fail and hang your UI for five seconds. Because the file is locked, PictureBox.Load() puts a lock on the image file. Produced by a memory-mapped file used by the Image class, a very efficient way to keep the pixel data of an image out of the paging file. But rather notorious for producing hard-to-diagnose image file manipulation failures :)
You'll need to improve your code, don't try to load the same file twice. Simply done by using a variable that stores the last-used path. And keep the timeout more modest, 5 seconds is rather too long to hang the UI, up to a second is reasonable. Also note that you don't need a timeout at all. Since the Timer ensures that you'll try again after a second.
The file locking is easy to avoid btw, the Bitmap(Image) constructor can get that job done, it makes a deep copy and allows you to dispose the source Image. But not the correct way to solve the problem in this case.
You can further improve it by using FileSystemWatcher instead of polling with a timer. You'll however encounter the exact same locking problem again, the file is very rarely accessible at the exact moment an FSW event fires since whatever process is writing the file still hasn't closed it. That can all be worked around, as long as you know that this can happen.
Upvotes: 2
Reputation: 2963
Check remarks and notes in MSDN documentation. Most likely this causes the behavior you observe:
Calling Start after you have disabled a Timer by calling Stop will cause the Timer to restart the interrupted interval. If your Timer is set for a 5000-millisecond interval, and you call Stop at around 3000 milliseconds, calling Start will cause the Timer to wait 5000 milliseconds before raising the Tick event.
Calling Stop on any Timer within a Windows Forms application can cause messages from other Timer components in the application to be processed immediately, because all Timer components operate on the main application thread. If you have two Timer components, one set to 700 milliseconds and one set to 500 milliseconds, and you call Stop on the first Timer, your application may receive an event callback for the second component first.
You can also hit pause in debugger when your application temporary freezes to check what exactly happening.
Upvotes: 2