Вопрос

Я работаю над программой, которая должна создать несколько временных папок для приложения.Они не будут видны пользователю.Приложение написано на VB.net.Я могу придумать несколько способов сделать это, например, путем увеличения имени папки или имен папок со случайным номером, но мне было интересно, как другие люди решают эту проблему?

Это было полезно?

Решение

Обновлять: Добавлена ​​проверка File.Exists для каждого комментария (19 июня 2012 г.).

Вот что я использовал в VB.NET.По сути то же самое, что и представлено, за исключением того, что обычно я не хотел создавать папку сразу.

Преимущество использования Получить случайное имя файла заключается в том, что он не создает файл, поэтому вам не нужно очищать его, если вы используете имя для чего-то другого, кроме файла.Например, использовать его для имени папки.

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

Случайный Пример имени файла:

C:\Documents and Settings\имя пользователя\Локальные настройки emp\u3z5e0co.tvq


Вот вариант использования Guid для получения имени временной папки.

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

гид Пример:

C:\Documents and Settings\имя пользователя\Локальные настройки emp\2dbc6db7-2d45-4b75-b27f-0bd492c60496

Другие советы

Вы должны использовать System.IO.Path.GetTempFileName()

Создает на диске временный файл с уникальным именем и нулевым размером байта и возвращает полный путь к этому файлу.

Вы можете использовать System.IO.Path.GetDirectoryName(System.IO.Path.GetTempFileName()) чтобы получить только информацию о временной папке и создать там свои папки

Они создаются в папке temp Windows, и это считается лучшей практикой.

Просто для уточнения:

System.IO.Path.GetTempPath()

возвращает только путь к временной папке.

System.IO.Path.GetTempFileName()

возвращает полное имя файла (включая путь), поэтому:

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

является излишним.

Возможна ситуация гонки, когда:

  • создание временного файла с помощью GetTempFileName(), удалив его и создав папку с таким же именем, или
  • с использованием GetRandomFileName() или Guid.NewGuid.ToString чтобы назвать папку и создать папку позже

С GetTempFileName() после того, как произойдет удаление, другое приложение может успешно создать временный файл с тем же именем.А CreateDirectory() тогда потерпит неудачу.

Аналогично, между вызовами GetRandomFileName() и создав каталог, другой процесс может создать файл или каталог с тем же именем, что снова приведет к CreateDirectory() неудачно.

Для большинства приложений вполне нормально, что временный каталог выходит из строя из-за состояния гонки.Ведь это крайне редко.Для них эти расы часто можно игнорировать.

В мире сценариев оболочки Unix создание временных файлов и каталогов безопасным способом без гонок имеет большое значение.На многих машинах есть несколько (враждебных) пользователей (например, общий веб-хост), и многим сценариям и приложениям необходимо безопасно создавать временные файлы и каталоги в общем каталоге /tmp.Видеть Безопасное создание временных файлов в сценариях оболочки для обсуждения того, как безопасно создавать временные каталоги из сценариев оболочки.

Как @JonathanWright указал, для решений существуют условия гонки:

  • Создайте временный файл с GetTempFileName(), удалите его и создайте папку с таким же именем
  • Использовать GetRandomFileName() или Guid.NewGuid.ToString чтобы создать папку со случайным именем, проверьте, существует ли она, и создайте ее, если нет.

Однако можно создать уникальный временный каталог атомарно, используя команду Транзакционная NTFS (TxF) API.

TxF имеет CreateDirectoryTransacted() функция, которую можно вызвать через Platform Invoke.Для этого я адаптировал Кодекс Мохаммеда Эльшейми для звонка 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());
}

Что-то вроде...

using System.IO;

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

Directory.CreateDirectory(path);

Вы можете создать GUID для имен временных папок.

Поскольку имя папки не обязательно должно иметь смысл, как насчет использования для нее GUID?

Вы можете использовать GetTempFileName создать временный файл, затем удалите и заново создайте этот файл как каталог.

Примечание:ссылка не работает, скопируйте/вставьте из: http://msdn.microsoft.com/en-us/library/aa364991(VS.85).aspx

Комбинированные ответы @adam-wright и pix0r будут работать лучше всего, ИМХО:


using System.IO;

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

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

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

Преимущество использования System.IO.Path.GetTempFileName заключается в том, что это будет файл в локальном (т. е. не перемещаемом) пути пользователя.Это именно то место, где вам нужно это из соображений разрешений и безопасности.

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

@JonathanWright предполагает, что CreateDirectory завершится неудачно, если папка уже существует.Если я читаю Directory.createdIrectory, он говорит: «Этот объект возвращается независимо от того, существует ли каталог на указанном пути уже». Это означает, что вы не обнаруживаете папку, созданную между проверкой, существует и фактически создание.

Мне нравится CreateDirectoryTransacted(), предложенный @DanielTrebbien, но эта функция устарела.

Единственное решение, которое я вижу, - это использовать capi и вызвать 'Создать каталог' там, как и ошибка, если папка существует, если вам действительно нужно обязательно охватить все состояние гонки.Это приведет к чему-то вроде этого:

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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top