Share via


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

Question

Sunday, November 1, 2015 12:01 AM

I still can't figure out how to do it.

I have this part:

if (request.QueryString[0] == "stop")
{
    dirchanged = false;
    StartRecrod();
    result = "Recording stopped and preparing the file to be shared on youtube";
    WatchDirectory();
    StreamWriter w = new StreamWriter(userVideosDirectory + "\\UploadedVideoFiles.txt",true);
    w.WriteLine(fileforupload);
    w.Close();                    
    uploadedFilesList.Add(fileforupload);                   
    Youtube_Uploader youtubeupload = new Youtube_Uploader(uploadedFilesList[0]);
}

Then 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;
}

Then 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);
    }
}

And 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.

I was advice to this solution tip:

" The answer is more complicated than I want to write up on a Saturday morning, sick and tired. I will say that you need to know these things: WatchDirectory returns immediately, it doesn't wait for the events. TheOnChanged event is raised on a separate thread. You should use a BackgroundWorker andManualResetEvents to signal when files have changed or become unlocked. Don't poll for lock status; either use a timer or see if FileSystemWatcher can do it. "

But i'm not sure how to do it.

All replies (5)

Tuesday, November 3, 2015 12:32 PM ✅Answered

Guys.... I really don't see, why you won't tell him about this:

Write a function that returns true or false and has a out argument of type FileStream.

You never check if a file is open or whatever ( like Wyck and Christopher said ) cause the file may have changed when your method returns with true or false. That's why you should use a out FileStream as an argument:

bool TryOpen(string path, out FileStream fs)
{
    try
    {    
        fs = File.Open(path);
        return true;
    }
    catch(Exception ex)
    {
        // ^ bad practice but just for show!!!
        return false;
    }
}

this Method can also be used in a loop (ofc):

FileStream OpenASAP(string path, int maxTries = 3)
{
    FileStream fs = null;
    bool lastResult = false;
    int count = 0;
    while((lastResult = !TryOpen(path, fs)) && count < maxTries)
    {
        Thread.Sleep(100);
        count++;
    }
    if(!lastResult || count >= maxTries)
    {
        // you could throw an exception here or just leave it
    }
    return fs;
}

Sunday, November 1, 2015 1:11 AM

I don't like the function "IsFileLocked" because of the race condition of it and the temptation to misinterpret the fact that an open succeeded has any bearing on the state of the file after you close it.

If you intend to do anything with that file, then you should just attempt to open the file and if the operation is not permitted then it will fail.  The important thing about this is that if the operation succeeds, then you will retain the access that you requested.  The test for access and granting of the access are performed atomically when you open the file.  But if you simply test the file, by the time it gives you an answer, the state of the file may have changed.  This should be evident in your code where you open the file and then close it again.  This is a logical programming flaw called a race condition.

On the other hand, if you are just waiting for an operation to complete, then your IsFileLocked function is basically right, except for two things.  It should probably only test Open, not OpenOrCreate.  And the function should definitely be renamed,  because semantically speaking, you only know if the file is locked during the test.  Something external could lock the file after IsFileLocked has returned.  So it does not represent the current state of the file.  After the test is complete, (after the function returns) you don't know if the file is currently locked or not.  Suppose IsFileLocked returned true -- does that mean the file is now locked?  You don't know.  Or suppose IsFileLocked returned false.   Does that mean the file is now unlocked?  You don't know (could be externally locked again).

In your case, you are waiting for the file to reach an unlocked state and you presume a lot about what's already going on with the file.  You really have to document somewhere the fact that you are going to try to open the file and the conclusion that you are going to draw from the fact that it succeeds -- you are going to conclude that any pending operations (including that uploader thing) have completed.

The same thing goes for functions like "FileExists"  You don't know if the file exists after the call (even if it returns false, something may have just created it, or if it returns true, something may have just deleted it).

Anyway, I feel like I'm harping a bit.  But I just really don't want anyone to borrow your IsFileLocked function and use it to somehow think that they have determined the state of the file.  A better name would be one that describes what the function does, and does not draw any conclusions with the word "Is".  Something like "TestFileOpen" would be better.  It does just that, and doesn't come with a guarantee about whether the file is currently locked, only whether or not the test of Opening and closing the file succeeded.  Let the code that calls it embody the logical conclusions to be drawn from the results of the test.  Those conclusions rely heavily on external knowledge of the the file and what's the system is doing with it.


