Creating a temporary directory in Windows?
Asked Answered
L

10

164

What's the best way to get a temp directory name in Windows? I see that I can use GetTempPath and GetTempFileName to create a temporary file, but is there any equivalent to the Linux / BSD mkdtemp function for creating a temporary directory?

Luxemburg answered 10/11, 2008 at 16:50 Comment(3)
This question seems a little hard to find. In particular, it doesn't show up if you type things like "temp directory .net" in the Stack Overflow search box. That seems unfortunate, since the answers are so far all .NET answers. Do you think you could add the ".net" tag? (And maybe the "directory" or "temporary-directory" tag?) Or maybe add the word ".NET" to the title? Maybe also in the question body alternate saying "temp" with "temporary" -- so if you search for the shorter form you'll still get a good text search match. I don't seem to have enough rep to do these things myself. Thanks.Feminine
Well, I was looking for a non-.NET answer, so I'd prefer to leave that out, but I made the other edits you suggested. Thanks.Luxemburg
@Josh Kelley: I just double-checked the Win32 API and the only options there are to follow a similar approach of getting the temp path, generating a ramdom file name, and then creating a directory.Esculent
E
293

No, there is no equivalent to mkdtemp. The best option is to use a combination of GetTempPath and GetRandomFileName.

You would need code similar to this:

public string GetTemporaryDirectory()
{
    string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());

    if(File.Exist(tempDirectory)) {
        return GetTemporaryDirectory();
    } else {
        Directory.CreateDirectory(tempDirectory);
        return tempDirectory;
    }
}
Esculent answered 10/11, 2008 at 16:55 Comment(13)
@Will: Thanks for the edit and correcting my typo! One too many copy/paste errors. :(Esculent
This seems a little dangerous. In particular, there's a chance (small, but non-zero, right?) that Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()) will return the name of a directory that already exists. Since Directory.CreateDirectory(tempDirectory) won't throw an exception if tempDirectory already exists, this case won't be detected by your application. And then you may have two applications stepping on each other's work. Is there any safer alternative in .NET?Feminine
@Chris: The GetRandomFileName method returns a cryptographically strong, random string that can be used as either a folder name or a file name. I suppose it's theoretically possible that the resulting path could already exist, but there are no other ways to do this. You could check to see if the path exists, and if it does call Path.GetRandomFileName() again, and repeat.Esculent
@Scott I think checking if the path exists isn't actually sufficient if you are truly obsessed with safety. Suppose you call Directory.Exists(tempDirectory) and learn that the directory doesn't yet exist. At this point another process could potentially sneak in and create the very directory you are about to create before you get to your call to Directory.CreateDirectory(). When control returns to your application, your call to CreateDirectory won't throw an exception, and the two applications will end up stepping on each other's toes. Seems unlikely in practice, yet it still bothers me a bit.Feminine
@Chris: Yes, if you are worried about security to that extent there is a very slim chance that another process could create the directory between the Path.Combine and the Directory.CreateDirectory calls.Esculent
GetRandomFileName generates 11 random lowercase letters and numbers meaning the domain size is (26+10)^11 = ~57 bits. You can always make two calls to square itLondalondon
@piedar: If you don't trust the provider of your random number generator you could always write your own generator and then use that in place of the call to Path.GetRandomFileName().Esculent
@ScottDorman Aye, my comment was made with tongue in cheek. There's a fine line between security-conscious and paranoid.Regal
A classic workaround for this is to prefix the folder name with a string that is derived from your application name, so there is little chance any other apps will use it.Cymograph
Just after you check that the directory does not exist, god could pause the universe, sneak in and create the same directory with all the same files you're about to write, causing your app to blow up, just to ruin your day.Bacillus
Or you could use your C# program's process ID which is guaranteed unique on the system among running processes ( so no chance of resource collision vs another instance of the same process). In a similar fashion you can uniquely identify the resources of individual threads within that pid-named dir. Then the directory creation with Directory.CreateDirectory is idempotent so you don't need to test for existence. If for some reason you're giving people access to your filesystem such that your only defense is randomly-named directories, good luck to you...Truelove
The way I have handled this in the past, is with a do while directory or file exists right before trying to create. I don't think it has ever gone more than 2 iterations, and is completely safe (well safe enough)Austronesia
"Just after you check that the directory does not exist, god could pause the universe, sneak in and create the same directory with all the same files you're about to write, causing your app to blow up, just to ruin your day." - Trinko This sort of thing happens to me all the time. :-)Kenric
F
30

