System.IO.IOException:System.IO.Path.GetTempFileName()を使用する場合の「ファイルが存在します」-解像度?


84

私の顧客の1人は、私の製品を使おうとすると例外が発生しました。発生した例外のコールスタックを取得しました。その先頭は次のとおりです。

at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.__Error.WinIOError()
   at System.IO.Path.GetTempFileName()
   at System.Windows.Input.Cursor.LoadFromStream(Stream cursorStream)
   at System.Windows.Input.Cursor..ctor(Stream cursorStream)

これをグーグルで検索すると、%TEMP%フォルダーに65535を超える一時ファイルがある場合にこの例外がスローされ、解決策は古い一時ファイルを単純にクリアすることであると述べているブログ投稿がたくさん見つかりました。お客様にそうするように依頼することはできますが、これは一時的な解決策にすぎない可能性があります。GetTempFileNameを頻繁に呼び出す他のソフトウェアを定期的に実行していると、問題が繰り返し発生します。

%TEMP%フォルダーをプログラムでクリアすることはできません。これは、他の何かに損傷を与える可能性があるためです。また、GetTempFileNameを呼び出す(代わりに独自の一時フォルダーを使用する)のは、私ではなくWPFコードであるため避けられません。

これに対する恒久的な解決策はありますか?

更新:%TEMP%フォルダーがログファイルでオーバーフローする問題は、自分のコードが原因ではなく、お客様のマシン上の他のサードパーティアプリケーションが原因である必要があることを確認しました。の実装についても調べましたがCursor.LoadFromStream、間違いはありません。一時ファイルを生成しますが、finallyブロック内で削除します。


3
あなたは(「アプリケーションデータに)削除されます、あなた自身の「温度」フォルダを作ることができますが、おそらくすべての参照を変更するballache、良い質問だろう
Sayse

質問はWPFに関連しておらず、タグが削除されています。また、削除せずに非常に多くの一時ファイルを生成するコードを修正しないのはなぜですか?
デニス

1
@SayseCursor.LoadFromStream一時ファイルを生成しているのはWPFであるため、これはできません。@DennisこれはWPFのCursor.LoadFromStreamクラスに関連しています。削除せずに非常に多くの一時ファイルを生成する問題のあるコードは、私自身のものではない可能性があり、それでも例外に対処する必要があります。
Omer Raviv 2013

これらすべての一時ファイルを残しているアプリケーションを見つけることができますか?それはあなたのアプリケーションですか?WPFがこれらの一時ファイル自体を作成している場合、それらが不要になったときに削除することを確認しましたか?
アシゴレ2013

2
@OmerRaviv唯一の選択肢は、IOEを試して/キャッチし、一時ファイルを削除して再試行するかどうかをユーザーに尋ねることだと思います
2013

回答:


15

前回のコメントで述べたように、これを行うための唯一の安全な方法は、ファイルを削除して再試行するかどうかをユーザーに尋ねることだと思います。これにユーザーの入力を得ることが不可欠です。このようにすると、ユーザー自身の危険にさらされます。私の頭の中ではそれに似たものです。

