Как получить путь к сборке, в которой находится код?

Есть ли способ получить путь к сборке, в которой находится текущий код? Я не хочу путь к вызывающей сборке, только тот, который содержит код.

В основном мой модульный тест должен прочитать некоторые тестовые файлы 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: \ Program Files \ MbUnit

  • System.Reflection.Assembly.GetAssembly(typeof(DaoTests)).Location дает C: \ Documents and Settings \ george \ Local Settings \ Temp \ .... \ DaoTests.dll

  • System.Reflection.Assembly.GetExecutingAssembly().Location дает так же, как и предыдущий.

9.09.2008 20:12:28
Это ваше решение: var dir = AppDomain.CurrentDomain.BaseDirectory;
Jalal El-Shaer 22.05.2010 09:15:00
Это должно быть принятым решением. AppDomain.CurrentDomain.BaseDirectory - правильный подход.
aBetterGamer 25.06.2013 15:14:56
Я пришел сюда в поисках решения для пакета nuget для чтения файла JSON из его каталога pacakge. Кажется, что при выполнении пакета nuget «AppDomain.CurrentDomain.BaseDirectory» указывает на каталог запущенных проектов, а не на каталог пакета nuget. Кажется, что ни один из них не нацелен на каталог пакета nuget правильно.
Lucas 29.07.2016 11:47:34
@ Лукас, нет, не будет, потому что это не то, о чем этот вопрос (на самом деле, когда его спросили, nuget не существует) - не стесняйтесь, чтобы начать новый вопрос и пинговать меня там, но я могу сказать вам прямо сейчас, что это невозможно в большинстве случаев. Для большинства проектов каталог nuget находится packagesрядом с файлом sln. НО, когда вы компилируете и распространяете вещи, нет файла sln и каталога пакетов. Во время компиляции все необходимое (но не все) копируется в каталог bin. Лучше всего использовать сценарий пост-сборки, чтобы скопировать нужный файл.
George Mauer 29.07.2016 19:12:35
28 ОТВЕТОВ
РЕШЕНИЕ

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

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изменяет его на обычный формат Windows ,

1029
27.08.2014 20:00:49
Это одна проблема, с которой я столкнулся, если ваше имя каталога: c: \ My% 20Directory, тогда Uri.UnescapeDataString вернет: c: \ My Directory Это означает, что File.Exists ("c: \ My Directory \ MyFile.txt ") вернет false, поскольку правильный путь на самом деле" c: \ My% 20Directory \ MyFile.txt ". Я сталкивался с этим, поскольку наши пути SVN содержат пробелы, и когда мы их проверяем, он кодирует пробелы.
row1 13.07.2010 09:31:03
Будьте внимательны, когда используете эту функцию для проверки File.Exist (), так как этот метод вернет false в пути UNC. Вместо этого используйте ответ @ Keith.
AZ. 8.12.2011 02:22:26
Не знал, что вы можете поставить статические перед публикой. Приятно знать, и я думаю, что я предпочитаю для читабельности
Valamas 3.07.2013 04:27:31
Примечание: это не работает с сетевыми местоположениями (например, \\ REMOT_EPC \ Folder)
Muxa 27.01.2014 22:37:02
Также это не будет работать, если в каталоге есть цифры #. Цифровые знаки допускаются в каталогах и именах файлов в Windows.
Huemac 17.12.2014 11:32:55

Это должно работать, если сборка не скопирована тенями :

string path = System.Reflection.Assembly.GetExecutingAssembly().Location
32
22.01.2015 08:37:58

Текущий каталог, в котором вы находитесь.

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;
5
9.09.2008 20:16:04
это будет проблематично, если сборка была скопирована тенями .
spender 17.06.2014 10:17:36
+1520! Environment.CurrentDirectoryработает, если вы используете отражение в классе задач MSBuild, где исполняемая сборка находится в GAC, а ваш код находится где-то еще.
vulcan raven 10.08.2014 17:37:35
В общем случае CurrentDirectory не сообщает вам, где находятся ваши исполняемые файлы. Это не то, для чего это используется. Просто бывает так, что исполняемые файлы находятся в одном месте, поэтому многие программисты не понимают разницы. Затем они создают проблемы для некоторых конечных пользователей, которые ожидали, что приложение поймет правильное использование CurrentDirectory.
Bent Tranberg 6.11.2016 19:55:39

