C#でカレントディレクトリを取得・変更する方法|CurrentDirectoryの使い方と注意点

はじめに

C#でファイルを読み書きするときに、data.txtlogs/app.log のような相対パスを使うと、「どのフォルダを基準にしているのか」が重要になります。その基準になるのがカレントディレクトリ、つまり現在の作業ディレクトリです。

C#では、カレントディレクトリを取得する代表的な方法として Environment.CurrentDirectoryDirectory.GetCurrentDirectory() があります。また、Environment.CurrentDirectory への代入や Directory.SetCurrentDirectory() によって、実行中にカレントディレクトリを変更することもできます。Microsoftのドキュメントでも、Environment.CurrentDirectory は「現在の作業ディレクトリの完全修飾パスを取得または設定するプロパティ」、Directory.GetCurrentDirectory() は「アプリケーションの現在の作業ディレクトリを取得するメソッド」と説明されています。Microsoft Learn+1

この記事では、C#の current directory を取得・変更する基本から、AppContext.BaseDirectory など混同しやすいパスとの違い、実務で安全に扱うための注意点まで解説します。

1. C#のカレントディレクトリとは

1-1. カレントディレクトリの意味

カレントディレクトリとは、プログラムが現在の作業場所として扱っているディレクトリのことです。英語では current directorycurrent working directory と呼ばれます。

C#でファイルパスを指定するとき、C:\work\data.txt のような絶対パスではなく、data.txt のような相対パスを指定した場合、その相対パスはカレントディレクトリを基準に解決されます。

たとえば、カレントディレクトリが次の場所だったとします。

C#
C:\Users\sample\Documents\App

この状態で次のようにファイルを読み込むとします。

C#
var text = File.ReadAllText("data.txt");

この場合、C#は実質的に次のファイルを探します。

C#
C:\Users\sample\Documents\App\data.txt

つまり、カレントディレクトリは「相対パスの出発地点」と考えると理解しやすいです。

1-2. 相対パスの基準になるディレクトリ

相対パスは、現在位置から見たファイルやフォルダの場所を表します。Microsoftのパス形式の説明でも、2018\January.xlsx のようなパスは現在のディレクトリ内のサブディレクトリを指す相対パス、..\Publications\TravelBrochure.pdf は現在のディレクトリを基準に親ディレクトリ側へ移動する相対パスとして扱われます。Microsoft Learn

C#でよく使う相対パスには、次のようなものがあります。

C#
"data.txt"              // カレントディレクトリ直下の data.txt
"files/data.txt" // カレントディレクトリ配下の files フォルダ内
"../data.txt" // カレントディレクトリの1つ上の階層
"./data.txt" // カレントディレクトリ直下

相対パスは短く書けて便利ですが、カレントディレクトリが変わると参照先も変わります。そのため、C#でファイルが見つからないエラーが出る場合は、まず CurrentDirectory がどこになっているかを確認するのが基本です。

1-3. 実行ファイルの場所との違い

カレントディレクトリは、必ずしも実行ファイル、つまり .exe やアプリケーション本体が置かれているフォルダと同じではありません。

たとえば、アプリの実行ファイルが次の場所にあるとします。

C#
C:\Apps\MyApp\MyApp.exe

しかし、コマンドプロンプトで次のように別の場所から起動した場合、

cmd
C:\Work> C:\Apps\MyApp\MyApp.exe

カレントディレクトリは C:\Work になる可能性があります。実行ファイルの場所は C:\Apps\MyApp ですが、相対パスの基準は C:\Work になるということです。

実行ファイルの配置場所を取得したい場合は、Environment.CurrentDirectory ではなく、AppContext.BaseDirectory などを使うほうが適している場面があります。AppContext.BaseDirectory は、アセンブリを探すために使われるベースディレクトリを返すプロパティです。Microsoft Learn

1-4. アプリの種類によって変わる注意点

カレントディレクトリは、アプリの種類や起動方法によって変わります。

コンソールアプリでは、コマンドを実行した場所がカレントディレクトリになりやすいです。一方、Windows FormsやWPFでは、ショートカットの作業フォルダや起動方法の影響を受けることがあります。ASP.NET Coreでは、ファイルの基準として ContentRootPathWebRootPath を使う場面が多く、単純に Directory.GetCurrentDirectory() へ依存すると環境差が出ることがあります。

そのため、C#で current directory を使うときは、「今どこを指しているか」だけでなく、「なぜその場所になっているか」まで意識することが大切です。

2. C#でカレントディレクトリを取得する方法

2-1. Environment.CurrentDirectoryで取得する

