Как мне получить путь к сборке, в которой находится код?
-
09-06-2019 - |
Вопрос
Есть ли способ получить путь к сборке, в которой находится текущий код?Мне не нужен путь к вызывающей сборке, только тот, который содержит код.
По сути, моему модульному тестированию необходимо прочитать некоторые xml-тестовые файлы, которые расположены относительно библиотеки dll.Я хочу, чтобы путь всегда разрешался правильно, независимо от того, запущена ли тестируемая dll из TestDriven.NET, графический интерфейс MbUnit или что-то еще.
Редактировать:Люди, кажется, неправильно понимают, о чем я спрашиваю.
Моя тестовая библиотека находится, скажем, в
C:\projects\myapplication\daotests\bin\Debug\daotests.dll
и я хотел бы пройти этот путь:
C:\projects\myapplication\daotests\bin\Debug\
Три предложения до сих пор подводили меня, когда я запускался из графического интерфейса MbUnit:
Environment.CurrentDirectory
дает c:\Program Файлы\MbUnitSystem.Reflection.Assembly.GetAssembly(typeof(DaoTests)).Location
дает C:\Documents и Настройки \george\Local Настройки emp\ ....\DaoTests.dllSystem.Reflection.Assembly.GetExecutingAssembly().Location
выдает то же, что и предыдущий.
Решение
Я определил следующее свойство, поскольку мы часто используем его в модульном тестировании.
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.
Другие советы
Помогает ли это?
//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 );
Все очень просто, вот так:
var dir = AppDomain.CurrentDomain.BaseDirectory;
То же, что и в ответе Джона, но немного менее подробный метод расширения.
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();
Единственным решением, которое сработало для меня при использовании общих сетевых ресурсов CodeBase и UNC, было:
System.IO.Path.GetDirectoryName(new System.Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath);
Это также работает и с обычными URI.
Это должно сработать, если только сборка не скопированная тень:
string path = System.Reflection.Assembly.GetExecutingAssembly().Location
А как насчет этого:
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
Я подозреваю, что реальная проблема здесь заключается в том, что ваш тестировщик копирует вашу сборку в другое место.Во время выполнения невозможно определить, откуда была скопирована сборка, но вы, вероятно, можете щелкнуть переключателем, чтобы указать тестировщику запускать сборку оттуда, где она есть, а не копировать ее в теневой каталог.
Конечно, такой переключатель, скорее всего, будет разным для каждого участника тестирования.
Рассматривали ли вы возможность встраивания ваших XML-данных в качестве ресурсов внутри вашей тестовой сборки?
AppDomain.CurrentDomain.BaseDirectory
работает с графическим интерфейсом MbUnit.
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var assemblyPath = assembly.GetFiles()[0].Name;
var assemblyDir = System.IO.Path.GetDirectoryName(assemblyPath);
Вот 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
Как насчет этого ?..
string ThisdllDirectory = System.IO.Path.GetDirectoryName(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;
Я использовал сборку.Кодовая база вместо местоположения:
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 говорит:
"Кодовая база - это URL-адрес места, где был найден файл, в то время как Местоположение - это путь, по которому он был фактически загружен.Например, если сборка была загружена из Интернета, ее кодовая база может начинаться с "http://", но ее Местоположение может начинаться с "C:\".Если файл был скопирован теневым способом, Местоположением будет путь к копии файла в каталоге теневой копии.Также полезно знать, что кодовая база не гарантируется для сборок в GAC.Однако для сборок, загруженных с диска, всегда будет задано местоположение."
Ты мочь хотите использовать кодовую базу вместо Местоположения.
Насколько я могу судить, большинство других ответов имеют несколько проблем.
Правильный способ сделать это для сборка на диске (в отличие от веб-сборки), не привязанная к сети заключается в использовании текущей выполняемой сборки CodeBase
собственность.
Это возвращает URL-адрес (file://
).Вместо того, чтобы возиться с манипулирование строками или UnescapeDataString
, это может быть преобразовано с минимальными затратами, используя LocalPath
свойство Uri
.
var codeBaseUrl = Assembly.GetExecutingAssembly().CodeBase;
var filePathToCodeBase = new Uri(codeBaseUrl).LocalPath;
var directoryPath = Path.GetDirectoryName(filePathToCodeBase);
За все эти годы никто на самом деле не упоминал об этом.Трюк, которому я научился у потрясающего Проект одобрительных испытаний.Хитрость заключается в том, что вы используете отладочную информацию в сборке, чтобы найти исходный каталог.
Это не будет работать ни в режиме ВЫПУСКА, ни при включенной оптимизации, ни на компьютере, отличном от того, на котором оно было скомпилировано.
Но это приведет вас к путям, которые относительно расположения файла исходного кода, из которого вы его вызываете
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
Все предлагаемые ответы работают, когда разработчик может изменить код, включив в него требуемый фрагмент, но если вы хотите сделать это без изменения какого-либо кода, вы могли бы использовать Process Explorer.
В нем будут перечислены все выполняющиеся библиотеки DLL в системе, возможно, вам потребуется определить идентификатор процесса вашего запущенного приложения, но обычно это не слишком сложно.
Я написал полное описание того, как это сделать для библиотеки dll внутри II - http://nodogmablog.bryanhogan.net/2016/09/locating-and-checking-an-executing-dll-on-a-running-web-server/
в приложении 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"
string path = Path.GetDirectoryName(typeof(DaoTests).Module.FullyQualifiedName);
Вы получите неверный каталог, если путь содержит символ '#'.Поэтому я использую модификацию ответа Джона Сибли, которая представляет собой комбинацию UriBuilder.Path и UriBuilder.Фрагмент:
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);
}
}
Это то, что я придумал. В перерывах между веб-проектами - модульные тесты (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;
}
}
Это должно сработать:
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).
Я нахожу свое решение адекватным для поиска местоположения.
var executingAssembly = new FileInfo((Assembly.GetExecutingAssembly().Location)).Directory.FullName;
Я использую это, чтобы получить путь к каталогу Bin:
var i = Environment.CurrentDirectory.LastIndexOf(@"\");
var path = Environment.CurrentDirectory.Substring(0,i);
Вы получаете такой результат:
"c:\users icooley\documents\visual studio 2010\Проекты\Windows_Test_Project\Windows_Test_Project\bin"
Веб-приложение?
Server.MapPath("~/MyDir/MyFile.ext")