0

I am creating a program which takes passwords and applies an encoding on them onto a file which I have creatively labeled a PASSWORDFILE file. I am a self taught amateur programmer and this is my first time using streams => I'm sorry my code isn't cleaner. When I add a password to my file, the file refuses to open (giving me a "System.IO.IOException: The process cannot access the file '[file path here]' because it is being used by another process."). I have made sure I am closing all my streams yet this error still persists.

To add further confusion:

namespace PasswordSaver
{
    [Serializable]
    class Password
    {
        public string ID;
        string baseWord;
        public Password(string password, string ID)
        {
            this.ID = ID;
            baseWord = password;
        }
        public virtual string GetPassword()
        {
            return baseWord;
        }
    }

    [Serializable]
    class EncodedPassword : Password
    {
        EncoderAndDecoder Encoder;

        public EncodedPassword(string decodedBasePassword, string ID) : base(decodedBasePassword, ID)
        {
            Encoder = new EncoderAndDecoder();
        }

        public override string GetPassword()
        {
            return Encoder.Encode(base.GetPassword(), out _);
        }
    }

    [Serializable]
    class EncodedPasswordWithAddendum : EncodedPassword
    {
        string addendum;
        public EncodedPasswordWithAddendum(string decodedBasePassword, string addendum, string ID) : base(decodedBasePassword, ID)
        {
            this.addendum = addendum;
        }
        public override string GetPassword()
        {
            return base.GetPassword() + addendum;
        }
    }
}

the error only occurs when I attempt to add an EncodedPassword or EncodedPasswordWithAddendum instances but not a Password instance. My writing code is

namespace PasswordSaver
{
    class PasswordWriter
    {
        public readonly string saveFilePath;
        static string directory = Directory.GetCurrentDirectory();

        #region Constructors
        public PasswordWriter()
        {
            saveFilePath = directory + @"\PasswordSaver"
                + ".passwordfile";
        }
        public PasswordWriter(string saveFilePath)
        {
            this.saveFilePath = saveFilePath;
        }
        #endregion