C#でカレントディレクトリを取得する最もシンプルな方法は、Environment.CurrentDirectory を使う方法です。

C#
using System;

class Program
{
static void Main()
{
string currentDirectory = Environment.CurrentDirectory;
Console.WriteLine(currentDirectory);
}
}

Environment.CurrentDirectory はプロパティなので、メソッドのように () を付けません。現在の作業ディレクトリを文字列として取得できます。

Microsoftのドキュメントでは、Environment.CurrentDirectory は現在の作業ディレクトリの完全修飾パスを取得または設定すると説明されています。つまり、取得だけでなく変更にも使えるプロパティです。Microsoft Learn

2-2. Directory.GetCurrentDirectoryで取得する

もう1つの代表的な方法が、Directory.GetCurrentDirectory() を使う方法です。

C#
using System;
using System.IO;

class Program
{
static void Main()
{
string currentDirectory = Directory.GetCurrentDirectory();
Console.WriteLine(currentDirectory);
}
}

Directory.GetCurrentDirectory()System.IO 名前空間にあるメソッドです。戻り値は、現在の作業ディレクトリを表す絶対パスです。Microsoftのドキュメントでは、戻り値は現在の作業ディレクトリの絶対パスであり、末尾にバックスラッシュは付かないと説明されています。Microsoft Learn

2-3. 取得結果をConsole.WriteLineで確認する

カレントディレクトリに関するトラブルでは、まず実際の値を表示して確認するのが効果的です。

C#
using System;
using System.IO;

class Program
{
static void Main()
{
Console.WriteLine("Environment.CurrentDirectory:");
Console.WriteLine(Environment.CurrentDirectory);

Console.WriteLine();

Console.WriteLine("Directory.GetCurrentDirectory():");
Console.WriteLine(Directory.GetCurrentDirectory());
}
}

多くの場合、両方の結果は同じになります。ファイルが見つからないときは、思い込みでパスを修正する前に、このように現在のカレントディレクトリを出力して確認しましょう。

2-4. 2つの取得方法の違い

Environment.CurrentDirectoryDirectory.GetCurrentDirectory() は、どちらも現在の作業ディレクトリを取得できます。

大きな違いは、Environment.CurrentDirectory は取得と変更の両方ができるプロパティであり、Directory.GetCurrentDirectory() は取得専用のメソッドである点です。

C#
// 取得
string path1 = Environment.CurrentDirectory;
string path2 = Directory.GetCurrentDirectory();

// 変更
Environment.CurrentDirectory = @"C:\Temp";
Directory.SetCurrentDirectory(@"C:\Temp");

読みやすさを重視するなら、取得だけなら Directory.GetCurrentDirectory()、取得と変更をセットで扱うなら Environment.CurrentDirectory を使う、という考え方ができます。

3. C#でカレントディレクトリを変更する方法

3-1. Environment.CurrentDirectoryに代入して変更する

Environment.CurrentDirectory はプロパティなので、値を代入することでカレントディレクトリを変更できます。

C#
using System;

class Program
{
static void Main()
{
Console.WriteLine("変更前:");
Console.WriteLine(Environment.CurrentDirectory);

Environment.CurrentDirectory = @"C:\Temp";

Console.WriteLine("変更後:");
Console.WriteLine(Environment.CurrentDirectory);
}
}

このコードを実行すると、カレントディレクトリが C:\Temp に変更されます。ただし、指定したフォルダが存在しない場合や権限がない場合は例外が発生する可能性があります。Environment.CurrentDirectory では、空文字、null、見つからないローカルパスなどを設定しようとした場合の例外が定義されています。Microsoft Learn

3-2. Directory.SetCurrentDirectoryで変更する

Directory.SetCurrentDirectory() を使って変更することもできます。

C#
using System;
using System.IO;

class Program
{
static void Main()
{
Directory.SetCurrentDirectory(@"C:\Temp");
Console.WriteLine(Directory.GetCurrentDirectory());
}
}

Microsoftのドキュメントでは、Directory.SetCurrentDirectory(String) はアプリケーションの現在の作業ディレクトリを指定したディレクトリに設定するメソッドと説明されています。Microsoft Learn

3-3. 存在するフォルダに変更するサンプルコード

実務では、変更先のフォルダが存在するか確認してから変更する書き方が安全です。

C#
using System;
using System.IO;

class Program
{
static void Main()
{
string targetDirectory = @"C:\Temp";

if (Directory.Exists(targetDirectory))
{
Directory.SetCurrentDirectory(targetDirectory);
Console.WriteLine("カレントディレクトリを変更しました。");
Console.WriteLine(Directory.GetCurrentDirectory());
}
else
{
Console.WriteLine("指定したフォルダが存在しません。");
Console.WriteLine(targetDirectory);
}
}
}