public Stream GetStream(Stream cursorStream)
{
    try
    {
       //getting stream
    }
    catch(IOE)
    {
        MessageBox.Show(this, "Unable to get stream, your temporary
                              folder may be full, do you want to try deleting 
                                some and try again?");
         if(yes)
         try
         {
             //delete and try again
             return GetStream(cursorStream);
         }
         catch(IOE)
          {
                //no luck
           }
          else
              return null;
    }

}

確認するためのオプションのチェックは、

Directory.EnumerateFiles(Path.GetTempPath(), "*", SearchOption.TopLevelOnly)
  .Count() == ushort.MaxValue;

2
あなたが提案したように、私はトライ/キャッチで行くつもりです。提案したブールテストは実際には間違っています。GetTempFileName()が使用する「tmpXXXX.tmp」形式のファイル以外に、%TEMP%フォルダーに他のファイルがある可能性が高いため、実際に問題がない場合はテストがtrueを返す可能性があります。問題がある場合はfalse。
Omer Raviv 2013

このテストは、一時フォルダー内のすべてのファイルを検索するように設計されており(ただし、適切な一時フォルダーではない可能性があります)、65535に等しい"*"かどうかを確認し、(拡張子があるかどうかに関係なく)すべてのファイルを検索します。
2013

たとえば、1日より古いファイルをすべて削除できます。1日より古い一時ファイルは、他のアプリケーションで使用される可能性はほとんどありません。
JTテイラー

2
オプションのチェックのもう1つのポイント。ビルドサーバーでこの問題が発生したとき、一時ディレクトリには65535を超えるファイルがありました。このカウントは、一時ファイルを作成する唯一のものがパスヘルパークラスを介している場合にのみ機能します。
rshadman

36

これが本番環境または変更できないアプリで発生している場合、簡単な解決策は一時フォルダーを空にすることです。

アプリケーションを実行しているユーザーに応じて、次のいずれかを行う必要があります

  • C:\Windows\Temp(IISまたはLocalSystemアカウントで実行されているサービスの場合)
  • または%temp%、ローカルにログオンしているユーザーの場合(私にとってはC:\Users\MyUserName\AppData\Local\Temp)。

反対に、独自のコードがこれをスローしていて、これが二度と起こらないようにしたい場合は、次のようにします。

  1. System.IO.Path.GetTempFileName()を使用しないでください!

GetTempFileName()20年前のWin32Apiのラッパーです。非常に簡単に衝突するファイル名を生成します。ファイルシステムを頻繁にループし、可能なファイル名をから"%temp%\tmp0000.tmp"に繰り返し、"tmpFFFF.tmp"既存のファイル名をスキップすることで、これらの衝突を回避します。これは、I / Oを集中的に使用し、低速で、率直に言ってひどいアルゴリズムです。また、4つの16進文字のみを使用すると、失敗する前に65536ファイルが人為的に制限されます。

別の方法は、衝突しないファイル名を生成することです。たとえば、GUID'sロジックを再利用しましょう。32桁の16進数が衝突することはほとんどありません。

private string GetTempFileName()
{
    return Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
}
// Sample: c:\Windows\Temp\2e38fe87-f6bb-4b0d-90b3-2d07016324c1

これにより、制限が最大65kから4k百万ファイルに拡大されます(理論的には)...もちろん、65kファイルをリークしたことはすでにひどいので...

  1. 一時ファイルを漏らさないでください!

すべての幸せなパスと不幸なパス(予期しない例外など)がないかアプリを再確認してください。各FileStreamが正しく破棄され、Finallyブロックの一時ファイルが削除されていることを確認します。

  1. 一時フォルダをクリーンアップします

野生のすべてのアプリを信頼できるわけではないため、今すぐクリーンアップし、システム管理者に定期的にクリーンアップするように教育してください。私自身のサーバーでは、以下を使用してこのタスクを自動化します。

  • グローバルWindows \ Tempの場合

schtasks /Create /TR "cmd /c call DEL /F /S /Q %^TEMP%" /TN "Delete Global Temp Files" /sc WEEKLY /ST 12:00 /ru system

  • 現在のユーザーの場合:

schtasks /Create /TR "cmd /c call DEL /F /S /Q %^TEMP%" /TN "Delete %username% Temp Files" /sc WEEKLY /ST 12:00


5

これが私が最後に使用したコードであり、呼び出しCursor.LoadFromStreamが発生する前に、アプリの初期化コードパスの早い段階で配置しました。

    private void WarnUserIfTempFolderFull()
    {
        string tempFile = null;
        try
        {
            tempFile = Path.GetTempFileName();
        }
        catch (IOException e)
        {
            string problem = "The Temporary Folder is full.";

            string message = "{ProductName} has detected that the Windows Temporary Folder is full. \n" + 
                             "This may prevent the {ProductName} from functioning correctly.\n" + 
                             "Please delete old files in your temporary folder (%TEMP%) and try again.";

            Logger.Warn(problem);

            MessageBox.Show(message, caption: problem);
        }
        finally
        {
            if (tempFile != null) File.Delete(tempFile);
        }
    }

2

ソリューション:

  1. 正しいもの。どのアプリケーションが非常に多くの一時ファイルを生成していて、それらを削除していないかを検出します。のようなユーティリティProcess monitorはあなたを助けるはずです。次に、アプリケーションを修正するか、破棄します。そして、はい、これはあなたのアプリケーションかもしれません。だからこそ、悪の源を見つけることをお勧めします。
  2. 最も簡単なもの。独自の一時ディレクトリを使用してください。ファイルがコードから作成されている場合、これは役に立ちません。
  3. 最も醜いもの。アプリケーションから一時ディレクトリをクリアします。あなたは結果について絶対に正しいです-あなたは別のアプリケーションを壊す可能性があります。

独自の一時ディレクトリを使用することは、必ずしも解決策ではありません。APIを使用してtempfilenameを生成しますが、それを自分のディレクトリに書き込みます。残念ながら、それでも失敗します。
ジョージマウアー2013年

2
// one more implementation
string GetTempFileName()
{
    return Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
}

1

以下のようSayseが提案され、あなたはときにアプリが起動%TEMP%環境変数を設定してみてください。

Environment.SetEnvironmentVariable("TEMP", "<dir>");

これは素晴らしいアイデアですが、残念ながら私のアプリはVisual Studio拡張機能であり、他の拡張機能と平和的に共存する必要があります。これにより、他の拡張機能の動作が不注意に損なわれる可能性があります。
Omer Raviv 2013

これらすべてのファイルを残しているのが彼自身のプログラムである場合、このソリューションは役に立ちません。新しいディレクトリがいっぱいになるだけで、アプリケーションに固有のフォルダであっても、ファイルの目的がわからない場合は、フォルダからファイルを任意に削除するのはよくありません。
アシゴレ2013

@Ashigoreはい、明らかにこれは彼が作成したバグを修正しません。具体的に参照しwhat if they are regularly running some other piece of software that makes frequent calls to GetTempFileNameます。
エドチャペル

@OmerRavivこの情報は役に立ちます。実際、これはこのシナリオでは機能しません。
エドチャペル

1
申し訳ありませんが@ EdChapel、SOが私の投票をロックしました。回答が編集されない限り、投票を削除することはできません。
Gerardo Grignoli 2018年

1

この問題が発生し、オーバーフローしている一時フォルダーが見つからない場合は、「C:/ Windows / Temp」フォルダーを確認してください。このフォルダをクリーンアップすると、問題が解決しました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.