Как я могу преобразовать сборку.Кода в путь файловой системы в C#?


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

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

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

Environment.CommandLine также имеет ограниченное использование, потому что в NUnit et al он возвращается путь к Нанит, не мой проект.

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

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

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

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

5 52

5 ответов:

вы должны использовать систему.Ури.LocalPath:

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

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

string localPath = new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath;

сборка.Кодовая база выглядит многообещающе, но это UNC-путь:

обратите внимание, что это что-то приближается a файл uri,не an UNC path.


вы решаете это, делая манипуляции со строками вручную. серьезно.

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

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

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

доступная база кода (мы не хотим 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):
    • обратите внимание, как пробел () translates to %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 en - / декодирование CodeBase свойство, если это локальный путь, но учитывая, что можно просто снять file:/// и покончим с этим, я бы сказал, что это решение стоит достаточно хорошо, если конечно очень некрасиво.

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

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.конфигурационный файл.

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

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

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

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