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

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

В основном мой модульный тест должен читать некоторые тестовые файлы 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 и Настройки \ george \ Local Настройки \ Temp \ .... \ DaoTests.dll

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

Это ваше решение: var dir = AppDomain.CurrentDomain.BaseDirectory;

Jalal El-Shaer 22.05.2010 13:15

Это должно быть приемлемое решение. AppDomain.CurrentDomain.BaseDirectory - правильный подход.

aBetterGamer 25.06.2013 19:14

Я пришел сюда в поисках решения для пакета nuget для чтения файла JSON из его каталога pacakge. Похоже, что когда выполняется пакет nuget, «AppDomain.CurrentDomain.BaseDirectory» указывает на каталог запущенных проектов, а не на каталог пакета nuget. Кажется, что ни один из них не нацелен на каталог пакета nuget правильно.

Lucas 29.07.2016 14:47

@ Лукас, нет, это не так, потому что этот вопрос был не об этом (на самом деле, когда его спросили, nuget не существовало) - не стесняйтесь начинать новый вопрос и пинговать меня там, но я могу сказать вам прямо сейчас, что в большинстве случаев это невозможно. Для большинства проектов каталог nuget - packages рядом с файлом sln. НО, когда вы компилируете и распространяете вещи, нет файла sln и каталога пакетов. Во время компиляции все необходимое (но не все) копируется в каталог bin. Лучше всего использовать сценарий postbuild, чтобы скопировать нужный файл.

George Mauer 29.07.2016 22:12

Для тех, кто читает эти комментарии, полагая, что AppDomain.CurrentDomain.BaseDirectory - правильное решение, обратитесь к этот комментарий, который предлагает лучшее решение.

Neo 17.07.2019 15:16
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
831
6
650 127
30
Перейти к ответу Данный вопрос помечен как решенный

Ответы 30

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

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

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

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

Если вы скопируете файл .xml с помощью build, вы должны его найти.

или же

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

// The location of the Assembly
assembly.Location;

это будет проблематично, если сборка была тень скопирована.

spender 17.06.2014 14:17

+1520! Environment.CurrentDirectory работает, если вы используете отражение в классе задач MSBuild, где исполняемая сборка находится в GAC, а ваш код находится в другом месте.

vulcan raven 10.08.2014 21:37

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

Bent Tranberg 06.11.2016 22:55

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

//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?

George Mauer 10.09.2008 01:32

Почему? fullPath будет "C: \ projects \ myapplication \ daotests \ bin \ Debug \ daotests.dll", каталог будет "C: \ projects \ myapplication \ daotests \ bin \ Debug" Разве вы не этого хотите?

Keith 10.09.2008 01:35

Ах, нет, я тебя понял. MSTest делает то же самое - каждый раз все копируется в новый временный каталог.

Keith 10.09.2008 01:38

Задайте файлы xml как содержимое, скопированные с dll или ресурсы, прочитанные из dll.

Keith 10.09.2008 01:39

Или просто typeof(DaoTests).Assembly

SLaks 17.03.2012 01:34

Я бы лично выбрал такой метод: public static string GetAssemblyDirectory<T>(){return System.IO.Path.GetDirectoryName(typeof(T).Assembly.Location)‌​;}

Johny Skovdal 03.04.2012 13:17

@JohnySkovdal - зачем использовать общий, если тип возврата всегда string, а вход всегда Type? Вы можете сделать: public static string GetAssemblyDirectory(this Type input){return System.IO.Path.GetDirectoryName(input.Assembly.Location);}, так что вам не нужно явно знать тип: unknown.GetType().GetAssemblyDirectory();

Keith 04.04.2012 16:10

@Keith - я просто заменяю входной параметр параметром типа, поэтому мне не придется вызывать typeof, как вы это делаете в своем примере. Что касается того, что тип явно не нужен, у меня еще не было необходимости, поэтому у меня нет перегрузки, которая принимает System.Type в качестве входных данных (пока).

Johny Skovdal 19.04.2012 18:44

