Jak uzyskać ścieżkę aplikacji w aplikacji konsoli .NET?

953

Jak znaleźć ścieżkę aplikacji w aplikacji konsoli?

W Windows Forms mogę użyć Application.StartupPathdo znalezienia bieżącej ścieżki, ale wydaje się, że nie jest ona dostępna w aplikacji konsolowej.

JSmyth
źródło
5
Czy instalujesz .NET Framework na komputerze docelowym (klient, programista)? jeśli twoja odpowiedź jest prawdziwa; Możesz więc dodać odwołanie do System.Windows.Forms.dll i użyć Application.StartupPath! To najlepszy sposób, jeśli chcesz zrezygnować z przyszłych wyjątków!
Ehsan Mohammadi
AppDomain.BaseDirectory to katalog aplikacji. Należy pamiętać, że aplikacja może zachowywać się inaczej w środowisku VS env i Win env. Ale AppDomain powinien być taki sam, jak application.path, ale mam nadzieję, że nie dotyczy to tylko IIS.
Mertuarez

Odpowiedzi:

1179

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

Połącz to z, System.IO.Path.GetDirectoryNamejeśli wszystko, czego chcesz, to katalog.

1 Zgodnie z komentarzem Mr.Mindor:
System.Reflection.Assembly.GetExecutingAssembly().Location zwraca miejsce, w którym aktualnie znajduje się zespół wykonujący, który może, ale nie musi być, gdzie znajduje się zespół, gdy nie jest wykonywany. W przypadku zestawów kopiujących w tle otrzymasz ścieżkę do katalogu tymczasowego. System.Reflection.Assembly.GetExecutingAssembly().CodeBasezwróci „stałą” ścieżkę zestawu.

Sam Axe
źródło
243
System.Reflection.Assembly.GetExecutingAssembly (). Lokalizacja zwraca miejsce, w którym aktualnie znajduje się wykonujący zespół , który może, ale nie musi być, gdzie znajduje się zespół, gdy nie jest wykonywany. W przypadku zestawów kopiujących w tle otrzymasz ścieżkę do katalogu tymczasowego. System.Reflection.Assembly.GetExecutingAssembly (). CodeBase zwróci ścieżkę „ permenant ” zestawu.
Mr.Mindor
13
@SamGoldberg: To zależy od tego, jak jest używany: stackoverflow.com/q/1068420/391656 . Lub możesz ... nowy Uri (System.Reflection.Assembly.GetExecutingAssembly (). CodeBase) .LocalPath
Mr.Mindor
28
GetExecutingAssemblyzwraca zestaw, który zawiera aktualnie wykonywany kod . To niekoniecznie musi być zestaw .exe konsoli . Może to być zespół, który został załadowany z zupełnie innej lokalizacji. Będziesz musiał użyć GetEntryAssembly! Należy również pamiętać, że CodeBasemoże nie być ustawiony, gdy zestaw znajduje się w GAC. Lepszą alternatywą jest AppDomain.CurrentDomain.BaseDirectory.
bitbonk,
3
Wpisz kod w 4
spacjach,
3
jeśli wywołasz dll, System.Reflection.Assembly.GetExecutingAssembly (). CodeBase otrzyma „plik: /// C: /Windows/Microsoft.NET/Framework64/v4.0.30319/mscorlib.dll”
raidsan
407

Możesz użyć następującego kodu, aby uzyskać bieżący katalog aplikacji.

AppDomain.CurrentDomain.BaseDirectory
Richard Ev
źródło
42
Nie używaj tego. BaseDirectory można ustawić w czasie wykonywania. Nie gwarantuje się poprawności (tak jak w przypadku zaakceptowanej odpowiedzi).
usr
3
+1 Jest to prawdopodobnie odpowiedź, którą chcesz, ponieważ kompensuje kopiowanie w tle.
George Mauer
4
@usr Co sprawia, że ​​uważasz, że BaseDirectorymożna to ustawić w czasie wykonywania? Ma tylko getter.
bitbonk,
3
@bitbonk można go ustawić w czasie tworzenia domeny.
usr
3
Czy nie jest tak, że BaseDirectory można zmienić w pliku * .lnk w polu „Rozpocznij w:”?
Alexander
170

Masz dwie opcje znalezienia katalogu aplikacji, który wybierzesz zależy od celu.

// 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);
Mr.Mindor
źródło
3
Chciałem tylko powiedzieć, oczywiście, istnieje wiele więcej niż 2 opcji według liczby innych wyborów ...
vapcguy
17
Jeśli cokolwiek próbujesz zrobić ze wspomnianą ścieżką, nie obsługuje formatu URI, użyjvar localDirectory = new Uri(directory).LocalPath;
Scott Solmer
To jest po prostu złe. Co to plik wykonywalny wcale nie jest zestawem .NET? Prawidłowa odpowiedź to sprawdzenie środowiska i wiersza poleceń.
zaznacz
@ Ukuma.Scott To nie działa, jeśli ścieżka zawiera & lub #
MatsW
82

