Как я могу преобразовать Assembly.CodeBase в путь файловой системы в C #? - PullRequest
58 голосов
/ 05 ноября 2010

У меня есть проект, который хранит шаблоны в папке \Templates рядом с DLL и EXE.

Я хочу определить этот путь к файлу во время выполнения, но используя технику, которая будет работать как внутри модульного теста, так и в производственной среде (и я не хочу отключать теневое копирование в NUnit!)

Assembly.Location не годится, потому что при запуске под NUnit возвращает путь скопированной тени сборки.

Environment.CommandLine также имеет ограниченное использование, поскольку в NUnit и др. Он возвращает путь к NUnit, а не к моему проекту.

Assembly.CodeBase выглядит многообещающе, но это UNC-путь:

file:///D:/projects/MyApp/MyApp/bin/debug/MyApp.exe

Теперь я мог бы превратить это в путь к локальной файловой системе, используя манипуляции со строками, но я подозреваю, что есть более чистый способ сделать это, похороненный где-то в .NET Framework. Кто-нибудь знает рекомендуемый способ сделать это?

(Выдает исключение, если путь UNC не является file:/// URL-адресом абсолютно подходит в этом контексте)

Ответы [ 5 ]

104 голосов
/ 05 ноября 2010

Вам необходимо использовать System.Uri.LocalPath:

string localPath = new Uri("file:///D:/projects/MyApp/MyApp/bin/debug/MyApp.exe").LocalPath;

Поэтому, если вы хотите указать исходное местоположение текущей выполняющейся сборки:

string localPath = new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath;
23 голосов
/ 04 февраля 2015

Assembly.CodeBase выглядит многообещающе, но это UNC-путь:

Обратите внимание, что это что-то, приближенное к файлу uri , , а не и UNC-путь .


Вы решаете эту проблему вручную. Серьезно.

Попробуйте все другие методы, которые вы можете найти в SO, со следующим каталогом (дословно):

C:\Test\Space( )(h#)(p%20){[a&],t@,p%,+}.,\Release

Это допустимый, хотя и несколько необычный путь Windows. (У некоторых людей будет любой из этих символов в этих путях, и вы хотите, чтобы ваш метод работал для всех из них, верно?)

Доступная база кода ( нам не нужны Location, верно? ) свойства тогда (на моем Win7 с .NET 4):

assembly.CodeBase -> file:///C:/Test/Space( )(h#)(p%20){[a&],t@,p%,+}.,/Release

assembly.EscapedCodeBase -> file:///C:/Test/Space(%20)(h%23)(p%20)%7B%5Ba%26%5D,t@,p%,+%7D.,/Release

Вы заметите:

  • CodeBase вообще не экранирован, это просто обычный локальный путь с префиксом file:/// и заменены обратные слеши. Таким образом, не не работает, чтобы передать это System.Uri.
  • EscapedCodeBase не полностью экранирован (я не знаю, является ли это ошибкой или это недостаток схемы URI ):
    • Обратите внимание, как символ пробела () переводится в %20
    • но последовательность %20 также переводится в %20! (проценты % вообще не экранированы)
    • Никто не может восстановить оригинал из этой искаженной формы!

Для локальных файлов (И это действительно все, что мне нужно для CodeBase, потому что, если файл не локальный, вы, вероятно, все равно захотите использовать .Location, мне подходит следующее (обратите внимание, что это не самая красивая либо:

    public static string GetAssemblyFullPath(Assembly assembly)
    {
        string codeBasePseudoUrl = assembly.CodeBase; // "pseudo" because it is not properly escaped
        if (codeBasePseudoUrl != null) {
            const string filePrefix3 = @"file:///";
            if (codeBasePseudoUrl.StartsWith(filePrefix3)) {
                string sPath = codeBasePseudoUrl.Substring(filePrefix3.Length);
                string bsPath = sPath.Replace('/', '\\');
                Console.WriteLine("bsPath: " + bsPath);
                string fp = Path.GetFullPath(bsPath);
                Console.WriteLine("fp: " + fp);
                return fp;
            }
        }
        System.Diagnostics.Debug.Assert(false, "CodeBase evaluation failed! - Using Location as fallback.");
        return Path.GetFullPath(assembly.Location);
    }

Я уверен, что можно придумать лучшие решения, возможно, можно даже придумать решение, которое делает правильное URL-декодирование / свойство свойства CodeBase, если это локальный путь, но, учитывая, что можно просто удалить от file:/// и покончим с этим, я бы сказал, что это решение достаточно хорошо, если конечно действительно ужасно.

5 голосов
/ 12 июня 2013

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

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

3 голосов
/ 18 декабря 2016

Еще одно решение, включая сложные пути:

    public static string GetPath(this Assembly assembly)
    {
        return Path.GetDirectoryName(assembly.GetFileName());
    }

    public static string GetFileName(this Assembly assembly)
    {
        return assembly.CodeBase.GetPathFromUri();
    }

    public static string GetPathFromUri(this string uriString)
    {
        var uri = new Uri(Uri.EscapeUriString(uriString));
        return String.Format("{0}{1}", Uri.UnescapeDataString(uri.PathAndQuery), Uri.UnescapeDataString(uri.Fragment));
    }

и тесты:

    [Test]
    public void GetPathFromUriTest()
    {
        Assert.AreEqual(@"C:/Test/Space( )(h#)(p%20){[a&],t@,p%,+}.,/Release", @"file:///C:/Test/Space( )(h#)(p%20){[a&],t@,p%,+}.,/Release".GetPathFromUri());
        Assert.AreEqual(@"C:/Test/Space( )(h#)(p%20){[a&],t@,p%,+}.,/Release",  @"file://C:/Test/Space( )(h#)(p%20){[a&],t@,p%,+}.,/Release".GetPathFromUri());
    }

    [Test]
    public void AssemblyPathTest()
    {
        var asm = Assembly.GetExecutingAssembly();

        var path = asm.GetPath();
        var file = asm.GetFileName();

        Assert.IsNotEmpty(path);
        Assert.IsNotEmpty(file);

        Assert.That(File     .Exists(file));
        Assert.That(Directory.Exists(path));
    }
2 голосов
/ 12 марта 2016

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

using System.Reflection;
using NUnit.Framework.Internal;
...
string path = AssemblyHelper.GetDirectoryName(Assembly.GetExecutingAssembly())
...