Avoid Access Denied when writing to file

661 views Asked by At

I am logging to file with the following method

public static void LogDataContractToFile(string XMLStringToLog, string filePathAndName)
{
    FileInfo fileinfoMaster;
    FileInfo fileinfoLog;
    string fileName = string.Empty;
    int tmpInt = 0;
    filePathAndName = filePathAndName.ToLower();

    while (true)
    {
        lock (LogDataContractToFileLock)
        {
            if (!_workingWithFiles.Contains(filePathAndName))
            {
                _workingWithFiles.Add(filePathAndName);
                break;
            }
        }
        Thread.Sleep(100);
    }

    try
    {
        #region Create XMLFile

        if ((tmpInt = filePathAndName.LastIndexOf('.')) > 0)
            fileName = filePathAndName.Remove(tmpInt, filePathAndName.Length - tmpInt);
        else
            fileName = filePathAndName;

        fileinfoMaster = new FileInfo(fileName + ".xml");
        fileinfoLog = new FileInfo(fileinfoMaster.DirectoryName + "\\" + Path.GetFileNameWithoutExtension(fileinfoMaster.Name) + ".log");

        if ((fileinfoMaster.Exists && !fileinfoLog.Exists) ||
            (!fileinfoMaster.Exists && fileinfoLog.Exists))
        {
            fileinfoMaster.Delete();
            fileinfoLog.Delete();
        }

        //Se så att filen är 50 MB eller mindre annars arkivera
        if (fileinfoMaster.Exists && fileinfoLog.Length > 52428800)
        {
            tmpInt = FileWriter.FileCount(fileinfoLog.DirectoryName, Path.GetFileNameWithoutExtension(fileinfoMaster.Name) + "*.log");
            fileinfoLog.MoveTo(Path.Combine(fileinfoLog.DirectoryName, Path.GetFileNameWithoutExtension(fileinfoLog.Name) + "_" + tmpInt.ToString() + ".log"));
            CreateLogDataMasterFile(Path.Combine(fileinfoMaster.DirectoryName, Path.GetFileNameWithoutExtension(fileinfoMaster.Name) + "_" + tmpInt.ToString() + ".xml"), fileinfoLog.Name);
            fileinfoLog = new FileInfo(fileinfoMaster.DirectoryName + "\\" + Path.GetFileNameWithoutExtension(fileinfoMaster.Name) + ".log");
        }

        if (!fileinfoMaster.Exists)
        {
            DirectoryInfo info = new DirectoryInfo(fileinfoMaster.DirectoryName);
            if (info.Exists == false)
                info.Create();

            CreateLogDataMasterFile(fileinfoMaster.FullName, Path.GetFileNameWithoutExtension(fileinfoMaster.Name) + ".log");
        }

        #endregion

        using (StreamWriter sw = File.AppendText(fileinfoMaster.DirectoryName + "\\" + Path.GetFileNameWithoutExtension(fileinfoMaster.Name) + ".log"))
        {
            sw.Write(XMLStringToLog);
            sw.Flush();
        }
    }
    finally
    {
        lock (LogDataContractToFileLock)
        {
            _workingWithFiles.Remove(filePathAndName);
        }
    }

}

private static void CreateLogDataMasterFile(string filepathAndName, string dataFileName)
{
    XmlTextWriter xmlWriter;
    using (xmlWriter = new XmlTextWriter(filepathAndName, System.Text.Encoding.UTF8))
    {
        xmlWriter.Formatting = Formatting.Indented;
        xmlWriter.WriteStartDocument();
        xmlWriter.WriteDocType("DataLog", null, null, "<!ENTITY data SYSTEM \"" + dataFileName + "\">");
        xmlWriter.WriteStartElement("root");
        xmlWriter.WriteRaw("&data;");
        xmlWriter.WriteEndElement();
        xmlWriter.Flush();
    }
}

Several different threads might write to file at the same time and in some cases they will write to the same file. To avoid problems with access denied, used by other process I have implemented some logic as you can see.

The problem is that I am still getting access denied, used by other process from time to time.

I have looked at the build in tracers like the TextWriterTraceListener and here I can see the following note :

If an attempt is made to write to a file that is in use or unavailable, the file name is automatically prefixed by a GUID.

So it looks like even Microsoft has problems with this? Is it a Windows problem? Is there any way to handle this becides generating a new file? Generating a new file will mess of the data flow in files a lot!

1

There are 1 answers

4
spender On BEST ANSWER

Yes. Don't write to a file from multiple threads. Create a single thread that is reponsible for file writes, and have it read what to write from a ConcurrentQueue. Any thread that needs to write should just add data to the queue.