Як мені отримати шлях складання коду?


781

Чи є спосіб отримати шлях для складання, в якому знаходиться поточний код? Я не хочу шлях до виклику, що викликає, лише той, що містить код.

В основному мій тестовий модуль повинен прочитати деякі тестові файли xml, які розташовані відносно dll. Я хочу, щоб шлях завжди вирішувався правильно, незалежно від того, тестовий dll запускається з TestDriven.NET, графічного інтерфейсу MbUnit або чогось іншого.

Редагувати : Здається, люди не розуміють того, про що я прошу.

Моя тестова бібліотека розташована у скажімо

C: \ projects \ myapplication \ daotests \ bin \ Debug \ daotests.dll

і я хотів би пройти цей шлях:

C: \ projects \ myapplication \ daotests \ bin \ Debug \

Три мої пропозиції поки що не підлягають мене, коли я біжу від MbUnit Gui:

  • Environment.CurrentDirectory дає c: \ Файли програм \ MbUnit

  • System.Reflection.Assembly.GetAssembly(typeof(DaoTests)).Location дає C: \ Документи та налаштування \ george \ Місцеві налаштування \ Temp \ .... \ DaoTests.dll

  • System.Reflection.Assembly.GetExecutingAssembly().Location дає те саме, що і попереднє.


102
Це ваше рішення: var dir = AppDomain.CurrentDomain.BaseDirectory;
Джалал Ель-Шаер

7
Це має бути прийнятим рішенням. AppDomain.CurrentDomain.BaseDirectory - це правильний підхід.
aBetterGamer


2
Я прийшов сюди, шукаючи рішення для пакунків-нотаток, щоб прочитати файл JSON з каталогу pacakge. Здається, що при виконанні пакунка з цілими пакунками "AppDomain.CurrentDomain.BaseDirectory" вказує на каталог запущених проектів, а не на каталог пакунків нугетів. Здається, що жодне з них не орієнтоване правильно на каталог пакунків.
Лукас

@Lucas ні, ні, тому що це питання не стосувалося цього (насправді, коли його запитали, нугета не існувало) - сміливо почніть нове запитання і надішліть мені пінг, але я можу вам сказати, що зараз це неможливо в більшості випадків. Для більшості проектів каталог нута знаходиться packagesпоруч із файлом sln. АЛЕ при компілюванні та розповсюдженні речей немає файлу sln та каталогу пакунків. Під час компіляції потрібні речі (але не все) копіюються в каталог бін. Найкраще скористатися сценарієм після побудови, щоб скопіювати потрібний файл.
Джордж Мауер

Відповіді:


1036

Я визначив наступне властивість, оскільки ми часто використовуємо це при тестуванні одиниць.

public static string AssemblyDirectory
{
    get
    {
        string codeBase = Assembly.GetExecutingAssembly().CodeBase;
        UriBuilder uri = new UriBuilder(codeBase);
        string path = Uri.UnescapeDataString(uri.Path);
        return Path.GetDirectoryName(path);
    }
}

Assembly.LocationВластивість іноді дають деякі забавні результати при використанні NUnit (де вузли запуску з тимчасової папки), тому я вважаю за краще використовувати , CodeBaseяка дає вам шлях в форматі URI, а потім UriBuild.UnescapeDataStringвидаляє File://на початку, і GetDirectoryNameзмінює його в формат нормальних вікон .


29
У мене є одна проблема, з якою я зіткнувся, якщо назва вашого каталогу: c: \ My% 20Directory, тоді Uri.UnescapeDataString повернеться: c: \ My Directory Це означає, що File.Exists ("c: \ My Directory \ MyFile.txt ") поверне помилковим, оскільки правильний шлях насправді" c: \ My% 20Directory \ MyFile.txt "Я натрапив на це, оскільки наші шляхи SVN мають у них пробіли, і коли ми перевіряємо їх, він кодує пробіли.
рядок1

5
Будьте обережні, використовуючи це для перевірки File.Exist (), оскільки цей метод поверне помилковий шлях до UNC. Використовуйте натомість відповідь @ Keith.
AZ.

3
Не знав, що можна поставити статику перед публікою. Приємно знати, і я думаю, що я віддаю перевагу читабельності
Валамас

5
Примітка: це не працює з мережевими місцями (наприклад, \\ REMOT_EPC \ Folder)
Muxa,

5
Також це не спрацює, якщо в каталозі є числові знаки "#". Цифрові знаки дозволені в назвах директорій та файлів у Windows.
Huemac

