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

584

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

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

Изменить. Люди, похоже, неправильно понимают, что я прошу.

Моя тестовая библиотека находится в разделе

C:\Проекты\MyApplication\daotests\Bin\Debug\daotests.dll

и я хотел бы получить этот путь:

C:\Проекты\MyApplication\daotests\Bin\Debug\

Три предложения до сих пор не сработают, когда я бегу от MbUnit Gui:

  • Environment.CurrentDirectory дает c:\Program Files\MbUnit

  • System.Reflection.Assembly.GetAssembly(typeof(DaoTests)).Location дает C:\Documents and Настройки\джордж\Local Настройки \Temp \....\DaoTests.dll

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

  • 89
    Это ваше решение: var dir = AppDomain.CurrentDomain.BaseDirectory;
  • 7
    Это должно быть принятым решением. AppDomain.CurrentDomain.BaseDirectory - правильный подход.
Показать ещё 3 комментария
Теги:
reflection

25 ответов

807
Лучший ответ

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

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.

  • 23
    Это одна проблема, с которой я столкнулся, если имя вашего каталога: c: \ My% 20Directory, тогда Uri.UnescapeDataString вернет: c: \ My Directory Это означает, что File.Exists ("c: \ My Directory \ MyFile.txt ") вернет false, поскольку правильный путь на самом деле" c: \ My% 20Directory \ MyFile.txt ". Я сталкивался с этим, поскольку в наших путях SVN есть пробелы, и когда мы их проверяем, он кодирует пробелы.
  • 2
    блин, это все еще не работает для меня: 0 (Теперь вместо того, чтобы дать мне путь MSBuild, я получаю путь TeamCity C: \ TeamCity \ buildAgent \ temp \ buildTmp \ SYSTEM_SVR1 2010-08-24 17_34_23 \ Out, но еще один способ получить путь :-)
Показать ещё 15 комментариев
273

Помогает ли это?

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

//get the folder that in
string theDirectory = Path.GetDirectoryName( fullPath );
  • 0
    посмотрите мои правки, это не так, это что-то странное в том, как работает MbUnit?
  • 0
    Как так? fullPath будет "C: \ projects \ myapplication \ daotests \ bin \ Debug \ daotests.dll", каталог будет "C: \ projects \ myapplication \ daotests \ bin \ Debug" Разве это не то, что вы хотите?
Показать ещё 10 комментариев
243

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

var dir = AppDomain.CurrentDomain.BaseDirectory;
  • 9
    Это должно быть принятым решением. AppDomain.CurrentDomain.BaseDirectory - правильный подход.
  • 5
    спасибо за то, что обратили мое внимание на это - не уверен, было ли это доступно в то время, когда я задавал вопрос, но сейчас.
Показать ещё 5 комментариев
63

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

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
    Как указывает Чувак, вы передали аргумент и не смогли его использовать.
Показать ещё 1 комментарий
39

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

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

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

  • 4
    Это должен быть принятый ответ. Это действительно раздражает, что кодовая база по умолчанию не обрабатывает общие ресурсы UNC.
  • 1
    Это правильный ответ, все вышеперечисленное у меня не сработало
Показать ещё 1 комментарий
29

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

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

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

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

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

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

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

  • 0
    +1 за указание на проблему с теневым копированием. Тем не менее, действительно возможно определить исходное место из Assembly.CodeBase .
9
AppDomain.CurrentDomain.BaseDirectory

работает с GUI MbUnit.

  • 0
    Это прекрасно работает для записи файла относительно корневого каталога в веб-приложении asp.net
  • 0
    Я обнаружил, что этот работает лучше всего в целом. Выберите это, если вы не уверены.
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
  • 0
    в VB просто используйте свойство "My.Application.Info.DirectoryPath" ....
6

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

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

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

За все эти годы никто не упомянул об этом. Трюк, который я узнал из удивительного проекта ApprovedTests. Фокус в том, что вы используете информацию об отладке в сборке, чтобы найти исходный каталог.

Это не будет работать в режиме 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

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

Правильный способ сделать это для дисковой (в отличие от веб-) сборки, отличной от 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)
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;
  • 0
    это будет проблематично, если сборка была скопирована тенями .
  • 0
    +1520! Environment.CurrentDirectory работает, если вы используете отражение в классе задач MSBuild, где исполняемая сборка находится в GAC, а ваш код находится где-то еще.
Показать ещё 1 комментарий
3
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var assemblyPath = assembly.GetFiles()[0].Name;
var assemblyDir = System.IO.Path.GetDirectoryName(assemblyPath);
1

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

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

Затем просто взломайте то, что вам не нужно

1

Вы можете получить путь к bin   AppDomain.CurrentDomain.RelativeSearchPath

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

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

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

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

  • 0
    Обратите внимание, что, во-первых, код в этой статье довольно ориентирован на IIS, а во-вторых, он дает вам (я полагаю) все загруженные в настоящее время библиотеки DLL, а не то, что выполняется одновременно.
  • 0
    Данный пример относится к iis, но те же шаги применимы, если dll работает в процессе вне iis. Это просто вопрос идентификации идентификатора процесса. Я обновлю статью, чтобы отметить это. Спасибо за предложение.
0

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

Изображение 1025

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

0

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

var executingAssembly = new FileInfo((Assembly.GetExecutingAssembly().Location)).Directory.FullName;
  • 0
    Это уже один из самых популярных ответов, и он прямо упоминается в этом вопросе как нечто, что не работает в этой ситуации.
  • 0
    Извинения должны пропустить это! Очевидно, я не прочитал полностью.
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 файла).

  • 0
    Для чего fileMap используется fileMap ?
-1

Я использую это, чтобы получить путь к 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"

  • 6
    Я не вижу причин, чтобы избежать Path.getDirectoryName здесь
  • 0
    @MaxKeller Если вы не видите причин, это не значит, что это правильно. Этот альтернативный метод Path.GetDirectoryName работает в десять раз быстрее.
-2

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

Server.MapPath("~/MyDir/MyFile.ext")
  • 2
    @ christiandev это ответ, но может показаться, что это неправильный вопрос. Из вопроса довольно ясно, что это не веб-приложение, а сборка, запускаемая с помощью MbUnit. Тем не менее, ответ все еще не совсем корректен из-за теневого копирования в Asp.Net (хотя, возможно, это может быть тем, что ищет кто-то, занимающийся этим вопросом).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню