【问题标题】:Stream failing to close C#流无法关闭 C#
【发布时间】:2020-03-16 14:42:48
【问题描述】:

我正在创建一个程序,该程序接受密码并将其编码应用到我有创造性地标记为 PASSWORDFILE 文件的文件上。我是一名自学成才的业余程序员,这是我第一次使用流 => 很抱歉我的代码不干净。当我向我的文件添加密码时,文件拒绝打开(给我一个“System.IO.IOException:该进程无法访问文件'[此处的文件路径]',因为它正在被另一个进程使用。”)。我已确保关闭所有流,但此错误仍然存​​在。

为了进一步混淆:

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

该错误仅在我尝试添加 EncodedPasswordEncodedPasswordWithAddendum 实例而不是 Password 实例时发生。 我的写代码是

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

我已经删除了在EncodeFromStreamDecodeFromStream 中对流进行编码和解码的代码。 new FileStream(saveFilePath + "1", FileMode.OpenOrCreate, FileAccess.ReadWrite) 的任何出现都是我以解码格式写入单独文件的地方。为了区分这两个文件,我将文件类型从 PASSWORDFILE 更改为 PASSWORDFILE1。

结论: 我正在使用WriteFileWriteToFile 方法和包含EncodedPasswordEncodedPasswordWithAddendumPassword[]。然后当我尝试通过FileStream(通常通过方法ReadFile)打开文件时,我得到异常“System.IO.IOException:进程无法访问文件'[file path here]',因为它正在被另一个进程使用”。

感谢您的帮助。

【问题讨论】:

  • 旁注:从不 Closeexplicty,但包含在 using: using (Stream stream = new FileStream(...)) {}
  • 您需要避免显式调用Close。相反,将每个 Stream 的使用包装在 using 块中。
  • 确保没有外部应用程序锁定您的文件。请参阅:helpcenter.gsx.com/hc/en-us/articles/…(Process Explorer 可以安全使用 - 它由 Microsoft 创建)

标签: c# file filestream memorystream


【解决方案1】:

流通常包含未管理的资源(操作系统文件句柄),因此它们实现了 IDisposeable。

虽然您始终可以确定 GC 会最终清理可丢弃的东西(最迟在应用程序关闭时),但通常这已经太晚了。你必须明确地做到这一点。为此,我有一条关于 IDisposeable 东西的规则:

“永远不要将可处置资源的创建和处置分开。创建、使用、处置。所有这些都在同一段代码中,最好使用 using 块。” 我遇到的唯一例外是日志文件。没有其他东西是远程值得为保持一次性打开的东西而烦恼和头痛的。尤其不是性能。

由于using block 使用了try...最后,您可以确定它会运行。编译器和运行时使某些 finally 块始终运行,即使在函数返回、通过 goto 或异常情况跳转时也是如此。

【讨论】:

    猜你喜欢
    • 2021-01-08
    • 1970-01-01
    • 2011-09-04
    • 2019-01-26
    • 2011-01-20
    • 2012-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多