Directory.Exists() で存在確認をしておくと、存在しないフォルダを指定して DirectoryNotFoundException が発生するケースを減らせます。

3-4. 存在しないパスを指定した場合のエラー

存在しないパスにカレントディレクトリを変更しようとすると、DirectoryNotFoundException が発生します。Directory.SetCurrentDirectory() のドキュメントでも、指定したディレクトリが見つからない場合に DirectoryNotFoundException が発生すると説明されています。Microsoft Learn

C#
using System;
using System.IO;

class Program
{
static void Main()
{
try
{
Directory.SetCurrentDirectory(@"C:\NotExistsFolder");
}
catch (DirectoryNotFoundException ex)
{
Console.WriteLine("指定したフォルダが見つかりません。");
Console.WriteLine(ex.Message);
}
}
}

ファイル操作の前にパスを確認する、例外を捕捉する、設定値に誤りがないか検証する、といった対策が重要です。

4. CurrentDirectoryの基本的な使い方

4-1. ファイル読み込みで相対パスを使う例

カレントディレクトリは、ファイル読み込み時の相対パスに影響します。

C#
using System;
using System.IO;

class Program
{
static void Main()
{
string text = File.ReadAllText("data.txt");
Console.WriteLine(text);
}
}

このコードでは、data.txt はカレントディレクトリ直下にあるものとして扱われます。ファイルが見つからない場合は、data.txt がソースコードの横にあるかではなく、実行時のカレントディレクトリ直下にあるかを確認する必要があります。

4-2. ファイル書き込みで相対パスを使う例

書き込みでも同じです。

C#
using System.IO;

class Program
{
static void Main()
{
File.WriteAllText("output.txt", "Hello, C#!");
}
}

このコードを実行すると、output.txt はカレントディレクトリ直下に作成されます。Visual Studioでデバッグしている場合と、exeを直接実行した場合で出力先が異なることがあるため、出力先を確認したい場合は Directory.GetCurrentDirectory() を表示しましょう。

4-3. Path.Combineと組み合わせる方法

ファイルパスを組み立てるときは、文字列連結よりも Path.Combine() を使うのがおすすめです。

C#
using System;
using System.IO;

class Program
{
static void Main()
{
string currentDirectory = Directory.GetCurrentDirectory();
string filePath = Path.Combine(currentDirectory, "data", "sample.txt");

Console.WriteLine(filePath);
}
}

Path.Combine() を使うと、区切り文字の付け忘れや重複を避けやすくなります。

避けたい書き方は次のような文字列連結です。

C#
string filePath = currentDirectory + "\\data\\sample.txt";

Windowsだけで動かす小さなツールなら問題になりにくい場合もありますが、保守性やクロスプラットフォーム対応を考えると、Path.Combine() を使うほうが安全です。

4-4. 絶対パスと相対パスの使い分け

絶対パスは、ドライブやルートから始まる完全な場所を表します。

C#
C:\Apps\MyApp\data.txt

相対パスは、カレントディレクトリを基準にした場所を表します。

C#
data.txt
data\sample.txt
..\config.json

相対パスは簡潔ですが、カレントディレクトリに依存します。設定ファイル、ログファイル、テンプレートファイルなど、確実に場所を決めたいファイルでは、AppContext.BaseDirectory や設定値から絶対パスを組み立てるほうが実務向きです。

5. Environment.CurrentDirectoryとDirectory.GetCurrentDirectoryの違い

5-1. 取得専用か取得・変更できるかの違い

Environment.CurrentDirectory は、現在の作業ディレクトリを取得できるだけでなく、代入によって変更できます。

C#
string current = Environment.CurrentDirectory;
Environment.CurrentDirectory = @"C:\Temp";

一方、Directory.GetCurrentDirectory() は取得専用です。

C#
string current = Directory.GetCurrentDirectory();

変更する場合は、Directory.SetCurrentDirectory() を使います。

C#
Directory.SetCurrentDirectory(@"C:\Temp");

Environment.CurrentDirectory は取得と設定ができるプロパティ、Directory.GetCurrentDirectory() は取得メソッド、Directory.SetCurrentDirectory() は設定メソッド、と整理すると分かりやすいです。Microsoft Learn+2Microsoft Learn+2

5-2. Directory.SetCurrentDirectoryとの関係

Directory.GetCurrentDirectory()Directory.SetCurrentDirectory() は対になるメソッドです。

C#
string before = Directory.GetCurrentDirectory();

