Avatar ·

Как получить путь к исполняемому файлу консольного приложения в .NET?

Всем привет! Пишу консольное приложение на C# (.NET 6), и мне нужно получить путь к папке, где лежит мой .exe-файл. Пробовал несколько способов, но результаты путают. Например, я запускаю приложение из папки `C:\MyApp`, но хочу, чтобы оно читало файл `settings.json` из той же директории. Пробовал так: ```csharp string path = Environment.CurrentDirectory; Console.WriteLine(path); ``` Но когда я запускаю приложение из командной строки, находясь в другой папке, выводится не `C:\MyApp`, а текущая рабочая директория. Это неправильно. Ещё пробовал: ```csharp string path = Assembly.GetExecutingAssembly().Location; Console.WriteLine(path); ``` Но это возвращает путь с именем файла (например, `C:\MyApp\MyApp.exe`), а мне нужна только папка. И ещё я слышал, что в некоторых случаях этот метод может вернуть путь к временной папке из-за shadow copying. В общем, запутался окончательно. Как правильно получить надёжный путь к папке с приложением в .NET, чтобы он работал независимо от того, как запущена программа? Нужно именно для консольного приложения, а не для ASP.NET или WinForms. Заранее спасибо! <<>>

Правильный способ: AppContext.BaseDirectory

В современном .NET (начиная с .NET Core 3.0 и во всех версиях .NET 5/6/7/8/9) самый надёжный и простой способ получить путь к папке с исполняемым файлом — использовать свойство AppContext.BaseDirectory. Оно возвращает именно базовую директорию приложения (где лежит .dll или .exe), с завершающим обратным слешем.

using System;

class Program
{
    static void Main()
    {
        string appPath = AppContext.BaseDirectory;
        Console.WriteLine($"Путь к приложению: {appPath}");
        
        // Чтение файла из той же папки
        string settingsPath = Path.Combine(appPath, "settings.json");
        if (File.Exists(settingsPath))
        {
            string json = File.ReadAllText(settingsPath);
            Console.WriteLine($"Файл найден: {settingsPath}");
        }
    }
}

Почему это работает надёжно?

  • Не зависит от текущей рабочей директории — в отличие от Environment.CurrentDirectory, который меняется при запуске из другого места.
  • Корректно работает при shadow copying — возвращает реальную папку, а не временную (в отличие от Assembly.Location в некоторых сценариях).
  • Работает одинаково для single-file публикации, когда приложение упаковано в один .exe файл.
  • Не требует дополнительных using — доступен из пространства имён System.

Другие варианты и их недостатки

Вариант 1: Assembly.GetExecutingAssembly().Location

string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

Этот способ работает, но имеет два недостатка:

  • При shadow copying (например, в ASP.NET или некоторых тестовых средах) может вернуть временный путь.
  • Требует ссылки на System.Reflection и вызова Path.GetDirectoryName для отсечения имени файла.

Вариант 2: Environment.GetCommandLineArgs()[0]

string path = Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]);

Ненадёжный: при запуске через ассоциацию файлов или из PATH может вернуть только имя файла без полного пути.

Вариант 3: Process.GetCurrentProcess().MainModule.FileName

string path = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);

Работает, но медленнее и требует System.Diagnostics. При single-file публикации может вернуть путь к временной папке.

Рекомендация

Используйте AppContext.BaseDirectory — это официально рекомендуемый Microsoft способ для консольных приложений. Он простой, быстрый и надёжный во всех сценариях. Если вам нужно получить именно путь к .exe файлу (а не к папке), дополните код так:

string exePath = Path.Combine(AppContext.BaseDirectory, AppDomain.CurrentDomain.FriendlyName + ".exe");

Но в 99% случаев вам нужна именно папка, чтобы читать или сохранять файлы рядом с приложением. AppContext.BaseDirectory идеально подходит для этой задачи.

<<>>

Альтернативный подход: использование Directory.GetCurrentDirectory() с проверкой

Если ваше приложение всегда запускается из своей папки (например, через ярлык или из проводника), можно использовать Environment.CurrentDirectory, но с предварительной проверкой:

string appDir = AppDomain.CurrentDomain.BaseDirectory;
Console.WriteLine($"Базовый домен: {appDir}");

Свойство AppDomain.CurrentDomain.BaseDirectory — это устаревший синоним AppContext.BaseDirectory (доступен с .NET Framework 1.0). В .NET Core/5+ он работает идентично, но Microsoft рекомендует переходить на AppContext.BaseDirectory.

Дополнительный совет для кросс-платформенности

Если ваше приложение должно работать и на Windows, и на Linux, используйте Path.Combine для сборки путей и не полагайтесь на жёстко закодированные обратные слеши:

string configPath = Path.Combine(AppContext.BaseDirectory, "config", "appsettings.json");
// На Windows: C:\MyApp\config\appsettings.json
// На Linux: /home/user/MyApp/config/appsettings.json

Также учтите, что при публикации как single-file (один .exe файл) папка приложения — это временная папка, куда распаковываются файлы. В этом случае AppContext.BaseDirectory вернёт путь к этой временной папке, а не к месту, где лежит исходный .exe. Если вам нужен именно исходный путь, используйте Environment.ProcessPath (доступен в .NET 6+):

string originalExePath = Environment.ProcessPath; // Полный путь к исходному .exe
string originalDir = Path.GetDirectoryName(originalExePath);