Question

I am working on a program that needs to create a multiple temporary folders for the application. These will not be seen by the user. The app is written in VB.net. I can think of a few ways to do it such as incremental folder name or random numbered folder names, but I was wondering, how other people solve this problem?

Was it helpful?

Solution

Update: Added File.Exists check per comment (2012-Jun-19)

Here's what I've used in VB.NET. Essentially the same as presented, except I usually didn't want to create the folder immediately.

The advantage to use GetRandomFilename is that it doesn't create a file, so you don't have to clean up if your using the name for something other than a file. Like using it for folder name.

Private Function GetTempFolder() As String
    Dim folder As String = Path.Combine(Path.GetTempPath, Path.GetRandomFileName)
    Do While Directory.Exists(folder) or File.Exists(folder)
        folder = Path.Combine(Path.GetTempPath, Path.GetRandomFileName)
    Loop

    Return folder
End Function

Random Filename Example:

C:\Documents and Settings\username\Local Settings\Temp\u3z5e0co.tvq


Here's a variation using a Guid to get the temp folder name.

Private Function GetTempFolderGuid() As String
    Dim folder As String = Path.Combine(Path.GetTempPath, Guid.NewGuid.ToString)
    Do While Directory.Exists(folder) or File.Exists(folder)
        folder = Path.Combine(Path.GetTempPath, Guid.NewGuid.ToString)
    Loop

    Return folder
End Function

guid Example:

C:\Documents and Settings\username\Local Settings\Temp\2dbc6db7-2d45-4b75-b27f-0bd492c60496

OTHER TIPS

You have to use System.IO.Path.GetTempFileName()

Creates a uniquely named, zero-byte temporary file on disk and returns the full path of that file.

You can use System.IO.Path.GetDirectoryName(System.IO.Path.GetTempFileName()) to get only the temp folder information, and create your folders in there

They are created in the windows temp folder and that's consider a best practice

Just to clarify:

System.IO.Path.GetTempPath()

returns just the folder path to the temp folder.

System.IO.Path.GetTempFileName()

returns the fully qualified file name (including the path) so this:

System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetTempFileName())

is redundant.

There's a possible race condition when:

  • creating a temp file with GetTempFileName(), deleting it, and making a folder with the same name, or
  • using GetRandomFileName() or Guid.NewGuid.ToString to name a folder and creating the folder later

With GetTempFileName() after the delete occurs, another application could successfully create a temp file with the same name. The CreateDirectory() would then fail.

Similarly, between calling GetRandomFileName() and creating the directory another process could create a file or directory with the same name, again resulting in CreateDirectory() failing.

For most applications it's OK for a temp directory to fail due to a race condition. It's extremely rare after all. For them, these races can often be ignored.

In the Unix shell scripting world, creating temp files and directories in a safe race-free way is a big deal. Many machines have multiple (hostile) users -- think shared web host -- and many scripts and applications need to safely create temp files and directories in the shared /tmp directory. See Safely Creating Temporary Files in Shell Scripts for a discussion on how to safely create temp directories from shell scripts.

As @JonathanWright pointed out, race conditions exist for the solutions:

  • Create a temporary file with GetTempFileName(), delete it, and create a folder with the same name
  • Use GetRandomFileName() or Guid.NewGuid.ToString to create a random folder name, check whether it exists, and create it if not.

It is possible, however, to create a unique temporary directory atomically by utilizing the Transactional NTFS (TxF) API.

TxF has a CreateDirectoryTransacted() function that can be invoked via Platform Invoke. To do this, I adapted Mohammad Elsheimy's code for calling CreateFileTransacted():

// using System.ComponentModel;
// using System.Runtime.InteropServices;
// using System.Transactions;

