Як я можу отримати шлях до програми в консольній програмі .NET?


953

Як знайти шлях до програми в консольному додатку?

У Windows Forms я можу Application.StartupPathзнайти поточний шлях, але це, здається, не доступне в консольній програмі.


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

AppDomain.BaseDirectory - це каталог додатків. Майте на увазі, що програма може поводитися по-різному в VS env та Win env. Але AppDomain повинен бути таким же не як application.path, але я сподіваюся, що це стосується не лише IIS.
Мертуарес

Відповіді:


1179

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

Поєднайте це з тим, System.IO.Path.GetDirectoryNameякщо все, що вам потрібно, - це каталог.

1 Відповідно до коментаря містера Міндора:
System.Reflection.Assembly.GetExecutingAssembly().Location повертається там, де знаходиться збірка, що виконує, яка може бути, а може і не бути там, де збірка знаходиться під час не виконання. У випадку складання тіньових копіювальних збірок ви отримаєте шлях у тимчасовий каталог. System.Reflection.Assembly.GetExecutingAssembly().CodeBaseповерне "постійний" шлях складання.


243
System.Reflection.Assembly.GetExecutingAssembly (). Місцезнаходження повертається там, де наразі виконується збірка , яка може бути, а може і не бути там, де розташована збірка, коли не виконується. У випадку складання тіньових копіювальних збірок ви отримаєте шлях у тимчасовий каталог. System.Reflection.Assembly.GetExecutingAssembly (). CodeBase поверне шлях " permenant " збірки.
Містер Міндор

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.
bitbonk

3
Будь ласка, напишіть код у 4 місцях, щоб було зручно копіювати
fnc12

3
якщо ви викликаєте dll, System.Reflection.Assembly.GetExecutingAssembly (). CodeBase отримає "файл: /// C: /Windows/Microsoft.NET/Framework64/v4.0.30319/mscorlib.dll"
raidsan

407

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

AppDomain.CurrentDomain.BaseDirectory

42
Не використовуйте це. BaseDirectory можна встановити під час виконання. Це не гарантовано правильність (як прийнята відповідь).
usr

3
+1 Це, швидше за все, потрібна відповідь, оскільки вона компенсує тіньове копіювання.
Джордж Мауер

4
@usr Що змушує вас вважати, що BaseDirectoryце можна встановити під час виконання? У ньому є лише геттер.
бітбонк

3
@bitbonk його можна встановити під час створення додатка.
usr

3
Чи не так, що BaseDirectory можна змінити у файлі * .lnk у полі "Почати в:"?
Олександр

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
Просто хотів сказати, очевидно, що існує більше двох варіантів, скільки інших варіантів розміщено ...
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 - дивлячись на стовпчик cmkm лінії taskmgr, це резервне копіювання того, що я кажу. Кілька системних сервісів із лише ім'ям exe. Не звертай уваги. Що я намагаюся сказати, це те, що при розробці консольного додатка немає потреби ускладнювати речі, ніж вони повинні бути. Особливо, коли у нас вже є інформація. Тепер, якщо ви запускаєте консольний додаток таким чином, щоб обдурити GetCommandLineArgs, тоді ви вже стрибаєте через обручі, і вам, ймовірно, доведеться запитати себе, чи консольний додаток - це правильний шлях.
Стів Мак

5
Ваше "просте" рішення включає два виклики методу. "Складне" рішення включає два виклики методу. Ніякої різниці в практиці - за винятком того, що "просте" рішення може дати вам неправильну відповідь за певних обставин, які не знаходяться під вашим контролем під час написання програми. Навіщо ризикувати? Використовуйте інші два виклики методу, і ваша програма не буде складнішою, але буде більш надійною.
Кріс

3
Інші рішення не працювали за моїм сценарієм, тому дякую за надання іншої альтернативи :-) Я використовував тестовий бігун ReSharper для запуску тесту MS Unit, і код, який я тестував, потребував певного .dll, щоб бути у виконанні каталогу. ..і Assembly.GetExecutingDirectory () дивно повертає інший результат.
wallismark

1
@Chris - на захист цієї відповіді. Він працює для одиничних тестів, рішення GetEntryAssembly не робить, тому що GetEntryAssembly повертає нуль. Відповіді, які пропонують GetExecutingAssembly, є неправдивими, оскільки вони повертають виконуваний файл лише у тому випадку, коли виконується збірка є виконуваною. Це не просте, але правильне рішення.
позначте

44

Для всіх, хто цікавиться веб-додатками asp.net. Ось мої результати трьох різних методів

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% від того, що мені потрібно, але повернув Урі замість звичайного шляху для мене.

