Как получить путь к исполняемому файлу консольного приложения в .NET?
Правильный способ: используйте AppContext.BaseDirectory
В современном .NET (начиная с .NET Core 2.0 и выше, включая .NET 5, 6, 7, 8 и 9) самый надёжный и рекомендуемый способ получить путь к папке, где находится ваш исполняемый файл — это свойство AppContext.BaseDirectory. Оно всегда возвращает базовый каталог приложения, то есть папку с .dll или .exe файлом, независимо от того, как вы запустили приложение.
using System;
using System.IO;
class Program
{
static void Main(string[] args)
{
// Получаем путь к папке с приложением
string appPath = AppContext.BaseDirectory;
Console.WriteLine("Базовый каталог приложения: " + appPath);
// Строим полный путь к файлу config.json
string configPath = Path.Combine(appPath, "config.json");
if (File.Exists(configPath))
{
string configContent = File.ReadAllText(configPath);
Console.WriteLine("Содержимое config.json: " + configContent);
}
else
{
Console.WriteLine("Файл config.json не найден по пути: " + configPath);
}
}
}
Почему это работает лучше, чем другие варианты?
- В отличие от
Directory.GetCurrentDirectory():AppContext.BaseDirectoryне зависит от того, из какой папки вы запустили приложение. Текущая рабочая директория может меняться (например, при запуске из планировщика задач, из другой программы или через контекстное меню), а базовый каталог всегда указывает на место установки вашего приложения. - В отличие от
Assembly.GetExecutingAssembly().Location: В .NET Core и .NET 5+ исполняемые файлы часто компилируются как "single-file" приложения (один файл). В этом случаеAssembly.Locationможет вернуть пустую строку или временную папку распаковки.AppContext.BaseDirectoryкорректно обрабатывает и этот случай. - В отличие от
Environment.CurrentDirectory: Текущая директория может быть изменена в коде с помощьюDirectory.SetCurrentDirectory(), что сломает вашу логику.AppContext.BaseDirectory— это readonly-свойство, его нельзя случайно изменить.
Дополнительные рекомендации
Если вы работаете с файлами, которые должны лежать рядом с приложением, всегда используйте комбинацию AppContext.BaseDirectory и Path.Combine(). Это гарантирует корректное формирование пути на любой платформе (Windows, Linux, macOS).
Также обратите внимание: в консольных приложениях .NET после компиляции в папке bin\Debug\net8.0 (или аналогичной) лежит не .exe, а .dll файл (если вы не используете single-file публикацию). AppContext.BaseDirectory будет указывать именно на эту папку. Если вы публикуете приложение как single-file (одним .exe), то базовый каталог будет указывать на временную папку, куда распаковались файлы — это нормальное поведение, и ваши файлы конфигурации нужно класть рядом с этим .exe.
Пример для .NET 6+ с проверкой на null:
string basePath = AppContext.BaseDirectory
?? throw new InvalidOperationException("Не удалось определить базовый каталог приложения");
string configPath = Path.Combine(basePath, "settings.json");
<<Альтернативный подход: через Assembly.GetEntryAssembly()
Если по какой-то причине вы не хотите использовать AppContext.BaseDirectory (например, в очень старых версиях .NET Framework), можно получить путь через сборку приложения. Этот способ работает во всех версиях .NET, но имеет свои особенности.
using System;
using System.IO;
using System.Reflection;
class Program
{
static void Main()
{
// Получаем путь к файлу сборки (exe или dll)
string assemblyPath = Assembly.GetEntryAssembly().Location;
string directoryPath = Path.GetDirectoryName(assemblyPath);
Console.WriteLine("Путь к сборке: " + assemblyPath);
Console.WriteLine("Папка со сборкой: " + directoryPath);
// Используем путь
string logPath = Path.Combine(directoryPath, "log.txt");
File.AppendAllText(logPath, $"Запуск в {DateTime.Now}\n");
}
}
Когда это может пригодиться:
- Если вам нужен именно полный путь к самому .exe/.dll файлу, а не только к папке.
- В коде, который должен работать и в старом .NET Framework 4.x, и в современном .NET.
- Когда вы точно знаете, что приложение не будет опубликовано как single-file (иначе
Locationможет быть пустым).
Недостатки: При публикации в single-file режиме (особенно в .NET 6+) Assembly.GetEntryAssembly().Location может вернуть пустую строку. В этом случае лучше использовать AppContext.BaseDirectory из первого ответа. Для универсальности можно комбинировать оба подхода с проверкой на null.