74

What's a good way to ensure that a temp file is deleted if my application closes or crashes? Ideally, I would like to obtain a temp file, use it, and then forget about it.

Right now, I keep a list of my temp files and delete them with an EventHandler that's triggered on Application.ApplicationExit.

Is there a better way?

2
  • 3
    It's a shame .NET doesn't have something like Java's deleteOnExit() in the File class... not that it works properly if a file isn't closed. Commented Dec 30, 2008 at 13:45
  • I think FileOptions - DeleteOnClose will help to delete the fileStream on closing of fileStream. This option needs to be given at the time of fileStream creation learn.microsoft.com/en-us/dotnet/api/… Commented Jun 9, 2022 at 9:57

9 Answers 9

91

Nothing is guaranteed if the process is killed prematurely, however, I use "using" to do this..

using System;
using System.IO;
sealed class TempFile : IDisposable
{
    string path;
    public TempFile() : this(System.IO.Path.GetTempFileName()) { }

    public TempFile(string path)
    {
        if (string.IsNullOrEmpty(path)) throw new ArgumentNullException("path");
        this.path = path;
    }
    public string Path
    {
        get
        {
            if (path == null) throw new ObjectDisposedException(GetType().Name);
            return path;
        }
    }
    ~TempFile() { Dispose(false); }
    public void Dispose() { Dispose(true); }
    private void Dispose(bool disposing)
    {
        if (disposing)
        {
            GC.SuppressFinalize(this);                
        }
        if (path != null)
        {
            try { File.Delete(path); }
            catch { } // best effort
            path = null;
        }
    }
}
static class Program
{
    static void Main()
    {
        string path;
        using (var tmp = new TempFile())
        {
            path = tmp.Path;
            Console.WriteLine(File.Exists(path));
        }
        Console.WriteLine(File.Exists(path));
    }
}

Now when the TempFile is disposed or garbage-collected the file is deleted (if possible). You could obviously use this as tightly-scoped as you like, or in a collection somewhere.

Sign up to request clarification or add additional context in comments.

6 Comments

It's not often you see a proper place to use an empty catch block.
I'll probably implement a variant of this. It's straight forward and easy to understand.
is this this code can delete an temp files used by other applications
@KevinPanko because I haven't taken the time to think about all the possible inheritance scenarios to determine whether it will behave as expected when subclassed. If you want to do that, feel free,
@PMBjornerud yes, in this case; the purpose of the finalizer here has noting to do with the File object. It has to do with the intent of the class, which is (see the question) to delete the file when done. We want to do this even if somebody fails to Dispose() it correctly. We don't even have a File instance.
|
84

Consider using the FileOptions.DeleteOnClose flag:

using (FileStream fs = new FileStream(Path.GetTempFileName(),
       FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None,
       4096, FileOptions.RandomAccess | FileOptions.DeleteOnClose))
{
    // temp file exists
}

// temp file is gone

4 Comments