Directory.SetCurrentDirectory(@"C:\Temp");

string after = Directory.GetCurrentDirectory();

Environment.CurrentDirectory を使った場合も、結果的には同じ現在の作業ディレクトリを扱います。

C#
string before = Environment.CurrentDirectory;

Environment.CurrentDirectory = @"C:\Temp";

string after = Environment.CurrentDirectory;

チーム開発では、取得は Directory.GetCurrentDirectory()、変更は Directory.SetCurrentDirectory() のように役割が明確なメソッドを使うと、コードの意図が伝わりやすくなります。

5-3. どちらを使うべきか

基本的には、次のように使い分けるとよいでしょう。

取得だけなら、次の書き方が分かりやすいです。

C#
string currentDirectory = Directory.GetCurrentDirectory();

一時的に取得・変更をまとめて扱いたい場合は、次の書き方も自然です。

C#
string oldDirectory = Environment.CurrentDirectory;
Environment.CurrentDirectory = newDirectory;

ただし、カレントディレクトリの変更はプロセス全体に影響するため、安易に変更しないほうが安全です。特にライブラリや共通処理の中で変更すると、呼び出し元の相対パス処理に影響する可能性があります。

5-4. 実務で使いやすい書き方

実務では、カレントディレクトリを変更するよりも、基準ディレクトリを変数として持ち、Path.Combine() で絶対パスを作る書き方がおすすめです。

C#
using System;
using System.IO;

class Program
{
static void Main()
{
string baseDirectory = AppContext.BaseDirectory;
string configPath = Path.Combine(baseDirectory, "config", "settings.json");

string json = File.ReadAllText(configPath);
Console.WriteLine(json);
}
}

この書き方なら、カレントディレクトリがどこであっても、アプリのベースディレクトリを基準にファイルを探せます。

6. カレントディレクトリと混同しやすいパス

6-1. AppDomain.CurrentDomain.BaseDirectoryとの違い

AppDomain.CurrentDomain.BaseDirectory は、現在のアプリケーションドメインでアセンブリを探すためのベースディレクトリを取得します。Microsoftのドキュメントでも、アセンブリリゾルバーがアセンブリを探すために使うベースディレクトリを取得すると説明されています。Microsoft Learn

C#
string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
Console.WriteLine(baseDirectory);

Environment.CurrentDirectory は作業ディレクトリ、AppDomain.CurrentDomain.BaseDirectory はアプリケーションのベースディレクトリ、という違いがあります。

6-2. AppContext.BaseDirectoryとの違い

AppContext.BaseDirectory も、アセンブリを探すためのベースディレクトリを取得するプロパティです。AppContext.BaseDirectoryAppDomain.BaseDirectory に対応する値であり、.NET 5以降の単一ファイルアプリではホスト実行可能ファイルを含むディレクトリを返すと説明されています。Microsoft Learn

C#
string baseDirectory = AppContext.BaseDirectory;
Console.WriteLine(baseDirectory);

.NETの新しいアプリでは、実行ファイル基準のパスを扱うときに AppContext.BaseDirectory が使いやすいです。

6-3. Assembly.GetExecutingAssembly().Locationとの違い

Assembly.GetExecutingAssembly().Location は、実行中のアセンブリファイルの場所を取得するために使われます。

C#
using System;
using System.IO;
using System.Reflection;

class Program
{
static void Main()
{
string location = Assembly.GetExecutingAssembly().Location;
string? directory = Path.GetDirectoryName(location);

Console.WriteLine(location);
Console.WriteLine(directory);
}
}

ただし、Assembly.Location は常に実行ファイルのディレクトリ取得に最適とは限りません。Microsoftのドキュメントでは、Assembly.Location はマニフェストを含む読み込まれたファイルのフルパスまたはUNCの場所を返す一方、.NET 5以降のバンドルされたアセンブリでは空文字を返すと説明されています。Microsoft Learn

そのため、単にアプリの配置フォルダを知りたい場合は、AppContext.BaseDirectory のほうが扱いやすいケースがあります。

6-4. 実行ファイルのディレクトリを取得したい場合

実行ファイルのディレクトリを基準に設定ファイルやテンプレートを読み込みたい場合は、次のように書けます。

C#
using System;
using System.IO;

class Program
{
static void Main()
{
string appDirectory = AppContext.BaseDirectory;
string templatePath = Path.Combine(appDirectory, "Templates", "mail.txt");

Console.WriteLine(templatePath);
}
}

Environment.CurrentDirectory は起動場所によって変わる可能性があるため、「アプリと同じフォルダにあるファイル」を読みたい場合には向いていないことがあります。

