0

I am encrypting some text files. It works fine the file is encypted, however on occasion I get this error when attempting to delete the original unencrypted file:

System.IO.IOException: The process cannot access the file 'MyFile.TXT' because it is being used by another process. at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at System.IO.File.Delete(String path) at FileEncryption.Program.DeleteFile(String sInputFilename) in FileEncryption\Program.cs:line 159

This seems to happen on large text files. (50MB+) but not always.

Any idea what I might be doing wrong?

Method to PROCESS the folder of txt files:

private static void BeginFileProcessing(string sSecretKey_)
{
    DirectoryInfo di = new DirectoryInfo(_sourcePath);
    FileInfo[] files = di.GetFiles(_fileType);

    try
    {
        foreach (FileInfo file in files)
        {
            string thisFileExt = Path.GetExtension(file.Name);
            string thisFileName = Path.GetFileNameWithoutExtension(file.Name);
            string encFileName = String.Format("{0}-enc{1}", thisFileName, thisFileExt);

            if (_TestingOnly)
            {
                Console.Write("Source: " + file.Name + " " + 
                    " Encrypted File: " + encFileName + "\n");
            }

            EncryptFile(file.FullName, _targetPath + encFileName, sSecretKey_);

            if (_DeleteOriginal)
            {
                Console.WriteLine("Deleteing file: " + file.FullName);
                DeleteFile(file.FullName);
            }
        }
    }
    catch (Exception ex)
    {
        LogWriter(string.Format("\nError Decrypting file: {0}", ex), true);
    }
}

Method to ENCRYPT the files

private static void EncryptFile(string sInputFilename, 
    string sOutputFilename, string sKey)
{
    FileStream fsInput = 
        new FileStream(sInputFilename, FileMode.Open, FileAccess.Read);

    FileStream fsEncrypted = 
        new FileStream(sOutputFilename, FileMode.Create, FileAccess.Write);

    DESCryptoServiceProvider DES = new DESCryptoServiceProvider();

    DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
    DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
    ICryptoTransform desencrypt = DES.CreateEncryptor();

    CryptoStream cryptostream = 
        new CryptoStream(fsEncrypted, desencrypt, CryptoStreamMode.Write);

    try
    {
        byte[] bytearrayinput = System.IO.File.ReadAllBytes(sInputFilename);
        fsInput.Read(bytearrayinput, 0, bytearrayinput.Length);
        cryptostream.Write(bytearrayinput, 0, bytearrayinput.Length);
        cryptostream.Close();
        fsInput.Close();
        fsEncrypted.Close();
    }
    catch (Exception ex)
    {
        string error = "";

        foreach (DictionaryEntry pair in ex.Data)
        {
            error += pair.Key + " = " + pair.Value + "\n";
            Console.WriteLine(error);
        }

        LogWriter(error, true);
    }
} 

Method to DELETE the files

private static void DeleteFile(string sInputFilename)
{
    try
    {
        if (_TestingOnly)
        {
            Console.WriteLine("TESTING ONLY! File: " + sInputFilename + " would have been deleted.");
        }
        else
        {
            File.Delete(sInputFilename);
        }
    }
    catch (Exception ex)
    {
        Console.Write(ex.ToString());
        LogWriter(ex.ToString(), true);
    }
}
Rufus L
  • 36,127
  • 5
  • 30
  • 43
user-44651
  • 3,924
  • 6
  • 41
  • 87
  • Quite a bit to read, but the file is locked by another process. Ie, you open a text file then attempt to delete, while it is open it will throw an error. – Greg Mar 20 '15 at 16:48
  • Yeah, it is a lot to read. But rather too much info than not enough. I feel like I am "closing" the file after I encrypt it. – user-44651 Mar 20 '15 at 16:49
  • I have encountered similar file access issues with a particular AV product before. In that case I worked around it by adding some retry logic and a sleep. – bkr Mar 20 '15 at 16:50
  • You could implement a `using` and `IDispose` then implement said logic. – Greg Mar 20 '15 at 16:51
  • @Greg on what part should I use the Using. I have also never used iDispose before eeek – user-44651 Mar 20 '15 at 16:52
  • You should use `using` on any object that implements `IDisposable`, like all your `Stream` objects and the `DESCryptoServiceProvider`, – Rufus L Mar 20 '15 at 17:17
  • should i just stack the usings? using(filestream){using(filenew){using(DESCrypto){}}} – user-44651 Mar 20 '15 at 17:20

3 Answers3

1

This may be caused by your files not being closed after the call to EncryptFile. In your original code, if you hit an exception in EncryptFile the streams would be left open if the exception happens before the call to Close. Using Using statements makes this easier but you can also put the Close in a finally block and close the streams if they're not null.

Here's an example using Using:

private static void EncryptFile(string sInputFilename, 
    string sOutputFilename, string sKey)
{
    using(FileStream fsInput =  new FileStream(sInputFilename, FileMode.Open, FileAccess.Read),
            FileStream fsEncrypted = new FileStream(sOutputFilename, FileMode.Create, FileAccess.Write))
    {

        DESCryptoServiceProvider DES = new DESCryptoServiceProvider();

        DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
        DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
        ICryptoTransform desencrypt = DES.CreateEncryptor();

        using(CryptoStream cryptostream = 
            new CryptoStream(fsEncrypted, desencrypt, CryptoStreamMode.Write))
        {
            try
            {
                byte[] bytearrayinput = System.IO.File.ReadAllBytes(sInputFilename);
                fsInput.Read(bytearrayinput, 0, bytearrayinput.Length);
                cryptostream.Write(bytearrayinput, 0, bytearrayinput.Length);
            }
            catch (Exception ex)
            {
                string error = "";

                foreach (DictionaryEntry pair in ex.Data)
                {
                    error += pair.Key + " = " + pair.Value + "\n";
                    Console.WriteLine(error);
                }

                LogWriter(error, true);
            }
        }
    }
}