321

Чи допомагає це?

//get the full location of the assembly with DaoTests in it
string fullPath = System.Reflection.Assembly.GetAssembly(typeof(DaoTests)).Location;

//get the folder that's in
string theDirectory = Path.GetDirectoryName( fullPath );

дивіться мою редагування, це не так, це щось дивне в тому, як MbUnit робить речі?
Джордж Мауер

3
Встановіть файли xml вмістом, скопійованим з dll або ресурсами, прочитаними з dll.
Кіт

22
Або простоtypeof(DaoTests).Assembly
SLaks

4
@SLaks @JohnySkovdal @Keith: Ей, хлопці, використовуйте Assembly.GetExecutingAssembly(). Він "отримує збірку, що містить код, який зараз виконується" (з опису методу). Я використовую це в моєму «надбудові EntitiesToDTOs ». Дивіться AssemblyHelper.cs для реального прикладу.
kzfabi

4
Виникла проблема з публікацією від @John Silby, оскільки вона не схожа на те, що вона працює для UNC-шляхів ... наприклад, \\ Сервер \ Папка \ File.ext. Цей зробив трюк. +1
Чорниця

312

Це так просто, як це:

var dir = AppDomain.CurrentDomain.BaseDirectory;

11
Це має бути прийнятим рішенням. AppDomain.CurrentDomain.BaseDirectory - це правильний підхід.
aBetterGamer

5
дякую, що повернули мою увагу до цього - не впевнений, чи це було доступне в той момент, коли я задав питання, але це зараз.
Джордж Мауер

120
Ні, це неправильно. Це повертає шлях ОРИГІНАЛЬНОГО ВХІДНОГО ТОЧКА, а не поточного виконуючого коду. Якщо ви завантажили збірку вручну з іншого шляху або якщо вона була завантажена з GAC, вона поверне неправильний результат. Ця відповідь правильна: stackoverflow.com/a/283917/243557 Більш швидкий все ще є Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location).
nathanchere

9
Насправді це не працюватиме у веб-додатках, але, наскільки я виявив, наступні додатки повинні працювати для будь-якого типу додатків:AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory
Ілля Черномордик

4
Це відмінно підходить для тестування одиниць, якщо ви просто хочете отримати початковий шлях бін вашої тестової збірки (скажімо, для отримання файлів допоміжних даних у підпапках). Тестова збірка є точкою входу вашого коду.
MarioDS

68

Те саме, що відповідь Джона, але трохи менш дослівний метод продовження.

public static string GetDirectoryPath(this Assembly assembly)
{
    string filePath = new Uri(assembly.CodeBase).LocalPath;
    return Path.GetDirectoryName(filePath);            
}

Тепер ви можете зробити:

var localDir = Assembly.GetExecutingAssembly().GetDirectoryPath();

або якщо ви віддаєте перевагу:

var localDir = typeof(DaoTests).Assembly.GetDirectoryPath();

6
Ти мав на увазі assemblyзамість Assembly.GetExecutingAssembly()?
Чувак Паскалу

3
Як зазначає Чувак, ви передали аргумент і не змогли його використати.
Кріс Москіні

4
Ця відповідь якраз неправильна для питання. Змінена версія цієї відповіді може дати вам шлях до певної збірки. Однак тут ми спеціально шукаємо виконаючу збірку, і тому проходити в зборі немає сенсу. Метод розширення - неправильний інструмент для роботи.
Едвард Брей

46

Єдине рішення, яке працювало для мене при використанні акцій CodeBase та UNC Network:

System.IO.Path.GetDirectoryName(new System.Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath);

Він також працює і зі звичайними URI.


5
Це має бути прийнятою відповіддю. Це дуже прикро, що база даних за замовчуванням не відповідає правильним поділам UNC.
Даніель Гілберт

Це руйнується, коли в папці є пробіли, і Бог знає, які інші символи ...
MarioDS

1
Я багато цього використовував і знайшов один сценарій, коли він не вдається: якщо сам цей рядок коду є частиною пакету NuGet, який потім використовується додатком! Ми також можемо підтримати цей сценарій, замінивши GetExecutingAssembly()на GetCallingAssembly().
Тимо

@Timo: Ви перевірили, чи має ця зміна побічні ефекти? Якщо це так, відредагуйте відповідь, щоб включити виправлення.
Ігнасіо Солер Гарсія