This is the simplest/best solution in most cases; just let the OS take care of it. I edited the code to make it a little clearer and changed the buffer size to match the FileStream default. BTW, I prefer FileOptions.CreateNew but I left FileMode.OpenOrCreate since you chose it purposefully.
The other process can't access file created even when FileOptions.DeleteOnClose is set and do not takes in account the FileShare.ReadWrite. So overall this code makes no sense to use as temp file.
@Tomas Don't suppose you'd be able to elaborate on why this code snippet will not allow other process to access despite the DeleteOnClose? (I'm not disagreeing with you, as I have literally wrote this same using statement and just ran into the issue that a process still has a hold on the file hence me finding this thread!) I'm new to C# and can't seem to work out specifically what about this is causing that, and what the recommended alternative would be (use File.Create instead of new FileStream?).
@piedar Path.GetTempFileName() already creates the file, so FileOptions.CreateNew will not work and throw an IOException: The file ... already exists.
22

You could P/Invoke CreateFile and pass the FILE_FLAG_DELETE_ON_CLOSE flag. This tells Windows to delete the file once all handles are closed. See also: Win32 CreateFile docs.

3 Comments

Be careful using this method if you need to open the file more than once (e.g. at a later time or by another process). For example, if you expect to open, populate, and close the file, it may vanish immediately so that you cannot use what you just created. You would have to leave the original handle open long enough for a 2nd handle to be opened, but that could cause a sharing violation if not opened with the appropriate flags. In .NET 4 (possibly earlier), just use File.Create( path, 0x1000, FileOptions.DeleteOnClose ); no need for P/Invoke.
I wonder if there is a way to use FileOptions.DeleteOnClose in conjunction with System.Diagnostics.Process.Start(myFile) somehow. I would like to display the file and immediately delete after.
@knockando This is usually done by waiting for the process and deleting the file after it has finished. I don't see a way to do this with DeleteOnClose.
10

I would use the .NET TempFileCollection class, as it's built-in, available in old versions of .NET, and implements the IDisposable interface and thus cleans up after itself if used e.g. in conjunction with the "using" keyword.

Here's an example that extracts text from an embedded resource (added via the projects property pages -> Resources tab as described here: How to embed a text file in a .NET assembly?, then set to "EmbeddedResource" in the embedded file's property settings).

    // Extracts the contents of the embedded file, writes them to a temp file, executes it, and cleans up automatically on exit.
    private void ExtractAndRunMyScript()
    {
        string vbsFilePath;

        // By default, TempFileCollection cleans up after itself.
        using (var tempFiles = new System.CodeDom.Compiler.TempFileCollection())
        {
            vbsFilePath= tempFiles.AddExtension("vbs");

            // Using IntelliSense will display the name, but it's the file name
            // minus its extension.
            System.IO.File.WriteAllText(vbsFilePath, global::Instrumentation.Properties.Resources.MyEmbeddedFileNameWithoutExtension);

            RunMyScript(vbsFilePath);
        }

        System.Diagnostics.Debug.Assert(!File.Exists(vbsFilePath), @"Temp file """ + vbsFilePath+ @""" has not been deleted.");
    }

Comments

6

I use a more reliable solution:

using System.IO;
using System.Reflection;
 
namespace Helpers
{
    public static partial class TemporaryFiles
    {
        private const string UserFilesListFilenamePrefix = ".used-temporary-files.txt";
        static private readonly object UsedFilesListLock = new object();
 
        private static string GetUsedFilesListFilename()
        {
            return Assembly.GetEntryAssembly().Location + UserFilesListFilenamePrefix;
        }
 
        private static void AddToUsedFilesList(string filename)
        {
            lock (UsedFilesListLock)
            {
                using (var writer = File.AppendText(GetUsedFilesListFilename()))
                    writer.WriteLine(filename);
            }
        }
 
        public static string UseNew()
        {
            var filename = Path.GetTempFileName();
            AddToUsedFilesList(filename);
            return filename;
        }
 
        public static void DeleteAllPreviouslyUsed()
        {
            lock (UsedFilesListLock)
            {
                var usedFilesListFilename = GetUsedFilesListFilename();
 
                if (!File.Exists(usedFilesListFilename))
                    return;
 
                using (var listFile = File.Open(usedFilesListFilename, FileMode.Open))
                {
                    using (var reader = new StreamReader(listFile))
                    {
                        string tempFileToDelete;
                        while ((tempFileToDelete = reader.ReadLine()) != null)
                        {
                            if (File.Exists(tempFileToDelete))
                                File.Delete(tempFileToDelete);
                        }
                    }
                }
 
                // Clean up
                using (File.Open(usedFilesListFilename, FileMode.Truncate)) { }
            }
        }
    }
}

Every time you need temporary file use:

var tempFile = TemporaryFiles.UseNew();

To be sure all temporary files are deleted after application closes or crashes put

TemporaryFiles.DeleteAllPreviouslyUsed();

at start of the application.

1 Comment

Or you can use TemporaryFiles abstraction from our LinksPlatfrom's class library (available as NuGet package).
3

It's nice to see that you want to be responsible, but if the files aren't huge (>50MB), you would be in line with everyone (MS included) in leaving them in the temp directory. Disk space is abundant.

As csl posted, GetTempPath is the way to go. Users who are short on space will be able to run disk cleanup and your files (along with everyone else's) will be cleaned up.

7 Comments

Personally I think a temp file is a temp file, and should be destroyed as soon as it's no longer needed. I hate my disk being cluttered with all kinds of trash. Shameful that no-one (MS included) cares about my computer's state...
Agreed.. if for whatever reason you can't clean up all files, they'd better be in the temp directory then anywhere else.
People don't like it when the truth of the situation doesn't match with their perfect ideals.
The trouble is Windows can't know when a temp file should be deleted, so it has to leave them on the disk, leaving it up to the programmer to remember to delete them. It would be good if they did remember! It's a pity more programmers don't use FileOptions DeleteOnClose
It's probably "OK" for most applications to leave temp files in place in the case of a sudden power loss. It might also be considered OK when the process is killed. It's certainly not OK tough to leave temp files in place if the application is neither killed nor interrupted by a power loss (or similar).
|
3

I'm not primarily a C# programmer, but in C++ I'd use RAII for this. There are some hints on using RAII-like behaviour in C# online, but most seem to use the finalizer — which is not deterministic.

I think there are some Windows SDK functions to create temporary files, but don't know if they are automatically deleted on program termination. There is the GetTempPath function, but files there are only deleted when you log out or restart, IIRC.

P.S. The C# destructor documentation says you can and should release resources there, which I find a bit odd. If so, you could simply delete the temp file in the destructor, but again, this might not be completely deterministic.

7 Comments

The destructor is commonly used to release handles and resources on a per class basis. I imagine that you could use it to delete files, but that should be really be handled by the application code that created them.
Noob question: Does it have to be deterministic? To my knowledge, deterministic means that you do not know exactly WHEN it will run, but normally you can be sure that it WILL run (unless you yank the power cord of course) - or am I wrong here?
How do you RAII expect to work when the process exits abnormally? The OP specifically asks "... or crashes".
@MarkusSchaber: If it's an exception, RAII works fine, if you pull the power cord to the computer, it won't help. :)
@csl: There are other ways (e. G. hard process kill) which prevent RAII from working.
|
0

You could launch a thread on startup that will delete files that exist when they "shouldn't" to recover from your crash.

Comments

-2

If you're building a Windows Forms Application, you can use this code:

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        File.Delete("temp.data");
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.