Як пояснено у публікації на форумах 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
це добре працює також, якщо екзе, про який йде мова, - це служба 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

Це можна змінити кількома способами (налаштування ярликів тощо) ... краще НЕ використовувати його.
Yousha Aleayoub

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


Дивіться також github.com/dotnet/runtime/isissue/13051 щодо автономних додатків консолі dotnet . Тут рекомендується використовуватиProcess.GetCurrentProcess().MainModule.FileName
Гавін

19

Для консольних програм ви можете спробувати це:

System.IO.Directory.GetCurrentDirectory();

Вихід (на моїй локальній машині):

c: \ користувачів \ xxxxxxx \ документи \ візуальна студія 2012 \ Проекти \ ImageHandler \ GetDir \ bin \ Налагодження

Або ви можете спробувати (у кінцевому підсумку є додатковий зворотний кут):

AppDomain.CurrentDomain.BaseDirectory

Вихід:

c: \ користувачів \ xxxxxxx \ документи \ візуальна студія 2012 \ Проекти \ ImageHandler \ GetDir \ bin \ Налагодження \


"Канал BaseDirectoryможе бути встановлений під час виконання. НЕ гарантовано, що він правильний"
Yousha Aleayoub


9

Ви можете просто додати до своїх посилань на проект, System.Windows.Formsа потім використовувати System.Windows.Forms.Application.StartupPath як завжди.

Отже, не потрібно більш складних методів чи використання рефлексії.


Я використав цей, і він працює добре. Але одного разу я застосував метод, що його застосовував у своєму проектному тестовому проекті. І звичайно, це не вдалося, тому що він шукав мій файл у C: \ FILES PROGRAM (X86) \ MICROSOFT VISUAL STUDIO 14.0 \ COMMON7 \ IDE \ COMMONEXTENSIONS \ MICROSOFT \ TESTWINDOW
ainasiart

@ainasiart так як я можу змусити це працювати під час тестування одиниць ??
Ніколас

7

Я використовую це, якщо передбачається викликати exe, двічі клацнувши його

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

5
Це неправильно, оскільки в результаті ви можете отримати випадкові каталоги.
amuliar

ця команда повертає Environment.CurrentDirectory, який може бути змінений під час виконання будь-якого шляху, тому це не є надійним рішенням.
Юрій Козлов

7

Я звик

System.AppDomain.CurrentDomain.BaseDirectory

коли я хочу знайти шлях відносно папки програм. Це працює як для програм ASP.Net, так і для Winform. Він також не вимагає посилань на збори System.Web.


7

Наступний рядок дасть вам шлях до програми:

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

Вищевказане рішення працює належним чином у наступних ситуаціях:

  • просте додаток
  • в іншому домені, де Assembly.GetEntryAssembly () поверне null
  • DLL завантажується із вбудованих ресурсів у вигляді байтового масиву та завантажується в AppDomain як Assembly.Load (byteArrayOfEmbeddedDll)
  • з mkbundleпакетами Mono (інші методи не працюють)

Під налагодженням на Linux це повертається: / usr / share / dotnet
Володимир

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 / call, коли для цього існує стільки .NET?
ПрофК

7
@ user3596865, оскільки він вимагає жорсткої залежності від Windows і не сумісний з DNX або Mono. І, можливо, в майбутніх версіях Windows відбудуться кардинальні зміни. Отже ще раз: чому ми повинні тут використовувати Pinvoke?
Бен

5

Assembly.GetEntryAssembly().Location або Assembly.GetExecutingAssembly().Location

Використовуйте в поєднанні з, System.IO.Path.GetDirectoryName()щоб отримати лише каталог.

Шляхи з GetEntryAssembly() та GetExecutingAssembly()можуть бути різними, хоча для більшості випадків каталог буде однаковим.

З GetEntryAssembly()ви повинні знати , що це може повернутися , nullякщо модуль входу є некерованим (тобто C ++ або VB6 виконуваний файл). У цих випадках можливо використовувати GetModuleFileNameAPI Win32:

[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

Жоден із цих методів не працює в особливих випадках, наприклад, використовуючи символьне посилання на exe, вони повернуть розташування посилання, а не фактичне exe.

Отже, використовуйте 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);


0

Я не бачив, щоб хтось конвертував локальний шлях, наданий .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()

... дасть вам поточний процес, разом з їх інформацією, наприклад, ідентифікатор тощо, а також обмежений контроль, наприклад, вбити тощо. *


-5

Ви можете створити ім'я папки як Ресурси в проекті за допомогою Провідника рішень, а потім можете вставити файл в Ресурси.

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.