Condividi tramite


Watch your step : The process cannot access the file (filename) because it is being used by another process.

Sometimes it happens. You code a super easy functionality but got hit big time when the unexpected happens.

Today, I would like share a scenario about reading files. All you need is to read a bunch of entries from that file.

You use a simple FileStream constructor and your project is going to be hosted by IIS. The "unexpected" in this case

is that the admins opted for Web Gardening. So, you end up with multiple instances of w3wp.exe each going after the same file.

Below is a code snippet that helps to build a repro.

[Serializable]
    public class Customer
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Surname { get; set; }
        public string City { get; set; }

        public static List<Customer> GetCustomersFromStorage(string FileName, bool BehaveBad)
        {
            List<Customer> customers = new List<Customer>();
            XmlSerializer serializer = new XmlSerializer(typeof(List<Customer>));

            if (BehaveBad)
                using (FileStream fs = new FileStream(FileName, FileMode.Open))
                {
                    using (TextReader reader = new StreamReader(fs))
                    {
                        customers = (List<Customer>)serializer.Deserialize(reader);

                        System.Threading.Thread.Sleep(10000);                   
                    }
                }
            else
                using (FileStream fs = new FileStream(FileName,FileMode.Open, FileAccess.Read, FileShare.Read)  )
                {
                    using (TextReader reader = new StreamReader(fs))
                    {
                        customers = (List<Customer>)serializer.Deserialize(reader);

                        System.Threading.Thread.Sleep(10000);
                    }
                }

            return customers;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            List<Customer> someCustomers = Customer.GetCustomersFromStorage(args[0], ( (args[1] == "0") ? false : true));
            Console.WriteLine("there are {0} customers in the database", someCustomers.Count);
        }
    }

 

To repro the problem you need to open two command prompts and execute the binary with parameters fullpathname 1

e.g. ViolateShare.exe C:\data\Customers.xml 1

The second instance of the process will terminate with the following call stack :

Unhandled Exception: System.IO.IOException: The process cannot access the file '
C:\data\Customers.xml' because it is being used by another process.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy)
   at System.IO.FileStream..ctor(String path, FileMode mode)
   at ViolateShare.Customer.GetCustomersFromStorage(String FileName, Boolean BehaveBad) in c:\code\SharingViolation\ViolateShare\Program.cs:line 26
   at ViolateShare.Program.Main(String[] args) in c:\code\SharingViolation\ViolateShare\Program.cs:line 54

Let's pause for a moment and think about this : What is the expected chain of call for a multiple overridden function ?

Yes! The simple forms ( those with less number of arguments ) call the complex ( those with more number of arguments ) ones. For the simple form to call the complex form there should be an assumption made for the

arguments of the complex form. In MSDN you will read the information about that assumption and its implications in the "Remarks" section of an API. Let's have a look :

FileStream Constructor (String, FileMode)
https://msdn.microsoft.com/en-us/library/47ek66wy(v=vs.110).aspx 

Let's scroll to the "Remarks". See how it reads :

"The constructor is given read/write access to the file, and it is opened sharing Read access (that is, requests to open the file for writing by this or another process will fail until the FileStream object has been closed, but read attempts will succeed)"

 

The solution to this particular instance is in the code snippet above. Use the form that explicitly allow a shared file access method. What else ?

  • Know your constructors, overridden functions
  • Do not assume single instance
  • Unless abused a try-catch block is a good idea

 

Take care

-faik