Avatar ·

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

Привет! Я новичок в C# и .NET, и у меня возникла проблема. Я пишу консольное приложение, которое должно читать файл конфигурации, лежащий в той же папке, что и сама программа. Но я никак не могу правильно получить путь к папке, где находится мой .exe-файл. Вот мой код: ```csharp using System; using System.IO; class Program { static void Main(string[] args) { // Пытаюсь получить путь к файлу конфигурации string configPath = "config.json"; if (File.Exists(configPath)) { Console.WriteLine("Файл найден: " + configPath); } else { Console.WriteLine("Файл не найден. Текущая директория: " + Directory.GetCurrentDirectory()); } Console.ReadKey(); } } ``` Проблема в том, что когда я запускаю приложение из Visual Studio (через F5), файл config.json находится в папке с исходниками, и всё работает. Но когда я запускаю скомпилированный .exe-файл из папки bin\Debug, файл не находится, потому что текущая рабочая директория может быть совсем другой (например, если я запускаю из командной строки из другой папки). Я перепробовал несколько вариантов: `Environment.CurrentDirectory`, `Assembly.GetExecutingAssembly().Location`, но они возвращают разные пути, и я путаюсь. Подскажите, как правильно и надёжно получить путь к папке, где лежит мой исполняемый файл, чтобы я мог читать файлы рядом с ним? Желательно с примерами для .NET 6+. <<>>

Правильный способ: используйте 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.