Edit

My proposed code will solve the issue of the file stream being left open. However, the root of the issue is that the system throws an OutOfMemoryException while reading the file if it's large. The original code would read all the bytes then read the bytes again into the same buffer, which is a waste of memory and a waste of time. Below is a corrected version:

private static void EncryptFile(string sInputFilename, 
    string sOutputFilename, string sKey)
{
    using(FileStream fsInput =  new FileStream(sInputFilename, FileMode.Open, FileAccess.Read),
            fsEncrypted = new FileStream(sOutputFilename, FileMode.Create, FileAccess.Write))
    {

        DESCryptoServiceProvider DES = new DESCryptoServiceProvider();

        DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
        DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
        ICryptoTransform desencrypt = DES.CreateEncryptor();

        using(CryptoStream cryptostream = 
            new CryptoStream(fsEncrypted, desencrypt, CryptoStreamMode.Write))
        {
            byte[] buffer = new byte[2048];
            int readCount = 0;

            try
            {
                while ((readCount = fsInput.Read(buffer, 0, 2048)) > 0)
                {
                    cryptostream.Write(buffer, 0, readCount);
                }
            }
            catch (Exception ex)
            {
                string error = "";

                foreach (DictionaryEntry pair in ex.Data)
                {
                    error += pair.Key + " = " + pair.Value + "\n";
                    Console.WriteLine(error);
                }

                LogWriter(error, true);
            }
        }
    }
}
amura.cxg
  • 2,348
  • 3
  • 21
  • 44
  • Hmmm i tried adding a finally to the try/catch to close the fsInput, fsEnctyped and the cryptoStream. It will close, but then the delete cannot access a closed file. – user-44651 Mar 20 '15 at 18:16
  • Can you give me a sample of how you call `BeginFileProcessing` I'm going to try running the code myself to see if I can spot anything – amura.cxg Mar 20 '15 at 18:23
  • BeginFileProcessing is just called in the Main method – user-44651 Mar 20 '15 at 19:28
  • I ran some tests and finally found something. It seems that it was as I expected, the code would throw and exception (`OutOfMemoryException`) while in the `EncryptFile` function, the files streams wouldn't be closed and then the delete would crash. I ran my tests on your original code and on my proposed code and the proposed code would correctly delete the files even when the exception occurred – amura.cxg Mar 20 '15 at 20:00
  • See my edit, this should fix your issues. I'm surprised no one noticed that you read all the bytes into a buffer then read all the bytes again the line below. – amura.cxg Mar 20 '15 at 20:08
  • Sorry for the late response. I tried you Edited code: I get a Cryptography error: Length of the data to encrypt is invalid. I also had to revise your first 'using' statement as separating the two filestreams with a comma was invalid. I just broke the second out and stacked it. – user-44651 Mar 23 '15 at 12:19
  • That's what I get for copying and pasting...I corrected the issue with the `using` statement, thanks for pointing that out! As for your issue, I can't say much as when I never encountered that kind of exception while testing. It's possible you have a logic error somewhere. If you post your code I'd be happy to take a look. If you want to see my test project you can download a copy [here](http://www60.zippyshare.com/v/fPqIlrBW/file.html) – amura.cxg Mar 23 '15 at 15:03
0

You should consider using using statements for all objects that implement IDisposable. This will ensure that they are closed and disposed at the end of the using block:

private static void EncryptFile(string sInputFilename, string sOutputFilename, 
    string sKey)
{
    using (var fsInput = new FileStream(sInputFilename, FileMode.Open, 
        FileAccess.Read))
    using (var fsEncrypted = new FileStream(sOutputFilename, FileMode.Create, 
        FileAccess.Write))
    using (var desCryptoProvider = new DESCryptoServiceProvider())
    {
        desCryptoProvider.Key = Encoding.ASCII.GetBytes(sKey);
        desCryptoProvider.IV = Encoding.ASCII.GetBytes(sKey);

        using (var encryptor = desCryptoProvider.CreateEncryptor())
        using (var cryptoStream = new CryptoStream(fsEncrypted, encryptor, 
            CryptoStreamMode.Write))
        {
            try
            {
                var bytearrayinput = File.ReadAllBytes(sInputFilename);
                fsInput.Read(bytearrayinput, 0, bytearrayinput.Length);
                cryptoStream.Write(bytearrayinput, 0, bytearrayinput.Length);
            }
            catch (Exception ex)
            {
                var errors = new StringBuilder();

                foreach (var pair in ex.Data)
                {
                    errors.AppendLine(string.Format("{0} = {1}", pair.Key, pair.Value));
                }

                Console.WriteLine(errors.ToString());
                LogWriter(errors.ToString(), true);
            }
        }
    }
}
Rufus L
  • 36,127
  • 5
  • 30
  • 43
-1

It's not exactly an answer, but may help; a simple method to check if a file is locked:

bool IsFileAvailable(string fileName)
{
    FileStream stream = null;

    try
    {
        FileInfo fileInfo = new FileInfo(fileName);
        stream = fileInfo.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
    }
    catch (IOException)
    {
        // File is not present, or locked by another process
        return false;
    }
    finally
    {
        if (stream != null)
            stream.Close();
    }

    // File is present and not locked
    return true;
}
technophebe
  • 494
  • 3
  • 13