0

I have a stream of byte[] which I write to a temporary file and then I send it to another method which attaches it to an email. I then want to delete the temporary folder. The code snippet I am using is as follows.

 byte[] blackboxBytes = Convert.FromBase64String(backBoxBase64);
 uniqueTempFolder = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()));
 zipFilePath = Path.Combine(uniqueTempFolder.FullName, "BlackBox.zip");
 File.WriteAllBytes(zipFilePath, blackboxBytes);

 sendEmail (deviceFQN, message, ZipFilePath);
 s_Log.Warn("Email sent");

//recursive delete of the whole folder
 uniqueTempFolder.Delete(true);
 s_Log.Warn("In BB zipFilePath after delete");

When I run, the email is getting sent and I get the log "Email sent". but after that I get an error message and the temporary directory is not deleted.

IOError: [Errno 32] The process cannot access the file 'BlackBox.zip' because it is being used by another process.

I am deleting the directory only after the email method finishes processing. So I don't know why the folder is still being processed. Any pointers will be greatly appreciated.

Also I have no access to the sendEmail method, so how can I solve this....can I probably put my code in a synchronous block or something

The retun type of sendEmail is void...I cannot modify sendEmail , but I see it has a lock when it sends the email(dispatchEmailTask).......

lock (m_QueueLock) { m_DispatchEmailTasks.Enqueue (dispatchEmailTask);}

s‌​o in my code, how can I wait for it to complete before I delete the file?

Aparna
  • 835
  • 2
  • 23
  • 47
  • 3
    Have you called the `Dispose` method on the various objects? – Stefan Jul 17 '17 at 18:26
  • 1
    Sending the email doesn't release the hold on attached files. You need to dispose of the attachment objects. –  Jul 17 '17 at 18:26
  • 3
    Possible duplicate of [IOException: The process cannot access the file 'file path' because it is being used by another process](https://stackoverflow.com/questions/26741191/ioexception-the-process-cannot-access-the-file-file-path-because-it-is-being). The example in the accepted answer of this question is nearly identical to your question - it should be very useful to you. – Tyler Roper Jul 17 '17 at 18:26
  • 1
    The problem is probably in `sendEmail`. If the tips posted in the other comments don't help, please post that code. – Jon B Jul 17 '17 at 18:30
  • If I have no access to the sendEmail method, how can I solve this....can I probably put my code in a synchronous block or something? – Aparna Jul 17 '17 at 18:40
  • 1
    You won't be able to solve this without modifying `sendEmail` and properly disposing of the attachments. *That method* contains the bug, so *that method* needs to be fixed. –  Jul 17 '17 at 18:45
  • What is the return type on `sendEmail`? is it `void` or `Task`? (If `Task` then change to use `awaiter`.. if void, you have a bug in `sendEmail`.) – Matthew Whited Jul 17 '17 at 18:49
  • I would suggest that you could skip file handling and add the attachment in-memory. Example: https://stackoverflow.com/questions/5336239/attach-a-file-from-memorystream-to-a-mailmessage-in-c-sharp – Kb. Jul 17 '17 at 19:15
  • 1
    That would leave the memory leaks. The attachments still wouldn't be disposed correctly. –  Jul 17 '17 at 19:48
  • the retun type of sendEmail is void...I cannot modify sendEmail , but I see it has a lock when it sends the email.......lock (m_QueueLock) { m_DispatchEmailTasks.Enqueue(dispatchEmailTask);}......so in my code, how can I wait for it to complete before I delete the file – Aparna Jul 18 '17 at 11:43
  • **You can't**. *Nothing* you do, except ensuring that method properly disposes of its attachments, can guarantee the garbage collector has released those files. –  Jul 18 '17 at 13:03
  • Something you can do is create a new delete method that includes support for `MoveFile` "Delete on Reboot" is there is an exception. This will clean up the files on the next reboot if they can't be cleaned how. If you are going to try your lock method I recomend you start a second thread/task so you don't block your entire app waiting for cleanup. https://stackoverflow.com/questions/21641006/how-does-windows-remove-locked-files-in-the-next-reboot-when-you-uninstall-a-pro – Matthew Whited Jul 18 '17 at 13:44

2 Answers2

0

There was no way to do this because the sendEmail sent the emails to a queue and there was no handler returned to let me know the action was competed. So in the end I created a cleanup job that ran daily to clean up the files.

Aparna
  • 835
  • 2
  • 23
  • 47
-2

This is because you are working with Streams which are normally In-Memory and normally employ locks for purposes of single calls. This means you can only use a stream once and have to re-create it again in case you want to perform additional operations on the file or directory.

The problem in your code is when you are writing the zip file to the directory, the stream does not get release. A Using statement coupled up together with a StreamWriter all under System.IO will help you in this as shown in the code below.

        byte[] blackboxBytes = Convert.FromBase64String(backBoxBase64);
        var uniqueTempFolder = 
                 Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), 
                                                 Path.GetRandomFileName()));
        var zipFilePath = Path.Combine(uniqueTempFolder.FullName, 
                                                            "BlackBox.zip");

        using (StreamWriter writer = new StreamWriter(zipFilePath))
        {
            writer.Write(blackboxBytes);
        }

        sendEmail(deviceFQN, message, ZipFilePath);
        s_Log.Warn("Email sent");

        //recursive delete of the whole folder
        uniqueTempFolder.Delete(true);
        s_Log.Warn("In BB zipFilePath after delete");
Timothy Macharia
  • 2,641
  • 1
  • 20
  • 27
  • `Directory.CreateDirectory` does not return a file stream, and `DirectoryInfo` is not disposable. You're right about the root cause, wrong about the actual solution. – Jeroen Mostert Jul 17 '17 at 20:10
  • Then use a Using statement plus a StreamWriter to solve the problem. Let me edit my answer and give you the correct answer then. – Timothy Macharia Jul 17 '17 at 20:19
  • Before you edit anything, carefully read the question again. There is no way you can modify the code given there so that it does the same thing *and* gets rid of the locking issue. That would require opening up `sendEmail`. – Jeroen Mostert Jul 17 '17 at 20:22
  • Please do try and give feedback of what results you get. – Timothy Macharia Jul 17 '17 at 20:26
  • 1
    This code will not compile because `StreamWriter.Write` does not take a `byte[]`. And before you go off and change things to use a`FileStream`, don't bother. [`File.WriteAllBytes`](http://referencesource.microsoft.com/#mscorlib/system/io/file.cs,943) already does that, including proper disposing. – Jeroen Mostert Jul 17 '17 at 20:32
  • this code gives the same error......I want to know how I can make my code to wait before doing the delete. – Aparna Jul 18 '17 at 11:50