Prawdopodobnie trochę późno, ale warto o tym wspomnieć:

Environment.GetCommandLineArgs()[0];

Lub bardziej poprawnie, aby uzyskać tylko ścieżkę do katalogu:

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

Edytować:

Sporo osób zauważyło, że GetCommandLineArgsnie ma gwarancji zwrócenia nazwy programu. Zobacz Pierwsze słowo w wierszu poleceń to nazwa programu tylko zgodnie z konwencją . Artykuł stwierdza, że ​​„Chociaż bardzo niewiele programów Windows używa tego dziwactwa (sam nie jestem świadomy)”. Można więc „sfałszować” GetCommandLineArgs, ale mówimy o aplikacji konsolowej. Aplikacje konsolowe są zwykle szybkie i brudne. To pasuje do mojej filozofii KISS.

Steve Mc
źródło
1
@usr sytuacja, do której nawiązujesz, jest wysoce teoretyczna. W kontekście aplikacji konsolowej nie ma sensu używać żadnej innej metody. Nie komplikuj!
Steve Mc
1
@usr mmm - spojrzenie na kolumnę taskmgr cmdline stanowi kopię zapasową tego, co mówię. Kilka usług systemowych z samą nazwą exe. Nieważne. Staram się powiedzieć, że podczas tworzenia aplikacji konsolowej nie ma potrzeby komplikowania rzeczy. Zwłaszcza, gdy mamy już dostępne informacje. Teraz, jeśli uruchamiasz aplikację konsolową w taki sposób, aby oszukać GetCommandLineArgs, to już przeskakujesz przez obręcze i prawdopodobnie będziesz musiał zadać sobie pytanie, czy aplikacja konsolowa jest właściwą drogą.
Steve Mc
5
Twoje „proste” rozwiązanie obejmuje dwa wywołania metod. „Skomplikowane” rozwiązanie obejmuje dwa wywołania metod. Brak praktycznej różnicy - z wyjątkiem tego, że „proste” rozwiązanie może dać złą odpowiedź w pewnych okolicznościach, które nie są pod twoją kontrolą podczas pisania programu. Po co ryzykować? Użyj pozostałych dwóch wywołań metod, a twój program nie będzie już bardziej skomplikowany, ale będzie bardziej niezawodny.
Chris
3
Pracowałem w moim scenariuszu, inne rozwiązania tego nie zrobiły, więc dziękuję za dostarczenie innej alternatywy :-) Używałem programu testującego ReSharper do uruchomienia testu MS Unit, a kod, który testowałem, potrzebował określonego pliku .dll, aby był w katalogu wykonawczym. .. i Assembly.GetExecutingDirectory () dziwnie zwraca inny wynik.
wallismark
1
@Chris - w obronie tej odpowiedzi. Działa w przypadku testów jednostkowych, rozwiązanie GetEntryAssembly nie działa, ponieważ GetEntryAssembly zwraca wartość null. Odpowiedzi, które proponują GetExecutingAssembly są fałszywe, ponieważ zwracają plik wykonywalny tylko wtedy, gdy zestaw wykonawczy jest plikiem wykonywalnym. To nie jest proste, ale prawidłowe rozwiązanie.
zaznacz
44

Dla wszystkich zainteresowanych aplikacjami internetowymi asp.net. Oto moje wyniki 3 różnych metod

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);
}

wynik

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

aplikacja działa fizycznie z „C: \ inetpub \ SBSPortal_staging”, więc pierwsze rozwiązanie zdecydowanie nie jest odpowiednie dla aplikacji internetowych.

rocketsarefast
źródło
42

Powyższa odpowiedź wyniosła 90% tego, czego potrzebowałem, ale zwróciło mi Uri zamiast zwykłej ścieżki dla mnie.

Jak wyjaśniono w poście na forach MSDN, jak przekonwertować ścieżkę URI na normalną ścieżkę do pliku? , Użyłem następujących:

// Get normal filepath of this assembly's permanent directory
var path = new Uri(
    System.IO.Path.GetDirectoryName(
        System.Reflection.Assembly.GetExecutingAssembly().CodeBase)
    ).LocalPath;