@IgnacioSolerGarcia На жаль, я повинен повідомити, що він працював лише на один шар глибоко, тобто він не вдається, якщо пакет NuGet був викликаний іншим пакетом NuGet! Я тепер з допомогою цього (з коментаря сюди через Чорномор): AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory. Перша частина - для веб-додатків, а друга - для інших програм.
Тимо

32

Це повинно працювати, якщо збірка не скопійована в тіні :

string path = System.Reflection.Assembly.GetExecutingAssembly().Location

14

Як що до цього:

System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

11

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

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

Чи розглядали ви вбудовувати свої дані XML в якості ресурсів всередині тестової збірки?


+1, щоб вказати на проблему при тіньовому копіюванні. Однак визначити оригінальне місце із с Assembly.CodeBase.
tm1


10

Я вважаю, що це спрацює для будь-якого типу додатків:

AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory

1
Мої експерименти показують, що це найбезпечніша відповідь, яка охоплює не лише веб-та консольні програми, а й дзвінки з тестів одиниць та пакетів NuGet (вкладених до будь-якого рівня рекурсії).
Тимо

8

Наскільки я можу сказати, більшість інших відповідей мають кілька проблем.

Правильний спосіб зробити це для дискової (на відміну від веб-базованої) збірки, яка не є GACed, - використовувати CodeBaseвластивість виконуючої в даний час збірки .

