知っていたはずなのに、また引っかかってしまったから、戒めを込めてメモ。
C#.NETにおけるFileDialogがカレントディレクトリを変更するバグ(?)
C#.NETのSystem.Windows.Forms.OpenFileDialogかSaveFileDialogを使用して、その中でディレクトリの移動操作などをすると、プログラム自体のカレントディレクトリが移動することがある。私は明らかにバグではないかと思っているのだが、もしかしたら仕様なのかもしれない。どのような場面で、このような事態が発生するのか例を出してみる。
異常を発生させる最小のプログラム例
class Program { const string filename = "test.txt"; [System.STAThread] static void Main(string[] args) { System.Windows.Forms.OpenFileDialog ofd = new System.Windows.Forms.OpenFileDialog(); ofd.ShowDialog(); //ダイアログボックス内でディレクトリを変える System.IO.StreamReader sr = new System.IO.StreamReader(filename); System.Console.WriteLine(sr.ReadLine()); System.Console.ReadLine(); } }
「開く」を押した場合のみ、カレントディレクトリは変更される。つまり、その場合だけ、StreamReader部分で例外(FileNotFoundException)が発生する。
解決法
変更される前のカレントディレクトリを保存しておく
class Program { const string filename = "test.txt"; [System.STAThread] static void Main(string[] args) { string current = System.IO.Directory.GetCurrentDirectory(); System.Windows.Forms.SaveFileDialog ofd = new System.Windows.Forms.SaveFileDialog(); ofd.ShowDialog(); System.IO.Directory.SetCurrentDirectory(current); //ダイアログボックス内でディレクトリを変える System.IO.StreamReader sr = new System.IO.StreamReader(filename); System.Console.WriteLine(sr.ReadLine()); System.Console.ReadLine(); } }
少し汚いが、こういう方法も有効だ。
プログラムの実行パスを取得する
class Program { static string filename = System.IO.Path.Combine(System.Windows.Forms.Application.StartupPath, "test.txt"); [System.STAThread] static void Main(string[] args) { System.Windows.Forms.SaveFileDialog ofd = new System.Windows.Forms.SaveFileDialog(); ofd.ShowDialog(); //ダイアログボックス内でディレクトリを変える System.IO.StreamReader sr = new System.IO.StreamReader(filename); System.Console.WriteLine(sr.ReadLine()); System.Console.ReadLine(); } }
そもそも、実行ファイルのパスを取得してしまおうという技。constでなくなり、Mainと同じ静的にしなければならない点にだけ注意。本来ならば、「変更される前のカレントディレクトリを保存しておく」方法よりは、こちらの方が応用性はありそうで、こちらを使うべきだろう。
例示されたコードでは確かめてませんが…
OpenFileDialog/SaveFileDialog の RestoreDirectory プロパティを true にしてから、ShowDialog() すれば、カレントディレクトリは移動しない筈です。