syczono
źródło
1
działa to również dobrze, jeśli exe to usługa Windows, a bieżący katalog zwraca C: \ Windows \ system32. Powyższy kod zwraca rzeczywistą lokalizację exe
DaImTo
Z wyjątkiem sytuacji, gdy spróbujesz zrobić coś takiego File.CreateDirectory(path), da ci wyjątek, że nie zezwala na ścieżki URI ...
vapcguy
1
Niestety nie działa to w przypadku ścieżek zawierających identyfikator fragmentu ( #znak). Identyfikator i wszystko po nim jest obcinane ze ścieżki wynikowej.
bgfvdu3w
Dlaczego nie zamienisz się new Urii System.IO.Path.GetDirectoryName? To daje normalny ciąg ścieżki zamiast Uri.
Timo,
Uważam to za najlepsze. To samo podejście działało dla mnie niezawodnie w każdym środowisku. W produkcji, lokalnie debugowanie, testowanie jednostkowe ... Chcesz otworzyć plik zawartości, który zawarłeś („treść - skopiuj, jeśli nowsza”) w teście jednostkowym? Jest tutaj.
Timo,
29

Być może chcesz to zrobić:

System.IO.Path.GetDirectoryName(
    System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)
ist_lion
źródło
23

możesz zamiast tego użyć tego.

System.Environment.CurrentDirectory
ButtShock
źródło
Spowoduje to pobranie folderu pliku wykonywalnego
Iain
Można to zmienić na wiele sposobów (ustawienia skrótów itp.) ... lepiej NIE używać.
Yousha Aleayoub
23

Jeśli szukasz sposobu zgodnego z .NET Core, użyj

System.AppContext.BaseDirectory

Zostało to wprowadzone w .NET Framework 4.6 i .NET Core 1.0 (i .NET Standard 1.3). Zobacz: Właściwość AppContext.BaseDirectory .

Według tej strony ,

Jest to preferowany zamiennik AppDomain.CurrentDomain.BaseDirectory w .NET Core

Dejan
źródło
zobacz także github.com/dotnet/runtime/issues/13051, aby zapoznać się z samodzielnymi aplikacjami konsolowymi dotnet. Zalecane jest użycieProcess.GetCurrentProcess().MainModule.FileName
Gavin
19

W przypadku aplikacji konsolowych możesz spróbować:

System.IO.Directory.GetCurrentDirectory();

Dane wyjściowe (na moim komputerze lokalnym):

c: \ users \ xxxxxxx \ dokumenty \ visual studio 2012 \ Projekty \ ImageHandler \ GetDir \ bin \ Debug

Lub możesz spróbować (na końcu jest dodatkowy ukośnik odwrotny):

AppDomain.CurrentDomain.BaseDirectory

Wynik:

c: \ users \ xxxxxxx \ dokumenty \ visual studio 2012 \ Projekty \ ImageHandler \ GetDir \ bin \ Debug \

F.Alves
źródło
BaseDirectoryMożna to ustawić w środowisku uruchomieniowym. NIE gwarantuje się poprawności”
Yousha Aleayoub,
14

Użyłem tego kodu i otrzymałem rozwiązanie.

AppDomain.CurrentDomain.BaseDirectory
Oprogramowanie Trimantra
źródło
9

Możesz po prostu dodać do referencji do projektu, System.Windows.Formsa następnie użyć System.Windows.Forms.Application.StartupPath jak zwykle.

Nie potrzeba więc bardziej skomplikowanych metod ani korzystania z odbicia.

fruggiero
źródło
Użyłem tego i działa dobrze. Ale pewnego razu użyłem tej metody w swoim projekcie testu jednostkowego. I oczywiście nie powiodło się, ponieważ szukał mojego pliku w C: \ PROGRAM PLIKI (X86) \ MICROSOFT VISUAL STUDIO 14.0 \ COMMON7 \ IDE \ COMMONEXTENSIONS \ MICROSOFT \ TESTWINDOW
ainasiart
@ainasiart, więc jak mogę to uruchomić podczas testowania jednostek?
Nicholas Siegmundt,
7

Używam tego, jeśli exe ma zostać wywołany przez podwójne kliknięcie

var thisPath = System.IO.Directory.GetCurrentDirectory();
developer747
źródło
5
To nie jest poprawne, ponieważ możesz uzyskać losowe katalogi.
osobliwy
to polecenie zwraca Environment.CurrentDirectory, które można zmienić w czasie wykonywania na dowolną ścieżkę, więc nie jest to niezawodne rozwiązanie.
Yury Kozlov
7

używałem

System.AppDomain.CurrentDomain.BaseDirectory

kiedy chcę znaleźć ścieżkę względem folderu aplikacji. Działa to zarówno dla aplikacji ASP.Net, jak i winform. Nie wymaga również żadnego odniesienia do zestawów System.Web.

użytkownik2346593
źródło
7

Następująca linia poda ścieżkę aplikacji:

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

Powyższe rozwiązanie działa poprawnie w następujących sytuacjach:

  • prosta aplikacja
  • w innej domenie, w której Assembly.GetEntryAssembly () zwróci wartość null
  • Biblioteka DLL jest ładowana z zasobów Embedded jako tablica bajtów i ładowana do AppDomain jako Assembly.Load (byteArrayOfEmbeddedDll)
  • z pakietami Mono mkbundle(żadne inne metody nie działają)
użytkownik2126375
źródło
Pod debuggerem na Linuksie zwraca: / usr / share / dotnet
Vladimir
6

Mam na myśli, dlaczego nie zastosować metody 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());
                }
            }
    }