I hack Path.GetTempFileName() to give me a valid, pseudo-random filepath on disk, then delete the file, and create a directory with the same file path.

This avoids the need for checking if the filepath is available in a while or loop, per Chris' comment on Scott Dorman's answer.

public string GetTemporaryDirectory()
{
  string tempFolder = Path.GetTempFileName();
  File.Delete(tempFolder);
  Directory.CreateDirectory(tempFolder);

  return tempFolder;
}

If you truly need a cryptographically secure random name, you may want to adapt Scott's answer to use a while or do loop to keep trying to create a path on disk.

Flagwaving answered 7/12, 2013 at 20:1 Comment(1)
Isn't there still a chance that someone creates the same directory between the Delete and the CreateDirectory?Sherd
D
18

Since .NET 7:

Directory.CreateTempSubdirectory();

C:\Users\USERNAME\AppData\Local\Temp\yafdhmxq.ngy


Using a custom prefix for the folder name

Directory.CreateTempSubdirectory("prefix_"); // prepends "prefix_" to the created dir name

C:\Users\USERNAME\AppData\Local\Temp\prefix_yrlbuecv.2lc

Damien answered 20/2, 2023 at 13:56 Comment(0)
B
11

I used some of the answers and implemented GetTmpDirectory method this way.

public string GetTmpDirectory()
{
    string tmpDirectory;

    do
    {
        tmpDirectory = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Path.GetRandomFileName()));
    } while (Directory.Exists(tmpDirectory));

    Directory.CreateDirectory(tmpDirectory);
    return tmpDirectory;
}
Bosnia answered 14/12, 2020 at 14:24 Comment(1)
Throw a || File.Exists(tempDirectory) in there and this is perfect. Aside from the unecessary abbreviation from temp to tmp. Do you really need to save that one character?Maladjusted
C
8

I like to use GetTempPath(), a GUID-creation function like CoCreateGuid(), and CreateDirectory().

A GUID is designed to have a high probability of uniqueness, and it's also highly improbable that someone would manually create a directory with the same form as a GUID (and if they do then CreateDirectory() will fail indicating its existence.)

Cremator answered 6/8, 2010 at 4:59 Comment(0)
R
7

@Chris. I too was obsessed with the remote risk that a temporary directory might already exist. The discussions about random and cryptographically strong don’t completely satisfy me either.