Это помогает?

//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 );
319
20.07.2015 06:54:55
посмотрите мои правки, это не так, это что-то странное в том, как работает MbUnit?
George Mauer 9.09.2008 21:32:03
Установите файлы XML для содержимого, скопированного с помощью библиотеки DLL или ресурсов, прочитанных из библиотеки DLL.
Keith 9.09.2008 21:39:43
Или простоtypeof(DaoTests).Assembly
SLaks 16.03.2012 21:34:38
@SLaks @JohnySkovdal @Keith: Эй, ребята, используйте Assembly.GetExecutingAssembly(). Он «получает сборку, которая содержит код, который выполняется в данный момент» (из описания метода). Я использую это в моем AddIn " EntitiesToDTOs ". См. AssemblyHelper.cs для реального примера.
kzfabi 30.06.2012 05:24:16
У него была проблема с сообщением @John Silby, так как оно не похоже на то, что оно работает для путей UNC ... например, \\ Server \ Folder \ File.ext. Этот сделал свое дело. +1
Blueberry 9.08.2012 17:24:24
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var assemblyPath = assembly.GetFiles()[0].Name;
var assemblyDir = System.IO.Path.GetDirectoryName(assemblyPath);
7
22.01.2015 08:42:25

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

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

Рассматривали ли вы встраивание своих XML-данных в качестве ресурсов в тестовую сборку?

11
9.09.2008 21:36:04
+1 за указание на проблему с теневым копированием. Тем не менее, действительно возможно определить исходное место из Assembly.CodeBase.
tm1 13.06.2017 07:21:20

Как насчет этого:

System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
14
9.09.2008 21:40:06
string path = Path.GetDirectoryName(typeof(DaoTests).Module.FullyQualifiedName);
1
9.09.2008 21:46:48

Я использовал 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: // » , но его Location может начинаться с "C: \". Если файл был скопирован теневым копированием, Location будет путем к копии файла в директории теневого копирования. Также полезно знать, что CodeBase не гарантируется будет установлен для сборок в GAC. Однако расположение всегда будет устанавливаться для сборок, загруженных с диска. "

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

5
9.09.2008 21:50:49
@Kiquenet: так много кода только для преобразования URI в путь. Конечно, это может быть улучшено. Посмотрите на ответ Майка Шалла или SoMoS. Вы не должны пытаться конвертировать URI на строковом уровне, а вместо этого использовать подходящие объекты. Хорошо, также неуклюже, что Assembly.CodeBase возвращает строку вместо более подходящего объекта, такого как URI или FileInfo.
Seven 28.03.2014 20:39:01

Вот порт 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
7
19.07.2015 12:33:51

То же, что и ответ Джона, но чуть менее подробный метод расширения.

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();
67
7.04.2016 15:57:29
Вы имели в виду assemblyвместо Assembly.GetExecutingAssembly()?
Dude Pascalou 30.01.2014 16:42:24
Как указывает Чувак, вы передали аргумент и не смогли его использовать.
Chris Moschini 23.10.2014 15:42:00
Этот ответ просто неверен для рассматриваемого вопроса. Модифицированная версия этого ответа может дать вам путь к данной сборке. Однако здесь мы специально ищем исполняемую сборку, и поэтому передача в сборку не имеет смысла. Метод расширения является неправильным инструментом для работы.
Edward Brey 27.05.2015 12:48:06

Это так просто, как это:

