Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

File being used by another process using StreamWriter

My program was practice for me, however, when I try to write all the directories it found, it crashes.

I tried the following:

  • Having it write to a file stream instead of the file itself
  • using File.Writealllines using a list<> (this worked, only it did the first five and no more)
  • FileStream.Write(subdir.ToCharArray())

I cannot see why this wouldn't work, what have I done wrong?

static void Main(string[] args)
{
    Method(@"C:\");
}

static void Method(string dir)
{
    //crash happens here v
    StreamWriter sw = new StreamWriter(@"C:\users\"+Environment.UserName+"\desktop\log.txt",true);

    foreach (string subdir in Directory.GetDirectories(dir))
    {
        try
        {
            Console.WriteLine(subdir);
            sw.Write(subdir);
            Method(subdir);
        }
        catch (UnauthorizedAccessException)
        {
            Console.WriteLine("Error");
        }
    }
  sw.Close(); 
}
like image 768
Chris Altig Avatar asked Jan 11 '23 21:01

Chris Altig


2 Answers

Its recursive.

Because you're calling Method again here:

Console.WriteLine(subdir);
sw.Write(subdir);
Method(subdir); // BOOM

Your file is already open. You can't open it for writing again.

Open the file in Main once..

static void Main(string[] args) {
    using (StreamWriter sw = new StreamWriter(@"C:\users\"+Environment.UserName+"\desktop\log.txt",true)) {
        Method(@"C:\", sw);
    }
}

Then accept it in your method:

public static void Method(string dir, StreamWriter sw) {

Then when you call it again:

sw.Write(subdir);
Method(subdir, sw); // pass in the streamwriter.

Note though, that you will quickly start chewing up memory. You're recursing through your entire C:\ drive. Maybe test it on a smaller folder?

like image 149
Simon Whitehead Avatar answered Jan 14 '23 11:01

Simon Whitehead


I am agree with above but in my case solution different a little.

    private static object locker = new object();

    private static void WriteMessageToFile(string message)
    {
        string dateStr = DateTime.Now.Date.Day.ToString()+"_"+ DateTime.Now.Date.Month.ToString()+"_"+ DateTime.Now.Date.Year.ToString();
        if (!Directory.Exists("Logs"))
        {
            DirectoryInfo di = Directory.CreateDirectory("Logs");
        }

        //Guid guidGenerator = Guid.NewGuid();
        string filePath = _env.ContentRootPath + "\\Logs\\ProcessLog_" + dateStr + ".txt";
        FileInfo fi = new FileInfo(filePath);

        lock (locker)
        {
            using (FileStream file = new FileStream(fi.FullName, FileMode.Append, FileAccess.Write, FileShare.Read))
            using (StreamWriter streamWriter = new StreamWriter(file))
            {
                streamWriter.WriteLine(message);
                streamWriter.Close();
            }
        }

    }

Because the following function is called asynchronous and asynchronous in many places in my asp.net core application. In this case, one thread was trying to write a file, another thread wanted to write the same file, and there was an error. As a solution, I tried the above, but it didn't work either because I tried to open a new stream before closing the previous stream. So I decided to write a secure block of code as a solution. In this case, since the other threads could not reach the locked area, they made the write operation by waiting for the previous operation and I was able to write the file without error.

I think; there is another reason code behind, cause i have used Singleton registration on startup. This function's caller classes are isolated from each other. with this reason they didn't know which thread is called the function before. Their lifetime has been finished while. Also FileStream wraps the StreamWriter then it also may work without lock, anyway it is guaranty.

Even Microsoft.Extensions.Logging does not support FileLoger by default, but we can write custom. I share the entire implementation below

public class FileLoger : ILogger
    {
        public static IHostingEnvironment _env;
        private static object locker = new object();

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            var message = string.Format("{0}: {1} - {2}", logLevel.ToString(), eventId.Id, formatter(state, exception));
            WriteMessageToFile(message);
        }
        private static void WriteMessageToFile(string message)
        {
            string dateStr = DateTime.Now.Date.Day.ToString()+"_"+ DateTime.Now.Date.Month.ToString()+"_"+ DateTime.Now.Date.Year.ToString();
            if (!Directory.Exists("Logs"))
            {
                DirectoryInfo di = Directory.CreateDirectory("Logs");
            }

            //Guid guidGenerator = Guid.NewGuid();
            string filePath = _env.ContentRootPath + "\\Logs\\ProcessLog_" + dateStr + ".txt";
            FileInfo fi = new FileInfo(filePath);

            lock (locker)
            {
                using (FileStream file = new FileStream(fi.FullName, FileMode.Append, FileAccess.Write, FileShare.Read))
                using (StreamWriter streamWriter = new StreamWriter(file))
                {
                    streamWriter.WriteLine(message);
                    streamWriter.Close();
                }
            }

        }
        public IDisposable BeginScope<TState>(TState state)
        {
            return null;
        }
        public bool IsEnabled(LogLevel logLevel)
        {
            return true;
        }
    }

    public class FileLogProvider : ILoggerProvider
    {

        public FileLogProvider(IHostingEnvironment env)
        {
            FileLoger._env = env;
        }

        public ILogger CreateLogger(string category)
        {
            return new FileLoger();
        }
        public void Dispose()
        {

        }
    }
like image 26
Hamit YILDIRIM Avatar answered Jan 14 '23 11:01

Hamit YILDIRIM