@SLaks @JohnySkovdal @Keith: Привет, ребята, используйте Assembly.GetExecutingAssembly(). Это "получает сборку, содержащую код, который выполняется в данный момент" (из описания метода). Я использую это в моей надстройке "EntitiesToDTOs". См. Реальный пример AssemblyHelper.cs.

kzfabi 30.06.2012 09:24

Возникла проблема с сообщением @John Silby, так как он не выглядит так, как будто он работает для путей UNC ... например. \\ Сервер \ Папка \ File.ext. Этот сработал. +1

Blueberry 09.08.2012 21:24

@FabianFernandez, посмотрите на вопрос еще раз, это не то, что здесь нужно, и это уже упоминалось как неработающее решение.

Johny Skovdal 10.08.2012 01:19

Да, я рассмотрел вопрос, и вы правы, похоже, я не заметил этого, когда увидел вопрос в первый раз. Мое плохое, спасибо!

kzfabi 11.08.2012 03:32

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

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

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

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

+1 за указание на проблему с теневым копированием. Однако по Assembly.CodeBase действительно можно определить исходное место.

tm1 13.06.2017 10:21

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

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

string path = Path.GetDirectoryName(typeof(DaoTests).Module.FullyQualifiedName);

Я использовал 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.

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

Seven 29.03.2014 00:39
Ответ принят как подходящий

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

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.

Я столкнулся с одной проблемой: если имя вашего каталога: 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 13:31

черт возьми, это все еще не работает для меня: 0 (Теперь вместо того, чтобы дать мне путь к MSBuild, я получаю путь к TeamCity C: \ TeamCity \ buildAgent \ temp \ buildTmp \ SYSTEM_SVR1 2010-08-24 17_34_23 \ Out, но еще один способ получить путь :-)

schmoopy 25.08.2010 02:12

@John Sible: возвращает путь к исполняемой сборке, а не к сборке (как в .dll), которая загружается вызывающим исполняемым файлом. Мне нужен .dll, чтобы установить рабочий каталог. +1 хотя за полезный ответ и хорошую отправную точку.

IAbstract 01.09.2010 16:08

Будьте осторожны при использовании этого параметра для проверки File.Exist (), поскольку этот метод вернет false на пути UNC. Вместо этого используйте ответ @Keith.

AZ. 08.12.2011 06:22

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

Valamas 03.07.2013 08:27

Для людей, которым необходимо знать, сборка находится в System.Reflection, а путь - в System.IO. :)

Fallenreaper 25.10.2013 18:55

Примечание: это не работает с сетевыми местоположениями (например, \\ REMOT_EPC \ Folder)

Muxa 28.01.2014 02:37

Обратите внимание, что вторая строка вызовет CA2234. Передайте новый Uri (codeBase) конструктору UriBuilder, чтобы исправить предупреждение.

Jedidja 01.07.2014 18:40

@Jedidja Какую версию Visual Studio вы используете, вызывая ошибку? Просто попробовал в VS2012, и вроде нормально

John Sibly 01.07.2014 20:31

@JohnSible 2012, хотя через msbuild в командной строке. Если вы посмотрите на включенные правила, проверено ли 2234? Я не уверен, что это один из минимально рекомендованных значений по умолчанию.

Jedidja 02.07.2014 16:57

Также это не сработает, если в справочнике есть знаки "#". Числовые знаки разрешены в именах каталогов и файлов в Windows.

Huemac 17.12.2014 14:32

CodeBase - это беспорядок. Вот мой взгляд на это: stackoverflow.com/a/28319367/321013

Martin Ba 04.02.2015 13:50

Я полагался на этот метод в течение многих лет, но сегодня он потерпел неудачу, когда путь к каталогу, о котором идет речь, содержал '#'.

Simon 21.11.2017 21:05

Это работает так же и кажется короче: string path = new Uri (Assembly.GetExecutingAssembly (). CodeBase) .LocalPath; return Path.GetDirectoryName (путь);

aeroson 24.01.2018 11:28