var dir = AppDomain.CurrentDomain.BaseDirectory;
310
3.07.2012 13:01:06
Это должно быть принятым решением. AppDomain.CurrentDomain.BaseDirectory - правильный подход.
aBetterGamer 25.06.2013 15:15:16
спасибо за то, что обратили мое внимание на это - не уверен, было ли это доступно в то время, когда я задавал вопрос, но сейчас.
George Mauer 25.06.2013 18:52:03
Нет, это неправильно Это возвращает путь ОРИГИНАЛЬНОЙ ВХОДНОЙ ТОЧКИ, а не исполняемого в данный момент кода. Если вы загрузили сборку вручную по другому пути или если она была загружена из GAC, она вернет неправильный результат. Этот ответ правильный: stackoverflow.com/a/283917/243557 Быстрее все еще Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location).
nathanchere 2.08.2013 09:29:40
На самом деле это не будет работать в веб-приложениях, но, насколько я понял, для любого типа приложений должно работать следующее расширение:AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory
Ilya Chernomordik 14.08.2013 08:28:31
Это отлично подходит для модульного тестирования, если вы просто хотите получить исходный путь к бину вашей тестовой сборки (скажем, для доступа к файлам вспомогательных данных в подпапках). Тестовая сборка - это точка входа в ваш код.
MarioDS 15.04.2016 13:34:06
AppDomain.CurrentDomain.BaseDirectory

работает с интерфейсом MbUnit.

11
16.06.2014 13:02:58
Это отлично сработало для записи файла относительно корневого каталога в веб-приложении asp.net
Philip Pittle 1.10.2014 14:47:36
Я обнаружил, что этот работает лучше всего в целом. Выберите это, если вы не уверены.
Erik Bergstedt 19.02.2016 09:07:08

Я использую это, чтобы получить путь к Bin Directory:

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

Вы получаете этот результат:

"c: \ users \ ricooley \ Documents \ visual studio 2010 \ Projects \ Windows_Test_Project \ Windows_Test_Project \ bin"

-3
21.12.2011 18:44:53
Я не вижу причин избегать Path.getDirectoryName здесь
Max Keller 30.05.2012 09:24:10
@MaxKeller Если вы не видите причин, это не значит, что это правильно. Этот альтернативный метод Path.GetDirectoryName работает в десять раз быстрее.
Ruslan Veselov 5.11.2015 12:34:26

Единственное решение, которое работало для меня при использовании общих ресурсов CodeBase и UNC Network, было:

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

Это также работает с обычными URI тоже.

46
22.07.2014 09:58:19
Это должен быть принятый ответ. Это действительно раздражает, что кодовая база по умолчанию не обрабатывает общие ресурсы UNC правильно.
Daniel Gilbert 18.09.2013 13:52:51
Это происходит сбой, когда папка содержит пробелы и бог знает, что другие символы ...
MarioDS 2.11.2017 12:56:15
Я много использовал это и нашел один сценарий, где он терпит неудачу: если эта строка кода сама является частью пакета NuGet, который затем используется приложением! Мы также можем поддержать этот сценарий, заменив GetExecutingAssembly()на GetCallingAssembly().
Timo 9.09.2019 13:39:19
@Timo: вы проверили, есть ли у этого изменения побочные эффекты? Если это так, пожалуйста, отредактируйте ответ, чтобы включить исправление.
Ignacio Soler Garcia 20.09.2019 18:38:50
@IgnacioSolerGarcia К сожалению, я должен сообщить, что он работал только на один уровень, то есть он не работает, если пакет NuGet был вызван другим пакетом NuGet! Я теперь с помощью этого (из комментария на этой странице по Черномор): AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory. Первая часть предназначена для веб-приложений, а вторая - для других приложений.
Timo 22.09.2019 13:19:22

Это должно работать:

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

0
19.07.2015 12:37:25
Для чего здесь fileMapиспользуется?
George Mauer 12.06.2013 14:22:08

Это то, что я придумал. Между веб-проектами - юнит-тесты (nunit и resharper test runner) ; Я обнаружил, что это работает для меня.

Я искал код, чтобы определить, в какой конфигурации находится сборка Debug/Release/CustomName. Увы, #if DEBUG. Так что, если кто-то может улучшить это !

Не стесняйтесь редактировать и улучшать.

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

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
3.07.2013 23:34:11

Насколько я могу судить, у большинства других ответов есть несколько проблем.

Правильный способ сделать это для дисковой (в отличие от сетевой) сборки без GAC - это использовать CodeBaseсвойство текущей выполняемой сборки .