6-5. プロジェクトフォルダを取得したい場合

開発中に「プロジェクト直下のファイルを読みたい」と考えることがあります。しかし、実行時のカレントディレクトリはプロジェクトフォルダとは限りません。ビルド後の出力先、Visual Studioのデバッグ設定、実行方法によって変わるためです。

プロジェクト直下のファイルを実行時に使いたい場合は、ファイルのプロパティで出力ディレクトリへコピーする設定にするか、.csproj でコピー設定を追加し、実行時は AppContext.BaseDirectory から参照するのが実務では扱いやすいです。

XML
<ItemGroup>
<None Update="settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

そのうえで、C#側では次のように読み込みます。

C#
string path = Path.Combine(AppContext.BaseDirectory, "settings.json");
string json = File.ReadAllText(path);

7. アプリケーション別のカレントディレクトリの注意点

7-1. コンソールアプリの場合

コンソールアプリでは、起動した場所がカレントディレクトリになることが多いです。

cmd
C:\Work> MyApp.exe

この場合、Directory.GetCurrentDirectory()C:\Work を返す可能性があります。

一方で、次のように別フォルダのexeを起動しても、

cmd
C:\Work> C:\Apps\MyApp\MyApp.exe

カレントディレクトリは C:\Work のままになることがあります。実行ファイルの場所とカレントディレクトリは別物です。

7-2. Windows Forms・WPFの場合

Windows FormsやWPFでは、ショートカットの「作業フォルダー」、Visual Studioのデバッグ設定、ユーザーの起動方法によってカレントディレクトリが変わることがあります。

そのため、画面アプリで設定ファイルや画像ファイルを読み込む場合に、次のような相対パスへ依存するのは注意が必要です。

C#
var imagePath = "Images/logo.png";

より安全にするなら、アプリのベースディレクトリを基準にします。

C#
var imagePath = Path.Combine(AppContext.BaseDirectory, "Images", "logo.png");

7-3. ASP.NET Coreの場合

ASP.NET Coreでは、カレントディレクトリよりも、IHostEnvironment.ContentRootPathIWebHostEnvironment.WebRootPath を使うほうが適している場面が多いです。

ContentRootPath はホストがコンテンツファイルを探し始める場所を表し、ASP.NET CoreのGeneric Hostのドキュメントでも、IHostEnvironment から ApplicationNameEnvironmentNameContentRootPath などの設定情報を取得できると説明されています。また、Content rootはホストがコンテンツファイルの検索を開始する場所です。Microsoft Learn

C#
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using System.IO;

public class HomeController : Controller
{
private readonly IWebHostEnvironment _env;

public HomeController(IWebHostEnvironment env)
{
_env = env;
}

public IActionResult Index()
{
string path = Path.Combine(_env.ContentRootPath, "Data", "sample.json");
return Content(path);
}
}

ASP.NET Coreでファイルを扱う場合は、Directory.GetCurrentDirectory() に直接依存するより、アプリケーションの設計上の基準ディレクトリを使うほうが安全です。

7-4. Visual Studioでデバッグ実行する場合

Visual Studioでデバッグ実行すると、カレントディレクトリはプロジェクト設定や起動プロファイルの影響を受けます。思っている場所と違う場合があるため、次のコードで確認すると確実です。

C#
Console.WriteLine("CurrentDirectory:");
Console.WriteLine(Directory.GetCurrentDirectory());

Console.WriteLine("BaseDirectory:");
Console.WriteLine(AppContext.BaseDirectory);

Visual Studio上ではファイルが見つかるのに、発行後やexe直接実行では見つからない場合、カレントディレクトリの違いが原因になっていることがあります。

7-5. exeを直接実行する場合

exeをダブルクリックで起動した場合、カレントディレクトリは実行ファイルの場所になることもありますが、常にそうとは限りません。ショートカット経由で起動する場合は、ショートカットの作業フォルダが影響することがあります。

そのため、exeと同じ場所にあるファイルを読むなら、次のように AppContext.BaseDirectory を基準にするのが安全です。

C#
string filePath = Path.Combine(AppContext.BaseDirectory, "config.json");

8. カレントディレクトリ変更時の注意点

8-1. プロセス全体に影響する

カレントディレクトリは、基本的にプロセス全体で共有される作業ディレクトリです。ある処理で変更すると、その後の相対パス処理にも影響します。

C#
Directory.SetCurrentDirectory(@"C:\Temp");

// 以降の相対パスは C:\Temp を基準に解決される
File.WriteAllText("log.txt", "message");

一時的な都合で変更したつもりでも、別の処理に影響する可能性があるため、変更は最小限にしましょう。