[ComImport]
[Guid("79427a2b-f895-40e0-be79-b57dc82ed231")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IKernelTransaction
{
    void GetHandle(out IntPtr pHandle);
}

// 2.2 Win32 Error Codes <http://msdn.microsoft.com/en-us/library/cc231199.aspx>
public const int ERROR_PATH_NOT_FOUND = 0x3;
public const int ERROR_ALREADY_EXISTS = 0xb7;
public const int ERROR_EFS_NOT_ALLOWED_IN_TRANSACTION = 0x1aaf;

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool CreateDirectoryTransacted(string lpTemplateDirectory, string lpNewDirectory, IntPtr lpSecurityAttributes, IntPtr hTransaction);

/// <summary>
/// Creates a uniquely-named directory in the directory named by <paramref name="tempPath"/> and returns the path to it.
/// </summary>
/// <param name="tempPath">Path of a directory in which the temporary directory will be created.</param>
/// <returns>The path of the newly-created temporary directory within <paramref name="tempPath"/>.</returns>
public static string GetTempDirectoryName(string tempPath)
{
    string retPath;

    using (TransactionScope transactionScope = new TransactionScope())
    {
        IKernelTransaction kernelTransaction = (IKernelTransaction)TransactionInterop.GetDtcTransaction(Transaction.Current);
        IntPtr hTransaction;
        kernelTransaction.GetHandle(out hTransaction);

        while (!CreateDirectoryTransacted(null, retPath = Path.Combine(tempPath, Path.GetRandomFileName()), IntPtr.Zero, hTransaction))
        {
            int lastWin32Error = Marshal.GetLastWin32Error();
            switch (lastWin32Error)
            {
                case ERROR_ALREADY_EXISTS:
                    break;
                default:
                    throw new Win32Exception(lastWin32Error);
            }
        }

        transactionScope.Complete();
    }
    return retPath;
}

/// <summary>
/// Equivalent to <c>GetTempDirectoryName(Path.GetTempPath())</c>.
/// </summary>
/// <seealso cref="GetTempDirectoryName(string)"/>
public static string GetTempDirectoryName()
{
    return GetTempDirectoryName(Path.GetTempPath());
}

Something like...

using System.IO;

string path = Path.GetTempPath() + Path.GetRandomFileName();
while (Directory.Exists(path))
 path = Path.GetTempPath() + Path.GetRandomFileName();

Directory.CreateDirectory(path);

You could generate a GUID for your temporary folder names.

As long as the name of the folder doesn't need to be meaningful, how about using a GUID for them?

You can use GetTempFileName to create a temporary file, then delete and re-create this file as a directory instead.

Note: link didn't work, copy/paste from: http://msdn.microsoft.com/en-us/library/aa364991(VS.85).aspx

Combined answers from @adam-wright and pix0r will work the best IMHO:


using System.IO;

string path = Path.GetTempPath() + Path.GetRandomFileName();

while (Directory.Exists(path)) 
  path = Path.GetTempPath() + Path.GetRandomFileName();

File.Delete(path);
Directory.CreateDirectory(path);

The advantage to using System.IO.Path.GetTempFileName is that it will be a file in the user's local (i.e., non-roaming) path. This is exactly where you would want it for permissions and security reasons.

Dim NewFolder = System.IO.Directory.CreateDirectory(IO.Path.Combine(IO.Path.GetTempPath, Guid.NewGuid.ToString))

@JonathanWright suggests CreateDirectory will fail when there is already a folder. If I read Directory.CreateDirectory it says 'This object is returned regardless of whether a directory at the specified path already exists.' Meaning you do not detect a folder created between check exists and actually creating.

I like the CreateDirectoryTransacted() suggested by @DanielTrebbien but this function is deprecated.

The only solution I see that is left is to use the c api and call the 'CreateDirectory' there as it does error if the folder exists if you really need to be sure to cover the whole race condition. That would result in something like this:

Private Function GetTempFolder() As String
    Dim folder As String
    Dim succes as Boolean = false
    Do While not succes
        folder = Path.Combine(Path.GetTempPath, Path.GetRandomFileName)
        success = c_api_create_directory(folder)
    Loop
    Return folder
End Function
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top