FileSystemWatcher on separate thread - c#

I have to create script which will push some files into provided location and grab the output generated by other script.
Copy files to location.
Other script will grab the files and process them.
Wait for all results.
Get data from files, remove files.
Repeat steps for next stack of files.
On my PC everything works fine but when I run my script on the machine where Windows Server 2016 is running some error pop-up:
So I created a SystemWatcher which is monitoring a provided location
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
private void RunWatcher(string location)
{
// Create a new FileSystemWatcher and set its properties.
using (FileSystemWatcher watcher = new FileSystemWatcher())
{
watcher.Path = location;
// Watch for changes in LastAccess and LastWrite times, and
// the renaming of files or directories.
watcher.NotifyFilter = NotifyFilters.LastAccess
| NotifyFilters.LastWrite
| NotifyFilters.FileName
| NotifyFilters.DirectoryName;
watcher.Filter = "*";
// Add event handlers.
watcher.Created += OnCreate;
// Begin watching.
watcher.EnableRaisingEvents = true;
// quit the program.
while (!endWatcher);
}
}
private void OnCreate(object source, FileSystemEventArgs e)
{
string fileExtension = Path.GetExtension(e.FullPath);
if (extensionWatch.Contains(fileExtension))
{
SaveFileData(fileExtension, e.FullPath);
}
}
The watcher is created on separate thread. Then a "while" loop inside the "for" is checking if all files has been created or if the "Abandon time" has been exceeded.
private void PushFiles(string destinationPath, int timeAbandon, BackgroundWorker worker)
{
Console.WriteLine("Before start watcher");
Task.Run(() =>
{
RunWatcher(destinationPath);
});
Console.WriteLine("After start watcher");
timeAbandon *= 60; // to sec
for (int i = 0; i < fileCollect.Count; i++)
{
double timeDiff;
start = DateTime.UtcNow;
currentListIdx = i;
CopyPtx(fileCollect[i], destinationPath);
while (!CheckIfDone(i))
{
timeDiff = (DateTime.UtcNow - start).TotalSeconds;
if (timeDiff > timeAbandon)
{
break;
}
Thread.Sleep(1000);
Console.WriteLine("Waiting for results...");
}
CopyAndRemoveFiles(fileCollect[i], destinationPath);
worker.ReportProgress(1);
}
endWatcher = true;
}
private bool CheckIfDone(int stackId)
{
bool isDone = true;
foreach (var file in fileCollect[stackId])
{
if (file["finished"] == "false")
{
isDone = false;
}
}
if (isDone)
{
return true;
}
return false;
}
I did have a look on Just-In-Time settings and "Managed, Native, Script" are selected. Also I use .net Core as I can't install any dependencies on the server machine.
To run the script on the server I do publish the script first with a following settings:
I tried to change the Configuration setting to "Debug|AnyCPU" but it didn't help.
Can anyone help me to get more information about the error? Or possible solution to my problem?

There was en error in function SaveFileData(fileExtension, e.FullPath);. The error appeard because I was trying to read file before saving into file was finished. So The title of this post is not correct.
To solve the problem with access to file I used a following code.
protected virtual bool IgnoreErrorMsg(FileInfo file, string ignoreMsg)
{
bool isAccess = false;
while (!isAccess)
{
try
{
using (FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None))
{
//if file can be read, then read it
if (stream.CanRead)
{
byte[] buffer = new byte[stream.Length];
int bytesRead = stream.Read(buffer, 0, buffer.Length);
string errorString = Encoding.ASCII.GetString(buffer, 0, bytesRead);
if (errorString.Contains(ignoreMsg))
{
return true;
}
// access has been granted so loop can be exit
isAccess = true;
}
stream.Close();
}
}
catch (IOException e)
{
//the loop will continue as long as "cannot access" error appear
isAccess = false;
}
Thread.Sleep(500);
}
//if return false, then script will wait for optimisation unitil AbandonTime
return false;
}
In that case the title of the post is not correct because it's not related to multithreading.

Related

Optimizer won't stop after end new files found by FSW

