Как мне получить путь к сборке, в которой находится код?
Есть ли способ получить путь к сборке, в которой находится текущий код? Я не хочу путь вызывающей сборки, только тот, который содержит код.
В основном мой модульный тест должен прочитать некоторые тестовые файлы XML, которые расположены относительно DLL. Я хочу, чтобы путь всегда разрешался правильно, независимо от того, запускается ли dll для тестирования из TestDriven.NET, графического интерфейса MbUnit или чего-то еще.
Изменить: Люди, кажется, неправильно понимают, что я спрашиваю.
Моя тестовая библиотека находится в скажем
C:\ Проекты \ MyApplication \ daotests \ Bin\Debug\daotests.dll
и я хотел бы получить этот путь:
C:\ Проекты \ MyApplication \ daotests \ Bin \ Debug \
Три предложения пока что подводят меня, когда я запускаю из MbUnit Gui:
Environment.CurrentDirectory
дает c: \ Program Files \ MbUnitSystem.Reflection.Assembly.GetAssembly(typeof(DaoTests)).Location
дает C:\Documents and Settings\george\Local Settings\Temp\ ....\DaoTests.dllSystem.Reflection.Assembly.GetExecutingAssembly().Location
дает так же, как и предыдущий.
31 ответ
Я определил следующее свойство, так как мы часто используем его в модульном тестировании.
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.
Это так просто, как это:
var dir = AppDomain.CurrentDomain.BaseDirectory;
Это помогает?
//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 );
То же самое, что и ответ Джона, но чуть менее подробный метод расширения.
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 Network, было:
System.IO.Path.GetDirectoryName(new System.Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath);
Это также работает с обычными URI тоже.
Это должно работать, если сборка не скопирована тенями:
string path = System.Reflection.Assembly.GetExecutingAssembly().Location
Я считаю, что это сработает для любого приложения:
AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory
Начиная с.net framework 4.6 / .net core 1.0, теперь существует AppContext.BaseDirectory, который должен давать тот же результат, что иAppDomain.CurrentDomain.BaseDirectory
, за исключением того, что домены приложений не были частью стандартного API 1.x.net core 1.x /.net.
AppContext.BaseDirectory
AppDomain.CurrentDomain.BaseDirectory
работает с MbUnit GUI.
Как насчет этого:
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
Я подозреваю, что реальная проблема заключается в том, что ваш тестовый участник копирует вашу сборку в другое место. Во время выполнения нет никакого способа определить, откуда была скопирована сборка, но вы, вероятно, можете щелкнуть переключателем, чтобы сообщить исполнителю теста о том, что нужно запустить сборку, а не копировать ее в теневой каталог.
Конечно, такое переключение может быть разным для каждого участника теста.
Рассматривали ли вы встраивание своих XML-данных в качестве ресурсов в тестовую сборку?
Как насчет этого...
string ThisdllDirectory = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
Тогда просто взломай то, что тебе не нужно
tl;dr
Понятия сборки и DLL-файла не совпадают. В зависимости от того, как сборка была загружена, информация о пути теряется или недоступна вообще . Однако в большинстве случаев предоставленные ответы будут работать.
Есть одно заблуждение в вопросе и в предыдущих ответах. В большинстве случаев предоставленные ответы будут работать нормально, но есть случаи, когда невозможно получить правильный путь к сборке, в которой находится текущий код.
Концепция сборки, которая содержит исполняемый код, и файла dll, который содержит сборку, не взаимосвязаны. Сборка может происходить из файла DLL, но это не обязательно.
С помощью
Assembly.Load(Byte[])
( MSDN) вы можете загрузить сборку прямо из массива байтов в памяти. Не имеет значения, откуда берется массив байтов. Он может быть загружен из файла, загружен из Интернета, динамически сгенерирован, ...
Вот пример, который загружает сборку из байтового массива. Информация о пути теряется после загрузки файла. Получить исходный путь к файлу невозможно, и все описанные выше методы не работают.
Этот метод находится в исполняемой сборке, которая находится по адресу «D:/Software/DynamicAssemblyLoad/DynamicAssemblyLoad/bin/Debug/Runner.exe»
static void Main(string[] args)
{
var fileContent = File.ReadAllBytes(@"C:\Library.dll");
var assembly = Assembly.Load(fileContent);
// Call the method of the library using reflection
assembly
?.GetType("Library.LibraryClass")
?.GetMethod("PrintPath", BindingFlags.Public | BindingFlags.Static)
?.Invoke(null, null);
Console.WriteLine("Hello from Application:");
Console.WriteLine($"GetViaAssemblyCodeBase: {GetViaAssemblyCodeBase(assembly)}");
Console.WriteLine($"GetViaAssemblyLocation: {assembly.Location}");
Console.WriteLine($"GetViaAppDomain : {AppDomain.CurrentDomain.BaseDirectory}");
Console.ReadLine();
}
Этот класс находится в Library.dll:
public class LibraryClass
{
public static void PrintPath()
{
var assembly = Assembly.GetAssembly(typeof(LibraryClass));
Console.WriteLine("Hello from Library:");
Console.WriteLine($"GetViaAssemblyCodeBase: {GetViaAssemblyCodeBase(assembly)}");
Console.WriteLine($"GetViaAssemblyLocation: {assembly.Location}");
Console.WriteLine($"GetViaAppDomain : {AppDomain.CurrentDomain.BaseDirectory}");
}
}
Для полноты картины ниже представлены реализации
GetViaAssemblyCodeBase()
что одинаково для обеих сборок:
private static string GetViaAssemblyCodeBase(Assembly assembly)
{
var codeBase = assembly.CodeBase;
var uri = new UriBuilder(codeBase);
return Uri.UnescapeDataString(uri.Path);
}
Бегун напечатает следующий вывод:
Hello from Library:
GetViaAssemblyCodeBase: D:/Software/DynamicAssemblyLoad/DynamicAssemblyLoad/bin/Debug/Runner.exe
GetViaAssemblyLocation:
GetViaAppDomain : D:\Software\DynamicAssemblyLoad\DynamicAssemblyLoad\bin\Debug\
Hello from Application:
GetViaAssemblyCodeBase: D:/Software/DynamicAssemblyLoad/DynamicAssemblyLoad/bin/Debug/Runner.exe
GetViaAssemblyLocation:
GetViaAppDomain : D:\Software\DynamicAssemblyLoad\DynamicAssemblyLoad\bin\Debug\
Как видите, ни база кода, ни местоположение, ни базовый каталог не являются правильными.
Насколько я могу судить, у большинства других ответов есть несколько проблем.
Правильный способ сделать это для дисковой (в отличие от сетевой) сборки без GAC - использовать текущую исполняемую сборку.CodeBase
имущество.
Это возвращает URL (file://
). Вместо того, чтобы возиться с манипуляцией строк илиUnescapeDataString
, это может быть преобразовано с минимальной суетой, используя LocalPath
собственностью Uri
,
var codeBaseUrl = Assembly.GetExecutingAssembly().CodeBase;
var filePathToCodeBase = new Uri(codeBaseUrl).LocalPath;
var directoryPath = Path.GetDirectoryName(filePathToCodeBase);
Вот порт 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
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var assemblyPath = assembly.GetFiles()[0].Name;
var assemblyDir = System.IO.Path.GetDirectoryName(assemblyPath);
За все эти годы никто не упоминал об этом. Уловка, которую я узнал от удивительного проекта 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;
}
}
Я использовал 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.
В приложении 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"
Текущий каталог, в котором вы находитесь.
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;
Все предложенные ответы работают, когда разработчик может изменить код для включения необходимого фрагмента, но если вы хотите сделать это без изменения какого-либо кода, вы можете использовать Process Explorer.
В нем будут перечислены все выполняющиеся библиотеки в системе. Возможно, вам потребуется определить идентификатор процесса вашего работающего приложения, но обычно это не так уж сложно.
Я написал полное описание того, как сделать это для DLL внутри II - http://nodogmablog.bryanhogan.net/2016/09/locating-and-checking-an-executing-dll-on-a-running-web-server/
Вы можете получить путь к корзине с помощью AppDomain.CurrentDomain.RelativeSearchPath.
Вы получите неправильный каталог, если путь содержит символ "#". Поэтому я использую модификацию ответа Джона Сибли, которая представляет собой комбинацию 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);
}
}
Для 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
}
}
}
string path = Path.GetDirectoryName(typeof(DaoTests).Module.FullyQualifiedName);
Я считаю, что мое решение подходит для поиска местоположения.
var executingAssembly = new FileInfo((Assembly.GetExecutingAssembly().Location)).Directory.FullName;
Это то, что я придумал. Между веб-проектами - юнит-тесты (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;
}
}
Это должно работать:
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).
Я получил такое же поведение в NUnit
в прошлом. По умолчанию NUnit
копирует вашу сборку во временный каталог. Вы можете изменить это поведение в NUnit
настройки:
Может быть TestDriven.NET
а также MbUnit
GUI имеет те же настройки.
Я использую это, чтобы получить путь к 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"