Це повертає URL-адресу ( file://). Замість того, щоб возитися за допомогою маніпуляцій зі струнами або UnescapeDataString, це можна перетворити з мінімальною суєтою, використовуючи LocalPathвластивість Uri.

var codeBaseUrl = Assembly.GetExecutingAssembly().CodeBase;
var filePathToCodeBase = new Uri(codeBaseUrl).LocalPath;
var directoryPath = Path.GetDirectoryName(filePathToCodeBase);

1
Не працює, якщо шлях містить #( EscapedCodeBaseпрацює, але EscapedCodeBase не працює, якщо шлях містить, наприклад, %20дослівний (що є дозволеною послідовністю символів у шляху Windows)
Мартін Ба,

Якщо ми хочемо мати цей код у пакеті NuGet, ми можемо виправити цей сценарій, замінивши GetExecutingAssembly()на GetCallingAssembly().
Тимо

8

Як щодо цього ...

string ThisdllDirectory = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

Тоді просто зламайте те, що вам не потрібно


7
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var assemblyPath = assembly.GetFiles()[0].Name;
var assemblyDir = System.IO.Path.GetDirectoryName(assemblyPath);

7

Ось порт VB.NET з кодом Джона Сіблі. Visual Basic не відрізняється від регістру, тому пара його імен змінних зіткнулася з іменами типів.

Public Shared ReadOnly Property AssemblyDirectory() As String
    Get
        Dim codeBase As String = Assembly.GetExecutingAssembly().CodeBase
        Dim uriBuilder As New UriBuilder(codeBase)
        Dim assemblyPath As String = Uri.UnescapeDataString(uriBuilder.Path)
        Return Path.GetDirectoryName(assemblyPath)
    End Get
End Property

6

Протягом усіх цих років насправді ніхто не згадував. Трюк я дізнався з дивовижного проекту ApprovalTests . Хитрість полягає в тому, що ви використовуєте інформацію про налагодження в збірці, щоб знайти оригінальний каталог.

Це не працюватиме в режимі RELEASE, ні з увімкненими оптимізаціями, ні на машині, відмінній від тієї, на якій було складено.

Але це дозволить отримати шляхи, що відносяться до місця розташування файлу вихідного коду, з якого ви його викликаєте

public static class PathUtilities
{
    public static string GetAdjacentFile(string relativePath)
    {
        return GetDirectoryForCaller(1) + relativePath;
    }
    public static string GetDirectoryForCaller()
    {
        return GetDirectoryForCaller(1);
    }


    public static string GetDirectoryForCaller(int callerStackDepth)
    {
        var stackFrame = new StackTrace(true).GetFrame(callerStackDepth + 1);
        return GetDirectoryForStackFrame(stackFrame);
    }

    public static string GetDirectoryForStackFrame(StackFrame stackFrame)
    {
        return new FileInfo(stackFrame.GetFileName()).Directory.FullName + Path.DirectorySeparatorChar;
    }
}

5

Поточний каталог, де ви існуєте.

Environment.CurrentDirectory;  // This is the current directory of your application

Якщо ви скопіюєте .xml файл із збірки, його слід знайти.

або

System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly(typeof(SomeObject));

// The location of the Assembly
assembly.Location;

це буде проблематично, якщо збірка була скопійована в тінь .
витрачається

+1520! Environment.CurrentDirectoryпрацює, якщо ви використовуєте відображення в класі завдань MSBuild, де виконана збірка знаходиться в GAC, а ваш код - десь в іншому місці.
вулкан ворон

4
Загалом CurrentDirectory не каже вам, де розміщуються ваші файли. Це не для чого використовується. Часто буває, що вони є одним і тим же місцем, у якому виконуються файли, тому багато програмістів не розуміють різниці. Потім вони створюють проблеми для деяких кінцевих користувачів, які очікували, що програма зрозуміє правильне використання CurrentDirectory.
Бент Транберберг

5

Я використовую Assembly.CodeBase замість Location:

Assembly a;
a = Assembly.GetAssembly(typeof(DaoTests));
string s = a.CodeBase.ToUpper(); // file:///c:/path/name.dll
Assert.AreEqual(true, s.StartsWith("FILE://"), "CodeBase is " + s);
s = s.Substring(7, s.LastIndexOf('/') - 7); // 7 = "file://"
while (s.StartsWith("/")) {
    s = s.Substring(1, s.Length - 1);
}
s = s.Replace("/", "\\");

Це працює, але я вже не впевнений, що це на 100% правильно. На сторінці http://blogs.msdn.com/suzcook/archive/2003/06/26/assembly-codebase-vs-assembly-location.aspx сказано:

"CodeBase - це URL-адреса до місця, де був знайдений файл, тоді як Location - це шлях, де він був фактично завантажений. Наприклад, якщо збірку було завантажено з Інтернету, її CodeBase може починатися з" http: // " , але його Місце розташування може починатися з "C: \". Якби файл був скопійований у тінь, Місце розташування - це шлях до копії файлу в тіньовій копірі dir. Також добре знати, що CodeBase не гарантується має бути встановлено для збірок в GAC. Однак місце завжди буде встановлено для збірок, завантажених з диска. "

Ви можете використовувати CodeBase замість Location.


1
@Kiquenet: Стільки коду просто для перетворення URI в шлях. Звичайно, це можна було б покращити. Подивіться на відповідь Майка Шалла або SoMoS. Не слід намагатися перетворити URI на рівні рядка, а натомість використовувати відповідні об'єкти. Гаразд, також незграбно, що Assembly.CodeBase повертає рядок замість більш підходящого об'єкта, наприклад URI або FileInfo.
Сім

2

Ви можете отримати шлях для сміття за допомогою AppDomain.CurrentDomain.RelativeSearchPath


2

Усі запропоновані відповіді працюють, коли розробник може змінити код, щоб він включив необхідний фрагмент, але якщо ви хочете це зробити, не змінюючи жодного коду, ви можете використовувати Process Explorer.

У ньому будуть перераховані всі виконуючі dll в системі, можливо, вам потрібно буде визначити ідентифікатор процесу вашої запущеної програми, але це, як правило, не так вже й складно.

Я написав повний опис того, як це зробити для dll всередині II - http://nodogmablog.bryanhogan.net/2016/09/locating-and-checking-an-executing-dll-on-a-running-web -сервер /


Зауважте, що по-перше, код у статті досить орієнтований на IIS, а по-друге, він дає вам (я вважаю) всі завантажені на даний момент dlls, а не те, що працює в будь-який момент.
Джордж Мауер

Наведений приклад стосується iis, але ті ж самі кроки застосовуються, якщо dll працює в процесі поза iis. Це лише питання визначення ідентифікатора процесу. Я оновлю статтю, щоб відзначити це. Дякую за пропозицію.
Брайан

2

у додатку форми Windows, ви можете просто використовувати Application.StartupPath

але для DLL-програм і консольних програм запам'ятати код набагато складніше ...

string slash = Path.DirectorySeparatorChar.ToString();
string root = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

root += slash;
string settingsIni = root + "settings.ini"


1

Ви отримаєте неправильний каталог, якщо шлях містить символ "#". Тому я використовую модифікацію відповіді Джона Сіблі, що є комбінацією UriBuilder.Path і UriBuilder.Fragment:

public static string AssemblyDirectory
{
    get
    {
        string codeBase = Assembly.GetExecutingAssembly().CodeBase;
        UriBuilder uri = new UriBuilder(codeBase);
        //modification of the John Sibly answer    
        string path = Uri.UnescapeDataString(uri.Path.Replace("/", "\\") + 
          uri.Fragment.Replace("/", "\\"));
        return Path.GetDirectoryName(path);
     }
}

0

Це те, що я придумав. Між веб-проектами, модульні тести (nunit та resharper test runner) ; Я виявив, що це працює для мене.

Я шукав код , щоб виявити , що конфігурація збірки знаходиться, Debug/Release/CustomName. На жаль, #if DEBUG. Тож якщо хтось може це покращити !

Не соромтеся редагувати та вдосконалювати.

Отримання папки додатків . Корисно для веб-коренів, unittests для отримання папки тестових файлів.

public static string AppPath
{
    get
    {
        DirectoryInfo appPath = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory);

        while (appPath.FullName.Contains(@"\bin\", StringComparison.CurrentCultureIgnoreCase)
                || appPath.FullName.EndsWith(@"\bin", StringComparison.CurrentCultureIgnoreCase))
        {
            appPath = appPath.Parent;
        }
        return appPath.FullName;
    }
}

Отримання папки bin : корисно для виконання збірок за допомогою відображення. Якщо файли скопійовані туди завдяки властивостям збірки.

public static string BinPath
{
    get
    {
        string binPath = AppDomain.CurrentDomain.BaseDirectory;

        if (!binPath.Contains(@"\bin\", StringComparison.CurrentCultureIgnoreCase)
            && !binPath.EndsWith(@"\bin", StringComparison.CurrentCultureIgnoreCase))
        {
            binPath = Path.Combine(binPath, "bin");
            //-- Please improve this if there is a better way
            //-- Also note that apps like webapps do not have a debug or release folder. So we would just return bin.
#if DEBUG
            if (Directory.Exists(Path.Combine(binPath, "Debug"))) 
                        binPath = Path.Combine(binPath, "Debug");
#else
            if (Directory.Exists(Path.Combine(binPath, "Release"))) 
                        binPath = Path.Combine(binPath, "Release");
#endif
        }
            return binPath;
    }
}

0

Це має працювати:

ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
Assembly asm = Assembly.GetCallingAssembly();
String path = Path.GetDirectoryName(new Uri(asm.EscapedCodeBase).LocalPath);

string strLog4NetConfigPath = System.IO.Path.Combine(path, "log4net.config");

Я використовую це для розгортання бібліотек файлів DLL разом із деяким файлом конфігурації (це для використання log4net з файлу DLL).


Для чого тут fileMapвикористовується?
Джордж Мауер

0

Я вважаю своє рішення адекватним для пошуку місця розташування.

var executingAssembly = new FileInfo((Assembly.GetExecutingAssembly().Location)).Directory.FullName;

Це вже одна з найбільш оцінених відповідей і прямо в питанні згадується як щось, що не працює в цій ситуації.
Джордж Мауер

Вибачення повинні пропустити це! Очевидно, я не перечитав ретельно.
Тез Вінгфілд

0

Я мав таку ж поведінку в NUnitминулому. За замовчуванням NUnitкопіює збірку в тимчасовий каталог. Ви можете змінити цю поведінку в NUnitналаштуваннях:

введіть тут опис зображення

Можливо, TestDriven.NETі MbUnitGUI мають однакові налаштування.


-3

Я використовую це для отримання шляху до каталогу Bin:

var i = Environment.CurrentDirectory.LastIndexOf(@"\");
var path = Environment.CurrentDirectory.Substring(0,i); 

Ви отримуєте такий результат:

"c: \ користувачів \ ricooley \ документи \ візуальна студія 2010 \ Проекти \ Windows_Test_Project \ Windows_Test_Project \ bin"


6
Я не бачу причин уникати Path.getDirectoryName тут
Макс Келлер

@MaxKeller Якщо ви не бачите причин, це не означає, що це правильно. Цей альтернативний метод Path.GetDirectoryName вдесятеро швидший.
Руслан Веселов

-3

Веб-додаток?

Server.MapPath("~/MyDir/MyFile.ext")

2
@christiandev це відповідь, але, можливо, це здається відповіддю на неправильне запитання. З питання зрозуміло, що це не веб-додаток, а збірка, що проводиться з MbUnit. Однак, відповідь все ще не дуже правильна через тіньове копіювання Asp.Net (хоча, можливо, це може бути те, що хтось, хто приземлився з цього питання, шукає).
Джордж Мауер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.