I have created a CLI Application that will watch over a directory and optimize any new PDF's that are moved into it. There are no errors as of my last build.
The issue I'm having is when I run it the application will detect a change and optimize the changed files, but it doesn't stop the cycle of optimizing the new files.
How would I set a stopping point in the optimization process once it reaches the end of the new files?
public class Methods
{
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public static void Optimize()
{
Thread.Sleep(1000);
PDFNet.Initialize();
string input_Path = #"C:\Users\user\Desktop\testinpactive\";
string output_Path = #"C:\Users\user\Desktop\output\";
string[] files = Directory.GetFiles(input_Path, "*.pdf", SearchOption.AllDirectories);
foreach (string file in files)
{
string fileName = Path.GetFileName(file);
Console.WriteLine($"Optimizing {fileName}");
string sub = file.Substring(41, 7);
CreateFolder(output_Path + sub);
// CreateFolder(output_Path + );
try
{
using (PDFDoc doc = new PDFDoc(file))
{
//--------------------------------------------------------------------------------
// Example 1) Simple optimization of a pdf with default settings.
doc.InitSecurityHandler();
Optimizer.Optimize(doc);
doc.Save(output_Path + sub + fileName, SDFDoc.SaveOptions.e_linearized);
// File Delete Process
//File.Delete(input_Path + files);
//Console.WriteLine("File Deleted");
Console.WriteLine("Done..\n");
}
}
catch (PDFNetException e)
{
Console.WriteLine(e.Message);
}
}
}
public static void Run()
{
// Create a new FileSystemWatcher and set its properties.
// Params: Path, and filter
using (FileSystemWatcher watcher = new FileSystemWatcher(#"C:\Users\user\Desktop\testinpactive", "*.pdf"))
{
// To watch SubDirectories
watcher.IncludeSubdirectories = true;
FswHandler Handler = new FswHandler();
// Add event handlers.
watcher.Created += Handler.OnCreated;
// Begin watching.
watcher.EnableRaisingEvents = true;
// Wait for the user to quit the program.
Console.WriteLine("Press 'q' to quit the sample.");
while (Console.Read() != 'q');
}
}
public class FswHandler
{
public void OnCreated(Object source, FileSystemEventArgs e)
{
// Write out Path (Testing)
Console.WriteLine($"FILE: {e.FullPath} CHANGE-TYPE: {e.ChangeType}");
// Specify what is done when a file is changed, created, or deleted.
Optimize();
}
}
A big thanks to #BenVoigt for explaining what I was doing wrong.
Here are the changes I made to my code to fix the issue.
public static void Optimize(string filePath, string outputPath, string fileName, string fileNum)
{
PDFNet.Initialize();
try
{
using (PDFDoc doc = new PDFDoc(filePath))
{
//--------------------------------------------------------------------------------
// Example 1) Simple optimization of a pdf with default settings.
doc.InitSecurityHandler();
Optimizer.Optimize(doc);
Directory.CreateDirectory(outputPath + filePath.Substring(41, 7));
doc.Save(filePath, SDFDoc.SaveOptions.e_linearized);
//doc.Save(outputPath + fileNum + fileName, SDFDoc.SaveOptions.e_linearized);
Console.WriteLine("Done..\n");
}
}
catch (PDFNetException e)
{
Console.WriteLine(e.Message);
}
}
public static void Run()
{
// Create a new FileSystemWatcher and set its properties.
// Params: Path, and filter
using (FileSystemWatcher watcher = new FileSystemWatcher(#"C:\Users\user\Desktop\testinpactive", "*.pdf"))
{
// To watch SubDirectories
watcher.IncludeSubdirectories = true;
FswHandler Handler = new FswHandler();
// Add event handlers.
watcher.Created += Handler.OnCreated;
// Begin watching.
watcher.EnableRaisingEvents = true;
// Wait for the user to quit the program.
Console.WriteLine("Press 'q' to quit the sample.");
while (Console.Read() != 'q');
}
}
public class FswHandler
{
public void OnCreated(Object source, FileSystemEventArgs e)
{
string output = #"C:\Users\user\Desktop\output";
// Write out Path (Testing)
Console.WriteLine($"FILE: {e.FullPath} CHANGE-TYPE: {e.ChangeType}");
Thread.Sleep(800);
// Specify what is done when a file is changed, created, or deleted.
Optimize(e.FullPath, output, e.Name.Substring(7), e.FullPath.Substring(40, 8));
}
}

Read text file line by line using timer

StreamReader sr = new StreamReader("C:/CR EZ Test/Log.txt"); //use with IF
private void timer2_Tick(object sender, EventArgs e)
{
if ((line = sr.ReadLine()) != null)
{
//FileStream fs = File.Open("C:/CR EZ Test/Log.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
//StreamReader sr = new StreamReader(fs); //use with While can't use with }else{
//while ((line = sr.ReadLine()) != null)
//{
string[] dataLog = line.Split(new[] { ',' }, StringSplitOptions.None);
mpa = (dataLog[1]);
ml = (dataLog[2]);
lph = (dataLog[3]);
elapsedTime = float.Parse(dataLog[4]) / 1000;
if (testStatus > 0) time = elapsedTime.ToString("0.0");
tb2.Value = int.Parse(dataLog[6]);
if (chart1.Series[0].Points.Count > tb1.Value && tb1.Value > 0)
{
chart1.Series[0].Points.RemoveAt(0);
chart1.Series[1].Points.RemoveAt(0);
}
chart1.Series[0].Points.AddXY(dataLog[5], int.Parse(dataLog[1]));
chart1.Series[1].Points.AddXY(dataLog[5], int.Parse(dataLog[6]));
//}
}
else
{
sr.DiscardBufferedData();
sr.BaseStream.Seek(0, SeekOrigin.Begin);
sr.BaseStream.Position = 0;
//sr.Close();
//alertTB.Text = "";
timer2.Enabled = false;
}
alertTB.ForeColor = Color.Red;
alertTB.Text = "Data Log Viewing In Progress";
}
The issue is I am reading a text file full of variables back through a GUI, like replaying a video. As the code is shown, it works and I can control the timer tick to change the replay speed. The issue is the file is in use, so I can't write to or delete the text while the file is in use, without closing it first. I would like to either be able to find a workaround of the Streamreader, or use the Filestream to Streamreader code that will allow me to edit the file while it is in use. The issue there is, I can't figure out how to make it work with the timer, it just reads the entire file very quickly. Any help or ideas are greatly appreciated.
The issue here is how to have the commented out code to:
read a line of the text file,
have the timer to tick
then read the next line of the text file, and so on. Obviously handling the data as it arrives.
Opening a file while it is in use
I think what you are looking for is FileStream with FileShare.ReadWrite for the instance of your StreamReader (not the instance you have commented out),
var fs = new FileStream("C:\foo.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
var sr = new StreamReader(fs);
Setting the position of the stream
It also seems like based on your comments, you are having trouble with positioning the stream, this is how you could do that...
fs.Position = 0; // note this is the FileStream not the StreamReader!
// alternatively, you could use Seek
Difference between sequential and random access
Lastly, you might want to take a look below to see the difference between sequential and random access
A Potential Solution
Here is a class called FileMonitor that will check the file and update the list whenever the file is changed / updated.
I understand that you want a timer to poll the data in the text file, but in case the timer is very fast, I have optimized the FileMonitor to watch the file for changes and only extract when there is a change.
Please note that this only continues to read where it was left off, based on the position of the stream. So, it will not work if lines are deleted or modified prior to getting "extracted". This means it only functions based on your requirements and is not improved to handle a lot of other scenarios, but it should adequately cover your requirements.
public class FileMonitor : IDisposable
{
private readonly FileStream _file;
private readonly StreamReader _reader;
private long _position;
private List<string> _lines;
public FileMonitor(string file)
{
if (String.IsNullOrEmpty(nameof(file))) throw new ArgumentNullException(nameof(file));
_lines = new List<string>();
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = Path.GetDirectoryName(file);
watcher.Filter = Path.GetFileName(file);
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
watcher.Changed += new FileSystemEventHandler(OnChanged);
//watcher.Created += new FileSystemEventHandler(OnCreated);
//watcher.Deleted += new FileSystemEventHandler(OnDeleted);
//watcher.Renamed += new RenamedEventHandler(OnRenamed);
// begin watching.
watcher.EnableRaisingEvents = true;
// begin reading
_file = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
_reader = new StreamReader(_file);
_lines = ReadLines(_reader).ToList();
_position = _file.Position;
}
private void OnChanged(object source, FileSystemEventArgs e)
{
List<string> update = ReadLines(_reader).ToList();
// fix to remove the immidate newline
if (update.Count() > 0 && String.IsNullOrEmpty(update[0])) update.RemoveAt(0);
_lines.AddRange(update);
_position = _file.Position;
// just for debugging, you should remove this
Console.WriteLine($"File: {e.FullPath} [{e.ChangeType}]");
}
public IEnumerable<string> Lines { get { return _lines; } }
public void Reset()
{
_file.Position = 0;
_position = _file.Position;
_lines.Clear();
}
private static IEnumerable<string> ReadLines(StreamReader reader)
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
public void Dispose()
{
_reader.Dispose();
_file.Dispose();
}
}
Here is how you could use it with your timer
private IEnumerable<string> _lines; // holds all the lines "extracted"
void Main()
{
string file = #"C:\Data\foo.txt";
using (var timer = new System.Timers.Timer())
{
timer.Interval = 2000; // 2 second interval
timer.Elapsed += OnTimedEvent; // attach delegate
timer.Enabled = true; // start the timer
// open the file
using (var monitor = new FileMonitor(file))
{
_lines = monitor.Lines;
// loop forever, remove this
while (true) { }
}
}
}
public void OnTimedEvent(object sender, EventArgs e)
{
// just for debugging, you should remove this
Console.WriteLine($"current count: {_lines.Count()}");
}
If it isn't clear, the data extracted is held in a list of strings. Above, you can grab the "extracted" data from the monitor using the monitor.Line property.
A Proven Working Solution
string line;
if (!File.Exists(logFile))
{
viewLog.Text = "Play";
alertTB.ForeColor = Color.Red;
alertTB.Text = "File Does Not Exist | Log Data To Create File";
chart.Text = "Scope On";
}
if (File.Exists(logFile))
{
var lineCount = File.ReadLines(logFile).Count();//read text file line count to establish length for array
if (lineCount < 2)
{
viewLog.Text = "Play";
alertTB.ForeColor = Color.Red;
alertTB.Text = "File Exists | No Data Has Been Recorded";
chart.Text = "Scope On";
}
if (counter < lineCount && lineCount > 0)//if counter is less than lineCount keep reading lines
{
line = File.ReadAllLines(logFile).Skip(counter).Take(lineCount).First();
string[] dataLog = line.Split(new[] { ',' }, StringSplitOptions.None);
//-----------------------------------------Handling my data
counter++;
}
else
{
counter = 0;
timer2.Enabled = false;
}
}
This is the fix I arrived at, it allows editing the file or deleting the contents of the file. I get the line count before trying to load the file. I then use the counter to iterate through the lines. I can change the delay between the next line read based upon the timer tick interval, pause it, or stop it.

FileSystemWatcherChanged event dosen't fire for a memory mapped file

I have json file that I want to share between two processess. So I created a memory mapped file as follows.
private void CreateMemoryMappedFile()
{
var info = Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "/" + model.Data.Settings.OrcaUISpecificSettings.TimeOutFolder);
string path = Path.Combine(info.FullName + #"\" + model.Data.Settings.OrcaUISpecificSettings.File);
FullPath = path;
try
{
mmf = MemoryMappedFile.CreateFromFile(path, FileMode.CreateNew, "MyMemoryFile", 1024 * 1024);
}
catch (Exception ex)
{
}
}
public MemoryMappedViewStream GetAccessor()
{
MemoryMappedViewStream FileMapView = null;
if (FileMapView != null)
{
return FileMapView;
}
FileMapView = mmf.CreateViewStream();
return FileMapView;
}
And to read and write to the files I am doing the following
public void WriteToMemoryMappedFile(string Data)
{
try
{
mutex.WaitOne();
byte[] bytes = Encoding.ASCII.GetBytes(Data);
var accessor = GetAccessor();
accessor.Write(bytes, 0, Data.Length);
mutex.ReleaseMutex();
}
catch (Exception ex)
{
}
}
public string ReadFromMemoryMappedFile()
{
mutex.WaitOne();
var accessor = GetAccessor();
using (BinaryReader binReader = new BinaryReader(accessor))
{
byte[] reader = binReader.ReadBytes((int)accessor.Length);
string result = Encoding.Default.GetString(reader);
mutex.ReleaseMutex();
return result.Replace("NULL", "");
}
}
My problem is I have an Activity Monitor for my app. So after x amount of time I am updating the json file with InActiveStatus. Along the same lines I am listening to any file changes(Look at D_IDle event). Problem is If a normal file is changed I get the FileSytemWatcher changed event firing just fine. But when I use a Memory Mapped File to update the status, the FileSystemWatcher changed event never gets fired please help.
private void D_IsIdle(object sender, EventArgs e)
{
MonitorDirectory();
//AppViewModel.SerializeData("InActive");
AppViewModel.SerializeDataToMemoryMap("InActive");
d.IsIdle -= D_IsIdle;
}
public void MonitorDirectory()
{
FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(AppViewModel.GetDriectory());
fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
fileSystemWatcher.Filter = "*.json";
fileSystemWatcher.Changed += FileSystemWatcher_Changed;
fileSystemWatcher.EnableRaisingEvents = true;
}
private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
//IT NEVER COMES HERE
}
Using FileSystemWatcher with memory mapped files must be some kind of anti-pattern:). If you are on a local system, use one of the process synchronization primitives (e.g. semaphores) to signal change.
My guess is that the FileSystemWatcher triggers when the file handle is closed to avoid other processed reading partially written files.

How do i check if file is in use/busy before continue the rest of the code?

I have this part:
public string SendResponse(HttpListenerRequest request)
{
string result = "";
string key = request.QueryString.GetKey(0);
if (key == "cmd")
{
if (request.QueryString[0] == "uploadstatus")
{
switch (Youtube_Uploader.uploadstatus)
{
case "uploading file":
return "uploading " + Youtube_Uploader.fileuploadpercentages;
case "status":
return Youtube_Uploader.fileuploadpercentages.ToString();
case "file uploaded successfully":
Youtube_Uploader.uploadstatus = "";
return "upload completed," + Youtube_Uploader.fileuploadpercentages + ","
+ Youtube_Uploader.time;
default:
return "upload unknown state";
}
}
if (request.QueryString[0] == "nothing")
{
return "Connection Success";
}
if (request.QueryString[0] == "start")
{
StartRecrod();
result = "Recording started";
}
if (request.QueryString[0] == "stop")
{
dirchanged = false;
StartRecrod();
result = "Recording stopped and preparing the file to be shared on youtube";
string fileforupload = await WatchDirectory();
await WaitForUnlockedFile(fileforupload);
using (StreamWriter w = new StreamWriter(userVideosDirectory + "\\UploadedVideoFiles.txt", true))
{
w.WriteLine(fileforupload);
}
uploadedFilesList.Add(fileforupload);
Youtube_Uploader youtubeupload = new Youtube_Uploader(uploadedFilesList[0]);
}
}
else
{
result = "Nothing have been done";
}
return result;
}
Then i have the WatchDirectory method:
FileSystemWatcher watcher;
private void WatchDirectory()
{
watcher = new FileSystemWatcher();
watcher.Path = userVideosDirectory;
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size;
watcher.Filter = "*.mp4";
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
}
The OnChanged event:
private void OnChanged(object source, FileSystemEventArgs e)
{
var info = new FileInfo(e.FullPath);
fileforupload = info.FullName;
while(IsFileLocked(fileforupload) == true)
{
System.Threading.Thread.Sleep(100);
}
}
Then the IsFileLocked method:
public bool IsFileLocked(string filename)
{
bool Locked = false;
try
{
FileStream fs =
File.Open(filename, FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None);
fs.Close();
}
catch (IOException ex)
{
Locked = true;
}
return Locked;
}
Now the order should be that first it will go to the WatchDirectory and then in the while loop will check if the file is locked/busy once the file is not locked/busy any more continue with the rest of the code the StreamWriter the uploadedFilesList.Add and the Youtube_Uploader...
First i'm not sure if it's the right way to use the While loop with the 100.
And second how do i make that it will first finish the file lock checking before continue ? Now what it does is getting to the WatchDirectory then making the StreamWriter... Not the order i want it to be.
The biggest problem with your code is that it doesn't wait in any useful place, and it does wait in the last place you want:
You call WatchDirectory() and then immediately move on to creating your writer. There's nothing in the WatchDirectory() method that would delay its return, so of course you move on to the next statement before anything's happened.
In the OnChanged() method, you poll for the file lock status. But this method is an event handler for the FileSystemWatcher event, and will be called in a context where you really don't/shouldn't be delaying the thread.
I would change your code to take advantage of the async pattern, not only to fix the problems above, but also to provide asynchronous operation, i.e. to prevent this logic from holding up the rest of your program while it's waiting for something interesting to happen in the watched directory.
Here are new versions of your methods that I think would be better:
private async Task<string> WatchDirectoryAsync()
{
using (FileSystemWatcher watcher = new FileSystemWatcher())
{
TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
watcher.Path = userVideosDirectory;
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size;
watcher.Filter = "*.mp4";
watcher.Changed += (sender, e) => tcs.SetResult(e.FullPath);
watcher.EnableRaisingEvents = true;
return await tcs.Task;
}
}
// You can get rid of the OnChanged() method altogether
private async Task WaitForUnlockedFileAsync(string fileName)
{
while (true)
{
try
{
using (IDisposable stream = File.Open(fileName, FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None))
{ /* on success, immediately dispose object */ }
break;
}
catch (IOException)
{
// ignore exception
// NOTE: for best results, consider checking the hresult value of
// the exception, to ensure that you are only ignoring the access violation
// exception you're expecting, rather than other exceptions, like
// FileNotFoundException, etc. which could result in a hung process
}
// You might want to consider a longer delay...maybe on the order of
// a second or two at least.
await Task.Delay(100);
}
}
Which you can then use like this:
if (request.QueryString[0] == "stop")
{
dirchanged = false;
StartRecrod();
result = "Recording stopped and preparing the file to be shared on youtube";
string fileforupload = await WatchDirectoryAsync();
await WaitForUnlockedFileAsync(fileforupload);
using (StreamWriter w = new StreamWriter(userVideosDirectory + "\\UploadedVideoFiles.txt",true))
{
w.WriteLine(fileforupload);
}
uploadedFilesList.Add(fileforupload);
Youtube_Uploader youtubeupload = new Youtube_Uploader(uploadedFilesList[0]);
}
Naturally, to use await in the above, the code will need to be contained in an async method. Without a good, minimal, complete code example showing the entire context, it's impossible to say how exactly you'd incorporate that into the program as a whole. But there is lots of advice on Stack Overflow and elsewhere on that topic. The basic idea is that, typically, the calling methods would all have to be turned into async methods, up to the top of the stack where the call chain starts (which is often some kind of event handler, invoked when the user performs some kind of input).
In some cases, you can just call the async method and ignore the returned Task object reference (not ideal), or defer handling of the return value (better). You'll have to decide based on your own scenario what works best in your case.
EDIT:
If you cannot or will not change the original calling method to be an async method, it is possible to perform these operations synchronously. They can be implemented themselves as synchronous methods as follows:
private string WatchDirectory()
{
using (FileSystemWatcher watcher = new FileSystemWatcher())
{
TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
watcher.Path = userVideosDirectory;
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size;
watcher.Filter = "*.mp4";
watcher.Changed += (sender, e) => tcs.SetResult(e.FullPath);
watcher.EnableRaisingEvents = true;
return tcs.Task.Result;
}
}
// You can get rid of the OnChanged() method altogether
private void WaitForUnlockedFile(string fileName)
{
while (true)
{
try
{
using (IDisposable stream = File.Open(fileName, FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None))
{ /* on success, immediately dispose object */ }
break;
}
catch (IOException)
{
// ignore exception
// NOTE: for best results, consider checking the hresult value of
// the exception, to ensure that you are only ignoring the access violation
// exception you're expecting, rather than other exceptions, like
// FileNotFoundException, etc. which could result in a hung process
}
// You might want to consider a longer delay...maybe on the order of
// a second or two at least.
Thread.Sleep(100);
}
}
Or you could simply consume the asynchronously implemented operations synchronously:
if (request.QueryString[0] == "stop")
{
dirchanged = false;
StartRecrod();
result = "Recording stopped and preparing the file to be shared on youtube";
string fileforupload = WatchDirectoryAsync().Result;
WaitForUnlockedFileAsync(fileforupload).Wait();
using (StreamWriter w = new StreamWriter(userVideosDirectory + "\\UploadedVideoFiles.txt",true))
{
w.WriteLine(fileforupload);
}
uploadedFilesList.Add(fileforupload);
Youtube_Uploader youtubeupload = new Youtube_Uploader(uploadedFilesList[0]);
}
You might choose the latter if, for example, you intend to convert your other code to async eventually but for some reason just can't right now.
Note that I don't advise this sort of approach. These operations are inherently asynchronous; i.e. they depend on and wait for some external activity that is itself not happening synchronously. And so in the long run, your program will work much better if it itself is not made to stop its progress while waiting for this external operation.

File.Delete the process cannot access the file because it is being used by another process

public bool DownloadMp3File (DownloadedMp3 mp3) {
WebClient client = new WebClient ();
string filePath = "";
bool wasDownload = false;
try {
string song = mp3.SongName;
filePath = #"mp3\" + song + ".mp3";
if (File.Exists (filePath)) {
File.Delete (filePath);
}
DateTime tryCountNow = DateTime.Now;
client = new WebClient ();
client.DownloadFileAsync (new Uri (mp3.Url), filePath);
client.DownloadProgressChanged += client_DownloadProgressChanged;
client.DownloadFileCompleted += client_DownloadFileCompleted;
DateTime start = DateTime.Now;
bool notDownload = false;
downloadComplete = false;
while (!downloadComplete) {
DateTime now = DateTime.Now;
TimeSpan ts = now - start;
int min = ts.Minutes;
int sec = ts.Seconds;
if (10 < sec && 0 == downloadProgress) {
notDownload = true;
client.CancelAsync ();
break;
}
if (min == 1) {
notDownload = true;
client.CancelAsync ();
break;
}
Thread.Sleep (30);
}
if (!notDownload) {
client.CancelAsync ();
client.OpenRead (mp3.Url);
int downloadedFileSize = Convert.ToInt32 (client.ResponseHeaders["Content-Length"]);
FileInfo localFile = new FileInfo (filePath);
if (localFile.Length == downloadedFileSize) {
wasDownload = true;
}
}
}
catch {
downloadProgress = 0;
downloadComplete = false;
}
finally {
client.CancelAsync ();
client.Dispose ();
downloadComplete = false;
downloadProgress = 0;
GC.Collect ();
if (!wasDownload) {
if (File.Exists (filePath)) {
FileSecurity fs = File.GetAccessControl (filePath);
File.Delete (filePath);
}
}
Application.Current.Dispatcher.BeginInvoke (
DispatcherPriority.Background,
new Action (() =>
MainWindow.label3.Content = ""
));
}
return wasDownload;
}
Please help! I sometimes get that exception:
File.Delete the process cannot access the file because it is being used by another process
I can't find out why (I disposed WebClient).
Your code suggests you're getting the "file being used" exception on a file that was newly downloaded. Many anti-virus programs automatically scan newly created and/or newly downloaded files, and may delay closing the file handle until the scan is done.
If that is your problem, then there's nothing more you can to do close the file on time. You can either switch to a different anti-virus that doesn't keep files locked during scans, or you can implement a delay+retry loop when trying to use a file that's recently been closed.

Categories

Resources