質問
アプリケーション用に複数の一時フォルダーを作成する必要があるプログラムに取り組んでいます。これらはユーザーには表示されません。アプリは VB.net で書かれています。増分フォルダー名やランダムな番号のフォルダー名など、いくつかの方法が思いつきますが、他の人はこの問題をどのように解決しているのでしょうか?
解決
アップデート: コメントごとに 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\ユーザー名\Local 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\ユーザー名\Local Settings emp\2dbc6db7-2d45-4b75-b27f-0bd492c60496
他のヒント
使用する必要があります System.IO.Path.GetTempFileName()
一意の名前が付いたゼロバイトの一時ファイルをディスク上に作成し、そのファイルの完全パスを返します。
使用できます System.IO.Path.GetDirectoryName(System.IO.Path.GetTempFileName())
一時フォルダーの情報のみを取得し、そこにフォルダーを作成します
これらは 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 シェル スクリプトの世界では、競合のない安全な方法で一時ファイルとディレクトリを作成することは重要です。多くのマシンには複数の (敵意のある) ユーザーが存在し (共有 Web ホストを考えてください)、多くのスクリプトとアプリケーションは共有の /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を読むと、「指定されたパスのディレクトリが既に存在するかどうかに関係なく、このオブジェクトは返されます」と表示されます。つまり、チェックと実際に作成される間に作成されたフォルダーを検出しないことを意味します。
@DanielTrebbien が提案した CreateDirectoryTransacted() が気に入っていますが、この関数は非推奨です。
残っている唯一の解決策は、c API を使用して 'ディレクトリの作成' 競合状態全体を確実にカバーする必要がある場合は、フォルダーが存在するとエラーが発生します。その結果、次のような結果になります。
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