Sunday, November 1, 2015 1:21 AM

I told you a few times by now:
You can't reliably check if a file is not in use.
Because between you noticing it is not in use and you trying to use it, something else might have opened it. Inlcuding your Virus scanner, the Windows file indexing service or any programm that might not yet have been written.
The only way to know if a file is in use, is to try to use it. You either get a error or exlusive access.

You are trying to avoid a certain state (file in use), but all your are going to do is run into a race condition with near certainty.

The only thing you can do is try to use the file. Catch the expected exceptions (and only those!). And just loop that code until you have success or a certain time of failed tries has passed (a timeout).


Sunday, November 1, 2015 2:31 PM

This is what i tried to do but i'm getting error.

I changed the method SendResponse that have the if (request.QueryString[0] == "stop") inside.

So now the method is like this i changed it's type to async Task<string>

public async Task<string> SendResponseAsync(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 changed the wAtchDirectory method to this:

private async Task<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 await tcs.Task;
            }
        }
private async Task 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.
                await Task.Delay(100);
            }
        }

I removed the OnChanged event and the the IsFileLocked method.

Now i'm getting error in the constructor on the line that make instance for the Web Server:

WebServer ws = new WebServer(SendResponseAsync, "http://+:8098/");

The error is on the SendResponseAsync 

Error 'System.Threading.Tasks.Task<string> Automatic_Record.Form1.SendResponseAsync(System.Net.HttpListenerRequest)' has the wrong return type

 

This is the WEbServer class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Threading;

namespace Automatic_Record
{
    class WebServer
    {
        private readonly HttpListener _listener = new HttpListener();
        private readonly Func<HttpListenerRequest, string> _responderMethod;

        public WebServer(string[] prefixes, Func<HttpListenerRequest, string> method)
        {
            if (!HttpListener.IsSupported)
                throw new NotSupportedException(
                    "Needs Windows XP SP2, Server 2003 or later.");

            // URI prefixes are required, for example 
            // "http://localhost:8080/index/".
            if (prefixes == null || prefixes.Length == 0)
                throw new ArgumentException("prefixes");

            // A responder method is required
            if (method == null)
                throw new ArgumentException("method");

            foreach (string s in prefixes)
                _listener.Prefixes.Add(s);

            _responderMethod = method;
            _listener.Start();
        }

        public WebServer(Func<HttpListenerRequest, string> method, params string[] prefixes)
            : this(prefixes, method) { }

        public void Run()
        {
            ThreadPool.QueueUserWorkItem((o) =>
            {
                Console.WriteLine("Webserver running...");
                try
                {
                    while (_listener.IsListening)
                    {
                        ThreadPool.QueueUserWorkItem((c) =>
                        {
                            var ctx = c as HttpListenerContext;
                            try
                            {
                                string rstr = _responderMethod(ctx.Request);
                                System.Diagnostics.Trace.Write(ctx.Request.QueryString);
                                //ctx.Request.QueryString

                                byte[] buf = Encoding.UTF8.GetBytes(rstr);
                                ctx.Response.ContentLength64 = buf.Length;
                                ctx.Response.OutputStream.Write(buf, 0, buf.Length);
                                System.Data.SqlClient.SqlConnectionStringBuilder builder = new System.Data.SqlClient.SqlConnectionStringBuilder();

                            }
                            catch { } // suppress any exceptions
                            finally
                            {
                                // always close the stream
                                ctx.Response.OutputStream.Close();
                            }
                        }, _listener.GetContext());
                    }
                }
                catch { } // suppress any exceptions
            });
        }

        public void Stop()
        {
            _listener.Stop();
            _listener.Close();
        }
    }
}

How can i fix this error ? 


Tuesday, November 3, 2015 5:53 AM

Hi Chocolade1972,

>>The error is on the SendResponseAsync 

Error 'System.Threading.Tasks.Task<string> Automatic_Record.Form1.SendResponseAsync(System.Net.HttpListenerRequest)' has the wrong return type

Just based on this error, you cannot use a method that return Task<string> since there is no overloaded method like this.

Here I made a sample as below, using a method like SendResponse will fix your error.

 class Program
    {
        static void Main(string[] args)
        {

            WebServer ws = new WebServer(Program.SendResponse, "http://+:8098/");
        }

        public static string SendResponse(HttpListenerRequest request)
        {
            return "This is the string you're looking for.";
        }
    }

Best regards,

Kristin

We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
Click HERE to participate the survey.