Это не поддается проверке ... Все ваши модульные тесты, которые проверяют этот код, завершатся ошибкой! см. ответ @Jalal

Yitzchak 28.06.2018 17:48

Мы долгое время использовали CodeBase, а теперь перешли на .Net45. Поведение CodeBase изменилось, поэтому он больше не возвращает каталог. Не используйте .CodeBase, если вы используете более новые версии DotNet! Теперь мы используем AppDomain.CurrentDomain.BaseDirectory, который правильно работает для любой версии DotNet.

Dutchman 11.03.2019 23:24

Я считаю, что решение из моего ответа ниже stackoverflow.com/a/58051383/1671558 должно охватывать все варианты использования и довольно простое. Другой пользователь сообщил, что он работает практически везде, включая консоль, Интернет, модульные тесты.

Ilya Chernomordik 19.02.2020 21:41

Вот порт 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

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

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

Вы имели ввиду assembly вместо Assembly.GetExecutingAssembly()?

Dude Pascalou 30.01.2014 20:42

Как отмечает Чувак, вы подали аргумент и не смогли его использовать.

Chris Moschini 23.10.2014 19:42

Этот ответ просто неверен для рассматриваемого вопроса. Измененная версия этого ответа может дать вам путь к данной сборке. Однако здесь мы специально ищем выполняющуюся сборку, поэтому передача сборки не имеет смысла. Метод расширения - неподходящий инструмент для работы.

Edward Brey 27.05.2015 15:48

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

var dir = AppDomain.CurrentDomain.BaseDirectory;

Это должно быть приемлемое решение. AppDomain.CurrentDomain.BaseDirectory - правильный подход.

aBetterGamer 25.06.2013 19:15

спасибо, что вернули мое внимание к этому - не уверен, было ли это доступно в то время, когда я задавал вопрос, но это сейчас.

George Mauer 25.06.2013 22:52

Нет, это не так. Это возвращает путь ОРИГИНАЛЬНОЙ ТОЧКИ ВХОДА, а не текущий исполняемый код. Если вы загрузили сборку вручную с другого пути или если она была загружена из GAC, она вернет неверный результат. Ответ правильный: stackoverflow.com/a/283917/243557 Быстрее по-прежнему Path.GetDirectoryName(Assembly.GetExecutingAssembly().Locati‌​on).

nathanchere 02.08.2013 13:29

На самом деле это не будет работать в веб-приложениях, но, насколько я понял, следующее дополнение должно работать для любого типа приложений: AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory

Ilya Chernomordik 14.08.2013 12:28

Илье Черномордику. Для веб-приложений я использую: System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPat‌ h

Manuel Alves 17.02.2016 13:21

Это отлично подходит для модульного тестирования, если вы просто хотите получить исходный путь к контейнеру вашей тестовой сборки (скажем, для доступа к файлам вспомогательных данных в подпапках). Тестовая сборка - это точка входа в ваш код.

MarioDS 15.04.2016 16:34

@IlyaChernomordik Я считаю, что ваше решение охватывает большинство сценариев - любой сценарий, с которым я столкнулся, в отличие от любого другого ответа. Он заслуживает большей наглядности. Вы определенно можете опубликовать это как отдельный ответ.

Timo 22.09.2019 16:21

Отправили ответ, который, кажется, работает для любого приложения: stackoverflow.com/a/58051383/1671558l, как предложил @Timo

Ilya Chernomordik 22.09.2019 19:50

AppDomain.CurrentDomain.BaseDirectory

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

Это отлично подходит для записи файла относительно корневого каталога в веб-приложении asp.net.

Philip Pittle 01.10.2014 18:47

Я обнаружил, что этот в целом работает лучше всего. Выберите его, если не уверены.

Erik Bergstedt 19.02.2016 12:07

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

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"

Я не вижу причин избегать здесь Path.getDirectoryName

Max Keller 30.05.2012 13:24

