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


953

Как найти путь к приложению в консольном приложении?

В Windows Forms я могу использовать, Application.StartupPathчтобы найти текущий путь, но он не доступен в консольном приложении.


5
Вы устанавливаете .NET Framework на целевой машине (клиент, разработчик)? если ваш ответ верен; Таким образом, вы можете добавить ссылку на System.Windows.Forms.dll и использовать Application.StartupPath! Это лучший способ, если вы хотите отказаться от дальнейших исключений в будущем!
Эхсан Мохаммади

AppDomain.BaseDirectory является каталогом приложения. Имейте в виду, что приложение может вести себя по-разному в VS env и Win env. Но AppDomain должен быть не таким, как application.path, но я надеюсь, что это не только для IIS.
Мертуарес

Ответы:


1179

System.Reflection.Assembly.GetExecutingAssembly(), 1Location

Добавьте к этому, System.IO.Path.GetDirectoryNameесли все, что вам нужно, это каталог.

1 Согласно комментарию Mr.Mindor:
System.Reflection.Assembly.GetExecutingAssembly().Location возвращает место, где в данный момент находится исполняющая сборка, которое может быть, а может и не быть, где находится сборка, когда не выполняется. В случае сборок теневого копирования вы получите путь во временный каталог. System.Reflection.Assembly.GetExecutingAssembly().CodeBaseвернет «постоянный» путь сборки.


243
System.Reflection.Assembly.GetExecutingAssembly (). Location возвращает местоположение, в котором в данный момент находится исполняющая сборка , что может быть или не быть в том месте, где находится сборка, когда она не выполняется. В случае сборок теневого копирования вы получите путь во временный каталог. System.Reflection.Assembly.GetExecutingAssembly (). CodeBase вернет « вас устраивают » путь сборки.
Мистер Миндор

13
@SamGoldberg: Это зависит от того, как он используется: stackoverflow.com/q/1068420/391656 . Или вы можете ... новый Uri (System.Reflection.Assembly.GetExecutingAssembly (). CodeBase) .LocalPath
Mr.Mindor

28
GetExecutingAssemblyвозвращает сборку, содержащую код, который в данный момент выполняется . Это не обязательно сборка консоли .exe . Это может быть сборка, которая была загружена из совершенно другого места. Вам придется использовать GetEntryAssembly! Также обратите внимание, что CodeBaseэто не может быть установлено, когда сборка находится в GAC. Лучшая альтернатива AppDomain.CurrentDomain.BaseDirectory.
битбонк

3
Пожалуйста, напишите код в 4 пробела, чтобы его было удобно копировать
fnc12

3
если вы вызовете dll, System.Reflection.Assembly.GetExecutingAssembly (). CodeBase получит «file: /// C: /Windows/Microsoft.NET/Framework64/v4.0.30319/mscorlib.dll»
raidsan

407

Вы можете использовать следующий код, чтобы получить текущий каталог приложения.

AppDomain.CurrentDomain.BaseDirectory

43
Не используйте это. BaseDirectory может быть установлен во время выполнения. Это не гарантируется быть правильным (как принятый ответ).
USR

3
+1 Это, вероятно, тот ответ, который вам нужен, поскольку он компенсирует теневое копирование.
Джордж Мауэр

4
@usr Что заставляет вас думать, что это BaseDirectoryможет быть установлено во время выполнения? У него есть только добытчик.
битбонк

3
@bitbonk это может быть установлено во время создания домена приложения.
USR

3
Разве не BaseDirectory можно изменить в файле * .lnk, в поле «Start in:»?
Александр

170

У вас есть два варианта поиска каталога приложения, который вы выберете, будет зависеть от вашей цели.

// to get the location the assembly is executing from
//(not necessarily where the it normally resides on disk)
// in the case of the using shadow copies, for instance in NUnit tests, 
// this will be in a temp directory.
string path = System.Reflection.Assembly.GetExecutingAssembly().Location;

//To get the location the assembly normally resides on disk or the install directory
string path = System.Reflection.Assembly.GetExecutingAssembly().CodeBase;

//once you have the path you get the directory with:
var directory = System.IO.Path.GetDirectoryName(path);

3
Просто хотел сказать, очевидно, что существует гораздо больше, чем 2 варианта, сколько других вариантов опубликовано ...
vapcguy

17
Если все, что вы пытаетесь сделать с указанным путем, не поддерживает формат URI, используйтеvar localDirectory = new Uri(directory).LocalPath;
Скотт Солмер,

Это просто неправильно. Какой исполняемый файл не является сборкой .NET вообще? Правильный ответ - проверить среду и проверить командную строку.
отметка

@ Ukuma.Scott Это не работает, если путь содержит & или #
MatsW

82

Возможно, немного поздно, но это стоит упомянуть:

Environment.GetCommandLineArgs()[0];

Или правильнее получить только путь к каталогу:

System.IO.Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]);

Редактировать:

Довольно много людей отметили, что GetCommandLineArgsне гарантируется возвращение имени программы. См . Первое слово в командной строке - это имя программы только по соглашению . В статье утверждается, что «Хотя очень немногие программы для Windows используют эту причуду (я сам о себе не знаю)». Так что можно «подделать» GetCommandLineArgs, но речь идет о консольном приложении. Консольные приложения обычно бывают быстрыми и грязными. Так что это вписывается в мою философию KISS.


1
@usr ситуация, на которую вы намекаете, носит исключительно теоретический характер. В контексте консольного приложения не имеет смысла использовать какой-либо другой метод. Будь проще!
Стив Мак

1
@usr mmm - просмотр столбца cmdline taskmgr подтверждает то, что я говорю. Несколько системных служб только с именем exe. Ничего. Я пытаюсь сказать, что при разработке консольного приложения нет необходимости делать вещи более сложными, чем они должны быть. Особенно, когда у нас уже есть информация. Теперь, если вы запускаете консольное приложение таким образом, чтобы обмануть GetCommandLineArgs, то вы уже перепрыгиваете через обручи, и вам, возможно, придется спросить себя, подходит ли консольное приложение.
Стив Мак

5
Ваше «простое» решение включает в себя два вызова метода. «Сложное» решение включает в себя два вызова метода. Нет практической разницы - за исключением того, что «простое» решение может дать вам неправильный ответ при определенных обстоятельствах, которые не находятся под вашим контролем при написании программы. Зачем рисковать? Используйте два других вызова метода, и ваша программа не будет более сложной, но будет более надежной.
Крис

3
Сработал для моего сценария, другие решения не сработали, поэтому спасибо за предоставленную альтернативу :-) Я использовал ReSharper Runner для запуска MS Unit test, и код, который я тестировал, нуждался в конкретной .dll, чтобы быть в каталоге выполнения. ..and Assembly.GetExecutingDirectory () странно возвращает другой результат.
wallismark

1
@ Крис - в защиту этого ответа. Это работает для модульных тестов, решение GetEntryAssembly - нет, потому что GetEntryAssembly возвращает ноль. Ответы, которые предлагают GetExecutingAssembly, являются поддельными, потому что они возвращают исполняемый файл, только если исполняемая сборка является исполняемым файлом. Это не простое, но правильное решение.
отметка

44

Для всех, кто интересуется веб-приложениями asp.net. Вот мои результаты 3 разных методов

protected void Application_Start(object sender, EventArgs e)
{
  string p1 = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
  string p2 = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath;
  string p3 = this.Server.MapPath("");
  Console.WriteLine("p1 = " + p1);
  Console.WriteLine("p2 = " + p2);
  Console.WriteLine("p3 = " + p3);
}

результат

p1 = C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\root\a897dd66\ec73ff95\assembly\dl3\ff65202d\29daade3_5e84cc01
p2 = C:\inetpub\SBSPortal_staging\
p3 = C:\inetpub\SBSPortal_staging

приложение физически запускается из «C: \ inetpub \ SBSPortal_staging», поэтому первое решение определенно не подходит для веб-приложений.


42

Ответ выше был на 90% от того, что мне было нужно, но вместо обычного пути я вернул Uri.

Как объясняется в сообщении на форумах MSDN, Как преобразовать путь URI в обычный путь к файлу? Я использовал следующее:

// Get normal filepath of this assembly's permanent directory
var path = new Uri(
    System.IO.Path.GetDirectoryName(
        System.Reflection.Assembly.GetExecutingAssembly().CodeBase)
    ).LocalPath;

1
это также хорошо работает, если рассматриваемый exe-файл является службой Windows и текущий каталог возвращает C: \ Windows \ system32. Приведенный выше код возвращает фактическое местоположение exe
DaImTo

За исключением случаев, когда вы пытаетесь сделать что-то подобное File.CreateDirectory(path), это даст вам исключение, что он не разрешает пути URI ...
vapcguy

1
К сожалению, это не работает для путей, которые содержат идентификатор фрагмента ( #символ). Идентификатор и все, что следует за ним, усекается из полученного пути.
bgfvdu3w

Почему бы вам не поменяться new Uriи System.IO.Path.GetDirectoryName? Это дает вам нормальную строку пути вместо Uri.
Тимо

Я считаю это лучшим. Этот же подход надежно работал для меня в любой среде. На производстве, локальная отладка, модульное тестирование ... Хотите открыть файл контента, который вы включили («контент - скопируйте, если новее») в модульный тест? Это здесь.
Тимо

29

Возможно, вы хотите сделать это:

System.IO.Path.GetDirectoryName(
    System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)

23

Вы можете использовать это вместо этого.

System.Environment.CurrentDirectory

Это позволит получить папку с исполняемым файлом
Iain

Это можно изменить несколькими способами (настройки ярлыков и т. Д.) ... лучше НЕ использовать это.
Юша Алеауб

23

Если вы ищете .NET Core совместимый способ, используйте

System.AppContext.BaseDirectory

Это было представлено в .NET Framework 4.6 и .NET Core 1.0 (и .NET Standard 1.3). Смотрите: Свойство AppContext.BaseDirectory .

Согласно этой странице ,

Это предпочтительная замена для AppDomain.CurrentDomain.BaseDirectory в .NET Core


1
см. также github.com/dotnet/runtime/issues/13051 для автономных консольных приложений dotnet. Рекомендация здесь - использоватьProcess.GetCurrentProcess().MainModule.FileName
Гэвин

19

Для консольных приложений вы можете попробовать это:

System.IO.Directory.GetCurrentDirectory();

Вывод (на моей локальной машине):

c: \ users \ xxxxxxx \ Documents \ visual studio 2012 \ Проекты \ ImageHandler \ GetDir \ bin \ Debug

Или вы можете попробовать (в конце есть дополнительная обратная косая черта):

AppDomain.CurrentDomain.BaseDirectory

Вывод:

c: \ users \ xxxxxxx \ Documents \ visual studio 2012 \ Проекты \ ImageHandler \ GetDir \ bin \ Debug \


« BaseDirectoryМожет быть установлено во время выполнения. Оно НЕ гарантированно будет правильным»
Юша Алеауб


9

Вы можете просто добавить в свой проект ссылки, System.Windows.Formsа затем использовать System.Windows.Forms.Application.StartupPath как обычно.

Таким образом, не нужно более сложных методов или использования отражения.


Я использовал этот, и он работает хорошо. Но однажды я использовал этот метод в своем проекте модульного тестирования. И, конечно, это не удалось, потому что он искал мой файл в C: \ PROGRAM FILES (X86) \ MICROSOFT VISUAL STUDIO 14.0 \ COMMON7 \ IDE \ COMMONEXTENSIONS \ MICROSOFT \ TESTWINDOW
ainasiart

@ainasiart, так как мне заставить это работать во время модульного тестирования?
Николас

8

Следующая строка даст вам путь к приложению:

var applicationPath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName)

Вышеупомянутое решение работает должным образом в следующих ситуациях:

  • простое приложение
  • в другом домене, где Assembly.GetEntryAssembly () будет возвращать ноль
  • DLL загружается из встроенных ресурсов как байтовый массив и загружается в домен приложений как Assembly.Load (byteArrayOfEmbeddedDll)
  • со mkbundleсвязками Моно (другие методы не работают)

Под отладчиком на Linux это возвращает: / usr / share / dotnet
Владимир

7

Я использую это, если предполагается, что exe-файл вызывается двойным щелчком

var thisPath = System.IO.Directory.GetCurrentDirectory();

5
Это не правильно, потому что вы можете получить случайные каталоги в результате.
amuliar

эта команда возвращает Environment.CurrentDirectory, который может быть изменен во время выполнения на любой путь, поэтому он не является надежным решением.
Юрий Козлов

7

я использовал

System.AppDomain.CurrentDomain.BaseDirectory

когда я хочу найти путь относительно папки приложений. Это работает как для ASP.Net, так и для приложений winform. Также не требуется никаких ссылок на сборки System.Web.


6

Я имею в виду, почему не метод ap / invoke?

    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Text;
    public class AppInfo
    {
            [DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false)]
            private static extern int GetModuleFileName(HandleRef hModule, StringBuilder buffer, int length);
            private static HandleRef NullHandleRef = new HandleRef(null, IntPtr.Zero);
            public static string StartupPath
            {
                get
                {
                    StringBuilder stringBuilder = new StringBuilder(260);
                    GetModuleFileName(NullHandleRef, stringBuilder, stringBuilder.Capacity);
                    return Path.GetDirectoryName(stringBuilder.ToString());
                }
            }
    }

Вы бы использовали его так же, как Application.StartupPath:

    Console.WriteLine("The path to this executable is: " + AppInfo.StartupPath + "\\" + System.Diagnostics.Process.GetCurrentProcess().ProcessName + ".exe");

2
Зачем p / invoke, когда для этого так много .NET?
ProfK

7
@ user3596865, поскольку требует жесткой зависимости от Windows и несовместим с DNX или Mono. И, возможно, в будущих версиях Windows произойдет серьезное изменение. Итак, еще раз: почему мы должны использовать pinvoke здесь?
Бен

5

Assembly.GetEntryAssembly().Location или Assembly.GetExecutingAssembly().Location

Использовать в сочетании с System.IO.Path.GetDirectoryName() чтобы получить только каталог.

Пути от GetEntryAssembly()иGetExecutingAssembly() могут отличаться, хотя в большинстве случаев каталог будет одинаковым.

При этом GetEntryAssembly()вы должны знать, что это может вернуться, nullесли модуль ввода неуправляемый (т.е. исполняемый файл C ++ или VB6). В этих случаях можно использовать GetModuleFileNameиз Win32 API:

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetModuleFileName(HandleRef hModule, StringBuilder buffer, int length);

5

в VB.net

My.Application.Info.DirectoryPath

у меня работает (Тип приложения: Библиотека классов). Не уверен насчет C # ... Возвращает путь без имени файла в виде строки


4
AppDomain.CurrentDomain.BaseDirectory

Решит проблему, ссылаясь на сторонние справочные файлы с установочными пакетами.


11
Этот ответ уже предлагался 5 лет назад, даже не раз.
PL

2

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

Поэтому можно использовать QueryFullProcessImageName, чтобы обойти это:

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Diagnostics;

internal static class NativeMethods
{
    [DllImport("kernel32.dll", SetLastError = true)]
    internal static extern bool QueryFullProcessImageName([In]IntPtr hProcess, [In]int dwFlags, [Out]StringBuilder lpExeName, ref int lpdwSize);

    [DllImport("kernel32.dll", SetLastError = true)]
    internal static extern IntPtr OpenProcess(
        UInt32 dwDesiredAccess,
        [MarshalAs(UnmanagedType.Bool)]
        Boolean bInheritHandle,
        Int32 dwProcessId
    );
}

public static class utils
{

    private const UInt32 PROCESS_QUERY_INFORMATION = 0x400;
    private const UInt32 PROCESS_VM_READ = 0x010;

    public static string getfolder()
    {
        Int32 pid = Process.GetCurrentProcess().Id;
        int capacity = 2000;
        StringBuilder sb = new StringBuilder(capacity);
        IntPtr proc;

        if ((proc = NativeMethods.OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid)) == IntPtr.Zero)
            return "";

        NativeMethods.QueryFullProcessImageName(proc, 0, sb, ref capacity);

        string fullPath = sb.ToString(0, capacity);

        return Path.GetDirectoryName(fullPath) + @"\";
    }
}

2

Попробуйте эту простую строку кода:

 string exePath = Path.GetDirectoryName( Application.ExecutablePath);

1

Другое решение использует относительные пути, указывающие на текущий путь:

Path.GetFullPath(".")

Это получает текущий каталог, а не местоположение запуска EXE.
tenfour

0

Я не видел, чтобы кто-нибудь преобразовал LocalPath, предоставленный отражением .Net Core, в пригодный для использования путь System.IO, так что вот моя версия.

public static string GetApplicationRoot()
{
   var exePath = new Uri(System.Reflection.
   Assembly.GetExecutingAssembly().CodeBase).LocalPath;

   return new FileInfo(exePath).DirectoryName;

}

Это вернет полный путь в формате «C: \ xxx \ xxx» туда, где находится ваш код.



-1

Вот надежное решение, которое работает с 32-битными и 64-битными приложениями.

Добавьте эти ссылки:

использование System.Diagnostics;

использование System.Management;

Добавьте этот метод в ваш проект:

public static string GetProcessPath(int processId)
{
    string MethodResult = "";
    try
    {
        string Query = "SELECT ExecutablePath FROM Win32_Process WHERE ProcessId = " + processId;

        using (ManagementObjectSearcher mos = new ManagementObjectSearcher(Query))
        {
            using (ManagementObjectCollection moc = mos.Get())
            {
                string ExecutablePath = (from mo in moc.Cast<ManagementObject>() select mo["ExecutablePath"]).First().ToString();

                MethodResult = ExecutablePath;

            }

        }

    }
    catch //(Exception ex)
    {
        //ex.HandleException();
    }
    return MethodResult;
}

Теперь используйте это так:

int RootProcessId = Process.GetCurrentProcess().Id;

GetProcessPath(RootProcessId);

Обратите внимание, что если вы знаете идентификатор процесса, то этот метод вернет соответствующий ExecutePath.

Дополнительно для заинтересованных:

Process.GetProcesses() 

... даст вам массив всех запущенных в данный момент процессов и ...

Process.GetCurrentProcess()

... даст вам текущий процесс, вместе с их информацией, например, Id и т. д., а также ограниченным контролем, например, Kill и т. д. *


-5

Вы можете создать имя папки как Ресурсы в проекте, используя Solution Explorer, затем вы можете вставить файл в Ресурсы.

private void Form1_Load(object sender, EventArgs e) {
    string appName = Environment.CurrentDirectory;
    int l = appName.Length;
    int h = appName.LastIndexOf("bin");
    string ll = appName.Remove(h);                
    string g = ll + "Resources\\sample.txt";
    System.Diagnostics.Process.Start(g);
}

6
Использование Environment.CurrentDirectory очень неправильно, не используйте это! этот путь может измениться во время выполнения. Даже при запуске это недетерминировано.
USR
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.