8-2. 相対パスの挙動が変わる

カレントディレクトリを変更すると、相対パスの参照先が変わります。

C#
Console.WriteLine(Path.GetFullPath("data.txt"));

Directory.SetCurrentDirectory(@"C:\Temp");

Console.WriteLine(Path.GetFullPath("data.txt"));

同じ "data.txt" という文字列でも、カレントディレクトリが違えば別のファイルを指します。相対パスを多用しているコードでは、カレントディレクトリの変更が不具合の原因になりやすいです。

8-3. マルチスレッド処理でのリスク

マルチスレッド処理や非同期処理でカレントディレクトリを変更すると、別スレッドが相対パスを使っている途中で基準ディレクトリが変わる可能性があります。

C#
// スレッドA
Directory.SetCurrentDirectory(@"C:\WorkA");

// スレッドB
File.ReadAllText("data.txt");

このようなコードでは、data.txt がどこから読み込まれるか分かりにくくなります。並列処理やWebアプリでは、カレントディレクトリを変更せず、絶対パスを作って処理するほうが安全です。

8-4. ライブラリ内で変更しない方がよい理由

ライブラリや共通クラスの中で Directory.SetCurrentDirectory() を呼び出すのは避けるべきです。呼び出し元アプリが想定している相対パスの基準を、ライブラリ側が勝手に変えてしまうためです。

悪い例は次のようなコードです。

C#
public class CsvLoader
{
public string Load(string fileName)
{
Directory.SetCurrentDirectory(@"C:\Csv");
return File.ReadAllText(fileName);
}
}

このような実装では、CsvLoader を呼び出したあと、アプリ全体のカレントディレクトリが変わってしまいます。

改善するなら、基準パスを引数やコンストラクタで受け取り、絶対パスを組み立てます。

C#
public class CsvLoader
{
private readonly string _baseDirectory;

public CsvLoader(string baseDirectory)
{
_baseDirectory = baseDirectory;
}

public string Load(string fileName)
{
string path = Path.Combine(_baseDirectory, fileName);
return File.ReadAllText(path);
}
}

8-5. 変更前のディレクトリを保存して戻す方法

どうしてもカレントディレクトリを一時的に変更する必要がある場合は、変更前の値を保存して、finally で戻すようにします。

C#
using System;
using System.IO;

class Program
{
static void Main()
{
string originalDirectory = Directory.GetCurrentDirectory();

try
{
Directory.SetCurrentDirectory(@"C:\Temp");

// C:\Temp を基準にした処理
File.WriteAllText("sample.txt", "test");
}
finally
{
Directory.SetCurrentDirectory(originalDirectory);
}
}
}

finally に戻す処理を書いておけば、途中で例外が発生しても元のカレントディレクトリへ戻しやすくなります。

9. よくあるエラーと対処法

9-1. DirectoryNotFoundExceptionが発生する

DirectoryNotFoundException は、指定したディレクトリが存在しない場合に発生します。Directory.SetCurrentDirectory() でも、指定したディレクトリが見つからない場合にこの例外が発生します。Microsoft Learn

対処法は、事前に Directory.Exists() で確認することです。

C#
string directory = @"C:\Data";

if (!Directory.Exists(directory))
{
Console.WriteLine("フォルダが存在しません。");
return;
}

Directory.SetCurrentDirectory(directory);

設定ファイルからパスを読み込んでいる場合は、余分な空白、全角文字、環境ごとの差異も確認しましょう。

9-2. UnauthorizedAccessExceptionが発生する

UnauthorizedAccessException は、指定した場所へアクセスする権限がない場合に発生します。Directory.GetCurrentDirectory() のドキュメントでも、呼び出し元に必要な権限がない場合の例外として UnauthorizedAccessException が挙げられています。Microsoft Learn

たとえば、システムフォルダや他ユーザーのフォルダ、管理者権限が必要な場所へ書き込もうとすると発生することがあります。

対処法は次のとおりです。

C#
try
{
File.WriteAllText(@"C:\Windows\sample.txt", "test");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("アクセス権限がありません。保存先を変更してください。");
}

ログや一時ファイルは、ユーザーが書き込み可能なフォルダやアプリ専用のデータフォルダへ保存するのが安全です。

9-3. 相対パスでファイルが見つからない

相対パスでファイルが見つからない場合は、最初にカレントディレクトリを表示します。

C#
Console.WriteLine(Directory.GetCurrentDirectory());
Console.WriteLine(Path.GetFullPath("data.txt"));

Path.GetFullPath("data.txt") を使うと、相対パスが実際にどの絶対パスとして解決されているか確認できます。

