31

アプリケーション用に複数の一時フォルダーを作成する必要があるプログラムに取り組んでいます。これらはユーザーには表示されません。アプリは VB.net で書かれています。インクリメンタルなフォルダー名やランダムな番号のフォルダー名など、いくつかの方法を考えることができますが、他の人がこの問題をどのように解決するのか疑問に思っていました。

4

13 に答える 13

29

更新:コメントごとに File.Exists チェックを追加 (2012-Jun-19)

これが私がVB.NETで使用したものです。基本的に提示されたものと同じですが、通常はフォルダーをすぐに作成したくありませんでした。

GetRandomFilenameを使用する利点は、ファイルを作成しないことです。そのため、ファイル以外の名前を使用している場合、クリーンアップする必要はありません。フォルダ名に使用するようなものです。

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\username\Local Settings\Temp\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\username\Local Settings\Temp\2dbc6db7-2d45-4b75-b27f-0bd492c60496

于 2008-08-19T19:30:17.737 に答える
20

使用する必要がありますSystem.IO.Path.GetTempFileName()

ディスク上に一意の名前の 0 バイトの一時ファイルを作成し、そのファイルの完全なパスを返します。

System.IO.Path.GetDirectoryName(System.IO.Path.GetTempFileName())一時フォルダー情報のみを取得し、そこにフォルダーを作成するために使用できます

これらは Windows temp フォルダーに作成され、ベスト プラクティスと見なされます。

于 2008-08-19T18:19:31.480 に答える
8

次の場合に競合状態が発生する可能性があります。

  • で一時ファイルを作成してGetTempFileName()削除し、同じ名前のフォルダーを作成する、または
  • GetRandomFileName()またはを使用しGuid.NewGuid.ToStringてフォルダーに名前を付け、後でフォルダーを作成する

GetTempFileName()削除が行われた後、別のアプリケーションが同じ名前の一時ファイルを正常に作成できました。はCreateDirectory()失敗します。

同様に、GetRandomFileName()ディレクトリの呼び出しと作成の間に、別のプロセスが同じ名前のファイルまたはディレクトリを作成し、再びCreateDirectory()失敗する可能性があります。

ほとんどのアプリケーションでは、競合状態が原因で一時ディレクトリが失敗しても問題ありません。結局のところ、それは非常にまれです。彼らにとって、これらの人種はしばしば無視されます。

Unix シェル スクリプトの世界では、競合のない安全な方法で一時ファイルとディレクトリを作成することは重要です。多くのマシンには複数の (悪意のある) ユーザー (共有 Web ホストを考えてください) があり、多くのスクリプトとアプリケーションは共有 /tmp ディレクトリに一時ファイルとディレクトリを安全に作成する必要があります。シェル スクリプトから一時ディレクトリを安全に作成する方法については、「シェル スクリプトで一時ファイルを安全に作成する」を参照してください。

于 2008-10-20T00:02:07.367 に答える
8

明確にするために:

System.IO.Path.GetTempPath()

一時フォルダーへのフォルダー パスのみを返します。

System.IO.Path.GetTempFileName()

完全修飾ファイル名 (パスを含む) を返すため、次のようになります。

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

冗長です。

于 2008-08-21T18:24:07.603 に答える
6

@JonathanWrightが指摘したように、ソリューションには競合状態が存在します。

  • で一時ファイルを作成してGetTempFileName()削除し、同じ名前のフォルダを作成します
  • GetRandomFileName()またはを使用Guid.NewGuid.ToStringしてランダムなフォルダ名を作成し、存在するかどうかを確認し、存在しない場合は作成します。

ただし、 Transactional NTFS(TxF)APIを利用して、一意の一時ディレクトリをアトミックに作成することは可能です。

TxFには、CreateDirectoryTransacted()PlatformInvokeを介して呼び出すことができる関数があります。これを行うために、私はMohammadElsheimyのコードを呼び出し用に適合させました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());
}
于 2012-01-05T22:45:28.930 に答える
3

何かのようなもの...

using System.IO;

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

Directory.CreateDirectory(path);
于 2008-08-19T18:28:30.590 に答える
2

一時フォルダー名の GUID を生成できます。

于 2008-08-19T18:18:12.873 に答える
1

GetTempFileNameを使用して一時ファイルを作成し、このファイルを削除して代わりにディレクトリとして再作成できます。

注: リンクが機能しませんでした。コピーして貼り付けてください: http://msdn.microsoft.com/en-us/library/aa364991(VS.85).aspx

于 2008-08-19T18:19:03.557 に答える
1

@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);
于 2008-08-19T18:35:49.357 に答える
1

フォルダの名前が意味のあるものである必要がない限り、それらに GUID を使用するのはどうですか?

于 2008-08-19T18:18:02.530 に答える
0

System.IO.Path.GetTempFileName を使用する利点は、ユーザーのローカル (つまり、非ローミング) パス内のファイルになることです。これは、アクセス許可とセキュリティ上の理由から、まさに必要な場所です。

于 2008-08-19T18:39:10.927 に答える
0

@JonathanWright は、既にフォルダーがある場合に CreateDirectory が失敗することを示唆しています。Directory.CreateDirectory を読むと、「指定されたパスにディレクトリが既に存在するかどうかに関係なく、このオブジェクトが返されます」と表示されます。つまり、チェックが存在してから実際に作成するまでの間に作成されたフォルダーを検出しません。

@DanielTrebbien によって提案された CreateDirectoryTransacted() が好きですが、この関数は非推奨です。

残されている唯一の解決策は、競合状態全体を確実にカバーする必要がある場合、フォルダーが存在する場合にエラーが発生するため、c API を使用して「CreateDirectory 」を呼び出すことです。それは次のような結果になります:

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
于 2016-09-13T10:20:37.293 に答える