Użyłbyś go tak jak Application.StartupPath:

    Console.WriteLine("The path to this executable is: " + AppInfo.StartupPath + "\\" + System.Diagnostics.Process.GetCurrentProcess().ProcessName + ".exe");
użytkownik3596865
źródło
2
Po co p / wywoływać, skoro jest na to tyle platformy .NET?
ProfK
7
@ user3596865, ponieważ wymaga silnej zależności od systemu Windows i nie jest kompatybilny z DNX lub Mono. Być może w przyszłych wersjach systemu Windows nastąpi przełomowa zmiana. Więc jeszcze raz: dlaczego powinniśmy tutaj używać pinvoke?
Ben
5

Assembly.GetEntryAssembly().Location lub Assembly.GetExecutingAssembly().Location

Użyj w połączeniu z, System.IO.Path.GetDirectoryName()aby uzyskać tylko katalog.

Te ścieżki GetEntryAssembly()i GetExecutingAssembly()mogą być różne, chociaż w większości przypadków katalog będzie taka sama.

Ze GetEntryAssembly()trzeba mieć świadomość, że to może powrócić null, gdy moduł wejścia jest niekontrolowana (czyli C ++ lub VB6 wykonywalny). W takich przypadkach można korzystać GetModuleFileNamez interfejsu API Win32:

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetModuleFileName(HandleRef hModule, StringBuilder buffer, int length);
Herman
źródło
5

w VB.net

My.Application.Info.DirectoryPath

działa dla mnie (Typ aplikacji: Biblioteka klas). Not sure about C # ... Zwraca ścieżkę bez nazwy pliku jako ciąg

dba
źródło
4
AppDomain.CurrentDomain.BaseDirectory

Rozwiązuje problem polegający na odsyłaniu plików referencyjnych innych firm z pakietami instalacyjnymi.

Nirav Mehta
źródło
11
Ta odpowiedź została już zasugerowana 5 lat temu, nawet więcej niż raz.
PL
2

Żadna z tych metod nie działa w szczególnych przypadkach, takich jak użycie dowiązania symbolicznego do pliku exe, zwracają one lokalizację łącza, a nie faktyczny plik exe.

Więc można użyć QueryFullProcessImageName, aby obejść ten problem :

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) + @"\";
    }
}
Colin Lamarre
źródło
2

Wypróbuj tę prostą linię kodu:

 string exePath = Path.GetDirectoryName( Application.ExecutablePath);
daniele3004
źródło
1

Innym rozwiązaniem jest użycie ścieżek względnych wskazujących bieżącą ścieżkę:

Path.GetFullPath(".")
blenderfreaky
źródło
Pobiera bieżący katalog, a nie lokalizację początkowego pliku EXE.
tenfour
0

Nie widziałem, żeby ktokolwiek konwertował ścieżkę LocalPath dostarczoną przez odbicie .Net Core na użyteczną ścieżkę System.IO, więc oto moja wersja.

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

   return new FileInfo(exePath).DirectoryName;

}

Zwróci to pełną ścieżkę w formacie „C: \ xxx \ xxx” do miejsca, w którym znajduje się kod.

zaznacz gamache
źródło
-1

Oto niezawodne rozwiązanie, które działa z aplikacjami 32 - bitowymi i 64 - bitowymi .

Dodaj te referencje:

using System.Diagnostics;

using System.Management;

Dodaj tę metodę do swojego projektu:

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;
}

Teraz użyj go w następujący sposób:

int RootProcessId = Process.GetCurrentProcess().Id;

GetProcessPath(RootProcessId);

Zauważ, że jeśli znasz identyfikator procesu, ta metoda zwróci odpowiednią ścieżkę ExecutePath.

Dodatkowo dla zainteresowanych:

Process.GetProcesses() 

... da ci tablicę wszystkich aktualnie uruchomionych procesów i ...

Process.GetCurrentProcess()

... da ci bieżący proces, wraz z jego informacjami, np. Id, itp., a także ograniczoną kontrolę, np. Kill itp. *

WonderWorker
źródło
-5

Możesz utworzyć nazwę folderu jako Zasoby w projekcie za pomocą Eksploratora rozwiązań, a następnie możesz wkleić plik w Zasobach.

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);
}
Devarajan.T
źródło
6
Korzystanie z Environment.CurrentDirectory jest bardzo złe, nie używaj tego! ta ścieżka może się zmienić w czasie wykonywania. Nawet przy starcie jest niedeterministyczny.
usr