@MaxKeller Если вы не видите причин, это не значит, что это правильно. Этот альтернативный метод Path.GetDirectoryName в десять раз быстрее.

Ruslan Veselov 05.11.2015 15:34

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

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

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

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

Daniel Gilbert 18.09.2013 17:52

Это вылетает, когда в папке есть пробелы и черт знает какие еще символы ...

MarioDS 02.11.2017 15:56

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

Timo 09.09.2019 16:39

@Timo: вы проверили, есть ли у этого изменения побочные эффекты? Если да, отредактируйте ответ, включив исправление.

Ignacio Soler Garcia 20.09.2019 21:38

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

Timo 22.09.2019 16:19

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

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?

George Mauer 12.06.2013 18:22

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

Я искал код, чтобы определить, в какой конфигурации находится сборка, 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;
    }
}

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

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

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

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

Не работает, если путь содержит # (EscapedCodeBase работает, но EscapedCodeBase не работает, если путь содержит, например, %20 дословно (что является допустимой последовательностью символов в пути Windows)

Martin Ba 04.02.2015 13:11

Если мы хотим иметь этот код в пакете NuGet, мы можем исправить этот сценарий, заменив GetExecutingAssembly() на GetCallingAssembly().

Timo 09.09.2019 16:40

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

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

@christiandev, это ответ, но может показаться, что это ответ на неправильный вопрос. Из вопроса довольно ясно, что это не веб-приложение, а сборка, запускаемая с помощью MbUnit. При этом ответ все еще не совсем правильный из-за теневого копирования Asp.Net (хотя, возможно, это может быть то, что ищет кто-то, ответивший на этот вопрос).

George Mauer 18.06.2014 18:33

Я считаю, что мое решение подходит для определения местоположения.

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

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

George Mauer 23.09.2015 23:38

Приношу извинения за то, что пропустил это! Очевидно, я не дочитал до конца.

Tez Wingfield 24.09.2015 10:39

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

Возможно, у TestDriven.NET и MbUnit GUI одинаковые настройки.

За все эти годы об этом никто не упомянул. Трюк, который я узнал из классного Проект 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;
    }
}

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

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

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

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

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

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

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

Обратите внимание, что, во-первых, код в статье в значительной степени ориентирован на IIS, а во-вторых, он дает вам (я считаю) все библиотеки dll в настоящее время загружен, а не то, что выполняется одновременно.

George Mauer 18.09.2016 18:04

Приведенный пример относится к iis, но те же шаги применимы, если dll выполняется в процессе вне iis. Это просто вопрос идентификации идентификатора процесса. Я обновлю статью, чтобы отметить это. Спасибо за предложение.

Bryan 19.09.2016 15:38

в приложении 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"

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

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

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

Мои эксперименты показывают, что это наиболее надежный ответ, охватывающий не только веб-приложения и консольные приложения, но также вызовы из модульных тестов и пакетов NuGet (вложенных в любой уровень рекурсии).

Timo 23.09.2019 14:23

Спасибо за это элегантное решение!

ecif 24.11.2020 13:22

Начиная с .net framework 4.6 / .net core 1.0, теперь существует AppContext.BaseDirectory, который должен давать тот же результат, что и AppDomain.CurrentDomain.BaseDirectory, за исключением того, что домены приложений не были частью стандартного API 1.x .net core 1.x /.net.

AppContext.BaseDirectory

Для ASP.Net это не работает. Я нашел более подходящее решение на Почему AppDomain.CurrentDomain.BaseDirectory не содержит «bin» в приложении asp.net?. Он работает как для приложения Win, так и для веб-приложения ASP.Net.

public string ApplicationPath
    {
        get
        {
            if (String.IsNullOrEmpty(AppDomain.CurrentDomain.RelativeSearchPath))
            {
                return AppDomain.CurrentDomain.BaseDirectory; //exe folder for WinForms, Consoles, Windows Services
            }
            else
            {
                return AppDomain.CurrentDomain.RelativeSearchPath; //bin folder for Web Apps 
            }
        }
    }

Другие вопросы по теме