My approach builds on the fundamental fact that the O/S must not allow 2 calls to create a file to both succeed. It is a little surprising that .NET designers chose to hide the Win32 API functionality for directories, which makes this much easier, because it does return an error when you attempt to create a directory for the second time. Here is what I use:

    [DllImport(@"kernel32.dll", EntryPoint = "CreateDirectory", SetLastError = true, CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool CreateDirectoryApi
        ([MarshalAs(UnmanagedType.LPTStr)] string lpPathName, IntPtr lpSecurityAttributes);

    /// <summary>
    /// Creates the directory if it does not exist.
    /// </summary>
    /// <param name="directoryPath">The directory path.</param>
    /// <returns>Returns false if directory already exists. Exceptions for any other errors</returns>
    /// <exception cref="System.ComponentModel.Win32Exception"></exception>
    internal static bool CreateDirectoryIfItDoesNotExist([NotNull] string directoryPath)
    {
        if (directoryPath == null) throw new ArgumentNullException("directoryPath");

        // First ensure parent exists, since the WIN Api does not
        CreateParentFolder(directoryPath);

        if (!CreateDirectoryApi(directoryPath, lpSecurityAttributes: IntPtr.Zero))
        {
            Win32Exception lastException = new Win32Exception();

            const int ERROR_ALREADY_EXISTS = 183;
            if (lastException.NativeErrorCode == ERROR_ALREADY_EXISTS) return false;

            throw new System.IO.IOException(
                "An exception occurred while creating directory'" + directoryPath + "'".NewLine() + lastException);
        }

        return true;
    }

You get to decide whether the "cost/risk" of unmanaged p/invoke code is worth it. Most would say it is not, but at least you now have a choice.

CreateParentFolder() is left as an exercise to the student. I use Directory.CreateDirectory(). Be careful getting the parent of a directory, since it is null when at the root.

Rehearse answered 10/1, 2016 at 22:42 Comment(0)
R
7

I usually use this:

    /// <summary>
    /// Creates the unique temporary directory.
    /// </summary>
    /// <returns>
    /// Directory path.
    /// </returns>
    public string CreateUniqueTempDirectory()
    {
        var uniqueTempDir = Path.GetFullPath(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));
        Directory.CreateDirectory(uniqueTempDir);
        return uniqueTempDir;
    }

If you want to be absolutely sure that this directory name will not exists in temp path then you need to check if this unique directory name exists and try to create other one if it really exists.

But this GUID-based implementation is sufficient. I have no experience with any problem in this case. Some MS applications uses GUID based temp directories too.

Recommit answered 2/9, 2016 at 7:25 Comment(0)
L
1

Here is a somewhat more brute-force approach to resolving the collision problem for temporary directory names. It is not an infallible approach, but it reduces significantly the chances of a folder path collision.

One could potentially add other process or assembly related information to the directory name to make the collision even less likely, although making such an information visible on the temporary directory name might not be desirable. One could also mix the order with which the time-related fields are combined to make the folder names look more random. I personally prefer to leave it that way simply because it is easier for me to find them all during debugging.

string randomlyGeneratedFolderNamePart = Path.GetFileNameWithoutExtension(Path.GetRandomFileName());

string timeRelatedFolderNamePart = DateTime.Now.Year.ToString()
                                 + DateTime.Now.Month.ToString()
                                 + DateTime.Now.Day.ToString()
                                 + DateTime.Now.Hour.ToString()
                                 + DateTime.Now.Minute.ToString()
                                 + DateTime.Now.Second.ToString()
                                 + DateTime.Now.Millisecond.ToString();

string processRelatedFolderNamePart = System.Diagnostics.Process.GetCurrentProcess().Id.ToString();

string temporaryDirectoryName = Path.Combine( Path.GetTempPath()
                                            , timeRelatedFolderNamePart 
                                            + processRelatedFolderNamePart 
                                            + randomlyGeneratedFolderNamePart);
Lamdin answered 9/12, 2013 at 23:48 Comment(0)
C
0

As mentioned above, Path.GetTempPath() is one way to do it. You could also call Environment.GetEnvironmentVariable("TEMP") if the user has a TEMP environment variable set up.

If you are planning on using the temp directory as a means of persisting data in the application, you probably should look at using IsolatedStorage as a repository for configuration/state/etc...

Cormorant answered 10/11, 2008 at 17:5 Comment(0)
F
-1

GetTempPath is the correct way of doing it; I'm not sure what your concern about this method is. You can then use CreateDirectory to make it.

Fortunato answered 10/11, 2008 at 16:55 Comment(2)
One problem is that GetTempFileName will create a zero-byte file. You need to use GetTempPath, GetRandomFileName and CreateDirectory instead.Esculent
Which is fine, possible, and doable. I was going to provide code but Dorman got it before me, and it works correctly.Fortunato

© 2022 - 2024 — McMap. All rights reserved.