よくある原因は次のようなものです。

  • ファイルをソースコードのフォルダに置いているが、実行時の出力フォルダにコピーしていない

  • Visual Studioのデバッグ時とexe直接実行時でカレントディレクトリが違う

  • カレントディレクトリを途中で変更している

  • ASP.NET CoreでContent rootやWeb rootを使うべき場面で相対パスに依存している

9-4. Visual Studio上では動くがexeでは動かない

Visual Studio上では動くのにexeでは動かない場合、開発環境のカレントディレクトリと実行環境のカレントディレクトリが違う可能性があります。

対策としては、必要なファイルを出力ディレクトリにコピーし、AppContext.BaseDirectory を基準に読み込む方法があります。

C#
string path = Path.Combine(AppContext.BaseDirectory, "data", "sample.json");

この書き方なら、開発時も配布時も、アプリの配置場所を基準にファイルを参照しやすくなります。

9-5. デバッグ時と本番環境でパスが違う

デバッグ時と本番環境では、実行ユーザー、作業ディレクトリ、配置場所、権限が異なることがあります。特にWindowsサービスでは、現在の作業ディレクトリが C:\WINDOWS\system32 になるケースがあり、設定ファイルやログの保存場所として適さないとMicrosoftのASP.NET Core Windows Serviceのドキュメントでも説明されています。Microsoft Learn

本番環境でパスの問題を避けるには、次の方針が有効です。

  • 設定ファイルはアプリのベースディレクトリやContent rootを基準にする

  • ログ出力先は明示的に絶対パスで設定する

  • カレントディレクトリに依存しない

  • 起動時に重要なパスをログへ出力する

10. カレントディレクトリを扱うおすすめの実装方法

10-1. なるべく絶対パスを使う

実務では、相対パスをそのまま使うより、基準ディレクトリから絶対パスを組み立てるほうが安全です。

C#
string baseDirectory = AppContext.BaseDirectory;
string filePath = Path.Combine(baseDirectory, "data", "input.csv");

このようにしておけば、カレントディレクトリが変わっても参照先が安定します。

10-2. Path.Combineで安全にパスを組み立てる

パスの組み立てには Path.Combine() を使いましょう。

C#
string logDirectory = Path.Combine(AppContext.BaseDirectory, "logs");
string logPath = Path.Combine(logDirectory, "app.log");

必要であれば、フォルダを作成してから書き込みます。

C#
Directory.CreateDirectory(logDirectory);
File.AppendAllText(logPath, "ログメッセージ" + Environment.NewLine);

この書き方なら、logs フォルダがない場合でも作成してから書き込めます。

10-3. BaseDirectoryを使うべきケース

AppContext.BaseDirectory は、アプリの配置場所を基準にファイルを扱いたい場合に便利です。AppContext.BaseDirectory は、アセンブリリゾルバーがアセンブリを探すために使うベースディレクトリを返すプロパティで、.NET 5以降の単一ファイルアプリでもホスト実行可能ファイルを含むディレクトリを返します。Microsoft Learn

向いているケースは次のような場面です。

  • exeと同じフォルダにある設定ファイルを読む

  • アプリ配下のテンプレートファイルを読む

  • 配布物に含めた初期データを読む

  • ローカルツールでアプリ基準のログフォルダを作る

C#
string templatePath = Path.Combine(AppContext.BaseDirectory, "Templates", "report.html");

10-4. CurrentDirectoryを変更せずに処理する方法

カレントディレクトリを変更しなくても、絶対パスを指定すればファイル操作はできます。

避けたい書き方は次のようなものです。

C#
Directory.SetCurrentDirectory(targetDirectory);
File.ReadAllText("data.txt");

おすすめは、最初から絶対パスを作る書き方です。

C#
string filePath = Path.Combine(targetDirectory, "data.txt");
string text = File.ReadAllText(filePath);

このほうが、処理の影響範囲が小さく、別の処理に副作用を与えにくくなります。

10-5. 設定ファイルやログ出力での実践例

設定ファイルを読み込み、ログを出力する例です。

C#
using System;
using System.IO;

class Program
{
static void Main()
{
string baseDirectory = AppContext.BaseDirectory;

string configPath = Path.Combine(baseDirectory, "config", "settings.json");
string logDirectory = Path.Combine(baseDirectory, "logs");
string logPath = Path.Combine(logDirectory, "app.log");

Directory.CreateDirectory(logDirectory);

if (File.Exists(configPath))
{
string config = File.ReadAllText(configPath);
File.AppendAllText(logPath, "設定ファイルを読み込みました。" + Environment.NewLine);
}
else
{
File.AppendAllText(logPath, "設定ファイルが見つかりません。" + Environment.NewLine);
}
}
}