Это возвращает URL ( file://). Вместо того, чтобы возиться с манипуляциями со строками или UnescapeDataString, это можно преобразовать с минимальной суетой, используя LocalPathсвойство Uri.

var codeBaseUrl = Assembly.GetExecutingAssembly().CodeBase;
var filePathToCodeBase = new Uri(codeBaseUrl).LocalPath;
var directoryPath = Path.GetDirectoryName(filePathToCodeBase);
8
23.05.2017 11:47:31
Не работает, если путь содержит #( EscapedCodeBaseработает, но EscapedCodeBase не работает, если путь содержит, например, %20дословно (что является допустимой последовательностью символов в пути Windows)
Martin Ba 4.02.2015 10:11:50
Если мы хотим иметь этот код в пакете NuGet, мы можем исправить этот сценарий, заменив GetExecutingAssembly()на GetCallingAssembly().
Timo 9.09.2019 13:40:47

Веб приложение?

Server.MapPath("~/MyDir/MyFile.ext")
-3
18.06.2014 10:34:01
@ christiandev это ответ, но может показаться, что это неправильный вопрос. Из вопроса довольно ясно, что это не веб-приложение, а сборка, запускаемая с помощью MbUnit. Тем не менее, ответ все еще не совсем корректен из-за теневого копирования в Asp.Net (хотя, возможно, это может быть тем, что ищет кто-то, занимающийся этим вопросом).
George Mauer 18.06.2014 14:33:09

Я нахожу свое решение адекватным для поиска местоположения.

var executingAssembly = new FileInfo((Assembly.GetExecutingAssembly().Location)).Directory.FullName;
0
23.09.2015 11:54:32
Это уже один из самых популярных ответов, и он прямо упоминается в этом вопросе как нечто, что не работает в этой ситуации.
George Mauer 23.09.2015 20:38:08
Извинения должны пропустить это! Очевидно, я не прочитал полностью.
Tez Wingfield 24.09.2015 07:39:49

Я получил такое же поведение NUnitв прошлом. По умолчанию NUnitкопирует вашу сборку во временный каталог. Вы можете изменить это поведение в NUnitнастройках:

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

Возможно TestDriven.NETи MbUnitGUI имеют одинаковые настройки.

0
30.09.2015 09:55:13

За все эти годы никто не упоминал об этом. Уловка, которую я узнал от удивительного проекта 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;
    }
}
6
30.09.2015 15:27:55

Вы можете получить путь к корзине, используя AppDomain.CurrentDomain.RelativeSearchPath.

2
22.04.2016 13:19:47

Как насчет этого ...

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

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

7
29.08.2016 22:37:14

Все предложенные ответы работают, когда разработчик может изменить код для включения необходимого фрагмента, но если вы хотите сделать это без изменения кода, вы можете использовать Process Explorer.

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

Я написал полное описание того, как сделать это для DLL внутри II - http://nodogmablog.bryanhogan.net/2016/09/locating-and-checking-an-executing-dll-on-a-running-web -server /

2
19.09.2016 12:36:26
Обратите внимание, что, во-первых, код в этой статье довольно ориентирован на IIS, а во-вторых, он дает вам (я полагаю) все загруженные в настоящее время библиотеки DLL, а не то, что выполняется одновременно.
George Mauer 18.09.2016 15:04:43
Приведенный пример относится к iis, но те же шаги применяются, если dll работает в процессе вне iis. Это просто вопрос идентификации идентификатора процесса. Я обновлю статью, чтобы отметить это. Спасибо за предложение.
Bryan 19.09.2016 12:38:01

в приложении Windows Form вы можете просто использовать Application.StartupPath

но для DLL и консольных приложений запоминать код гораздо сложнее ...

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

root += slash;
string settingsIni = root + "settings.ini"
2
11.12.2017 19:39:54

Вы получите неправильный каталог, если путь содержит символ «#». Поэтому я использую модификацию ответа Джона Сибли, которая представляет собой комбинацию 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);
     }
}
1
29.10.2018 15:12:51

Я считаю, что это будет работать для любого вида приложений:

AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory
8
22.09.2019 16:48:33
Мои эксперименты показывают, что это самый надежный ответ, охватывающий не только веб-приложения и консольные приложения, но также вызовы из модульных тестов и пакетов NuGet (вложенные в любой уровень рекурсии).
Timo 23.09.2019 11:23:03