        #region Individual Writing Functions
        private void WriteBinary(object objectToEncode)
        {
            WriteBinary(objectToEncode, out _);
        }
        private void WriteBinary(object objectToEncode, out Exception exception)
        {
            exception = null;
            try
            {
                IFormatter binaryFormatter = new BinaryFormatter();

                Stream fileStream = new FileStream(saveFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
                Stream memoryStream = new MemoryStream();

                memoryStream.Position = memoryStream.Length;
                binaryFormatter.Serialize(memoryStream, objectToEncode);

                EncodeFromStream(ref memoryStream, ref fileStream);


                fileStream.Close();
                memoryStream.Close();
            }
            catch (Exception e)
            {
                exception = e;
            }
        }
        #endregion

        #region File Read and Writing
        public void WriteFile(Password[] passwords)
        {
            if (File.Exists(saveFilePath))
            {
                Stream stream = new FileStream(saveFilePath, FileMode.Truncate, FileAccess.Write);
                stream.Close();
            }

            WriteBinary(passwords.Length);
            foreach (Password password in passwords)
            {
                WriteBinary(password);
            }
        }

        public void WriteToFile(Password password)
        {
            Password[] oldPasswords = ReadFile();
            Password[] passwords = new Password[oldPasswords.Length + 1];
            for (int i = 0; i < oldPasswords.Length; i++)
            {
                passwords[i] = oldPasswords[i];
            }
            passwords[oldPasswords.Length] = password;
            WriteFile(passwords);
        }

        public bool ReplacePassword(string oldPasswordID, Password newPassword)
        {
            Password[] passwords = ReadFile();
            for (int i = 0; i < passwords.Length; i++)
            {
                if (passwords[i].ID == oldPasswordID)
                {
                    passwords[i] = newPassword;
                    return true;
                }
            }
            return false;
        }

        public Password[] ReadFile()
        {
            Stream fileStream = new FileStream(saveFilePath, FileMode.OpenOrCreate, FileAccess.Read);
            IFormatter binaryFormatter = new BinaryFormatter();
            Stream memoryStream = new MemoryStream();

            DecodeFromStream(ref fileStream, ref memoryStream);

            fileStream.Close();

            memoryStream.Position = 0;

            int length = (int) binaryFormatter.Deserialize(memoryStream);
            //Console.WriteLine(length + " is the length");//debug
            Password[] passwords = new Password[length];
            for (int i = 0; i < length; i++)
            {
                //Console.WriteLine(memoryStream.Position + " " + memoryStream.Length);//debug
                //Console.WriteLine(i);//debug
                passwords[i] = (Password)binaryFormatter.Deserialize(memoryStream);
            }

            memoryStream.Close();
            return passwords;
        }
        #endregion

        #region Encode and Decode
        private void EncodeFromStream(ref Stream stream, ref Stream newStream)
        {

            stream.Position = 0;
            newStream.Position = newStream.Length;


            for (int i = 0; i < stream.Length; i++)
            {
                int integer = stream.ReadByte();
                byte originalByte = (byte)integer;// get a byte off of the line
                //Encode byte here
                newStream.WriteByte(setOfBits1);
                newStream.WriteByte(setOfBits2);
            }
        }

        private void DecodeFromStream(ref Stream stream, ref Stream newStream)
        {
            newStream.Position = newStream.Length;

            stream.Position = 0;
            for (int i = 0; i < (stream.Length / 2); i++)// stream.Length / 2 because the program reads two bytes per iteration of the for loop
            {
                //I decode the bytes here
                newStream.WriteByte(originalByte);
            }

        }
        #endregion
        public void WriteContentsToFile()
        {
            Stream stream = new FileStream(saveFilePath + "1", FileMode.OpenOrCreate, FileAccess.ReadWrite);
            Stream stream1 = new FileStream(saveFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
            this.DecodeFromStream(ref stream1, ref stream);
            stream.Close();
            stream1.Close();
        }
    }
}

I have removed the code that encoded and decoded the streams in EncodeFromStream and DecodeFromStream. any occurrence of new FileStream(saveFilePath + "1", FileMode.OpenOrCreate, FileAccess.ReadWrite) is a where I was writing to a seperate file in a decoded format. To distinguish the two files I changed the file type from PASSWORDFILE to PASSWORDFILE1.

In Conclusion: I am using the WriteFile or WriteToFile methods with a Password[] that contains an EncodedPassword or EncodedPasswordWithAddendum. then when I try to open the file through a FileStream (usually through the method ReadFile) I get the Exception "System.IO.IOException: The process cannot access the file '[file path here]' because it is being used by another process".

Thank you for your help.

Elliot Hodge
  • 103
  • 4
  • Side note: never `Close` stream **explicty**, but wrap in `using`: `using (Stream stream = new FileStream(...)) {}` – Dmitry Bychenko Mar 16 '20 at 14:45
  • You need to avoid calling `Close` explicitly. Instead, wrap the use of each `Stream` in a `using` block. – Sean Mar 16 '20 at 14:52
  • Make sure that no external applications locks your file. See: https://helpcenter.gsx.com/hc/en-us/articles/115015880627-How-to-Identify-which-Windows-Process-is-Locking-a-File-or-Folder (Process Explorer is safe to use - it's created by Microsoft) – Maciek Świszczowski Mar 16 '20 at 14:54

1 Answers1

1

Streams usually contain unamanged resources (the OS Filehandles), so they implement IDisposeable.

While you can always be certain that the GC will clean up disposeable stuff eventually (latest at application closing), usually that is way to late. You have to do it explicitly. And for that I have a one rule regarding IDisposeable stuff:

"Never split up the creation and disposing of a disposeable resource. Create. Use. Dispose. All in the same piece of code, ideally using a using block." The only exception I ever encountered a logfiles. Nothing else is remotely worth the trouble and headaches of keeping something disposeable open. Especially not performance.

As the using block uses a try...finally, you can be certain it will run. Compiler and runtime make certain finally blocks always run, even on function return, jump via goto or Exception cases.

Christopher
  • 8,956
  • 2
  • 14
  • 31