このコードでは、カレントディレクトリを変更していません。AppContext.BaseDirectory を基準に絶対パスを作っているため、起動場所が変わっても処理が安定しやすくなります。

11. C#のCurrentDirectoryに関するFAQ

11-1. カレントディレクトリはどこで決まる?

カレントディレクトリは、プロセスの起動方法、ショートカットの作業フォルダ、IDEのデバッグ設定、ホスティング環境などによって決まります。C#のコード上では、Environment.CurrentDirectoryDirectory.GetCurrentDirectory() で確認できます。

C#
Console.WriteLine(Environment.CurrentDirectory);
Console.WriteLine(Directory.GetCurrentDirectory());

Environment.CurrentDirectory は現在の作業ディレクトリを取得または設定でき、Directory.GetCurrentDirectory() は現在の作業ディレクトリを取得できます。Microsoft Learn+1

11-2. カレントディレクトリと作業ディレクトリは同じ?

基本的には同じ意味で使われます。C#や.NETの文脈では、カレントディレクトリは current working directory、つまり現在の作業ディレクトリを指します。

Directory.GetCurrentDirectory() のドキュメントでも、アプリケーションの現在の作業ディレクトリを取得すると説明されています。Microsoft Learn

11-3. 実行中にカレントディレクトリを変更してもよい?

変更は可能です。

C#
Environment.CurrentDirectory = @"C:\Temp";
Directory.SetCurrentDirectory(@"C:\Temp");

ただし、カレントディレクトリの変更は相対パスの解決に影響します。ライブラリ、マルチスレッド処理、Webアプリでは副作用が大きくなりやすいため、基本的には変更せず、絶対パスを組み立てて処理する方法をおすすめします。

11-4. プロジェクト直下のファイルを読み込むには?

実行時にプロジェクト直下のファイルを直接読むのではなく、ファイルを出力ディレクトリへコピーして、AppContext.BaseDirectory を基準に読み込む方法が扱いやすいです。

.csproj に次のように設定します。

XML
<ItemGroup>
<None Update="sample.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

C#側では次のように読み込みます。

C#
string path = Path.Combine(AppContext.BaseDirectory, "sample.json");
string json = File.ReadAllText(path);

この方法なら、Visual Studioでのデバッグ時と発行後の実行時でパスの扱いを揃えやすくなります。

11-5. .NET Frameworkと.NETで違いはある?

基本的な考え方は同じです。Environment.CurrentDirectoryDirectory.GetCurrentDirectory()Directory.SetCurrentDirectory() は、.NET Frameworkでも現在の.NETでも使われます。

ただし、周辺のパス取得では違いに注意が必要です。たとえば、Assembly.Location は .NET 5以降のバンドルされたアセンブリでは空文字を返すと説明されています。そのため、実行ファイルの配置ディレクトリを取得したい場合は、AppContext.BaseDirectory を使うほうが適しているケースがあります。Microsoft Learn+1

まとめ

C#のカレントディレクトリは、相対パスを解決するときの基準になる重要なディレクトリです。Environment.CurrentDirectoryDirectory.GetCurrentDirectory() を使えば現在のカレントディレクトリを取得でき、Environment.CurrentDirectory への代入や Directory.SetCurrentDirectory() を使えば変更もできます。

ただし、カレントディレクトリは実行ファイルの場所と同じとは限りません。Visual Studioでのデバッグ、exeの直接実行、ショートカット起動、ASP.NET Core、Windowsサービスなど、実行環境によって変わる可能性があります。

実務では、カレントディレクトリを安易に変更するよりも、AppContext.BaseDirectoryIHostEnvironment.ContentRootPath など適切な基準ディレクトリを使い、Path.Combine() で絶対パスを組み立てる実装がおすすめです。

特に設定ファイル、ログファイル、テンプレートファイルを扱う場合は、次の方針を意識しましょう。

  • Directory.GetCurrentDirectory() で現在の作業ディレクトリを確認する

  • 実行ファイル基準なら AppContext.BaseDirectory を使う

  • ASP.NET Coreでは ContentRootPathWebRootPath を使う

  • パス結合には Path.Combine() を使う

  • カレントディレクトリの変更は最小限にする

  • 変更する場合は元のディレクトリへ戻す

C#で current directory を正しく理解しておくと、「ファイルが見つからない」「Visual Studioでは動くのにexeでは動かない」「本番環境だけパスが違う」といったトラブルを防ぎやすくなります。