如何将Assembly.CodeBase转换为C#中的文件系统路径?

时间:2010-11-05 15:56:29

标签: c# .net nunit codebase

我有一个项目,可以将模板存储在DLL和EXE旁边的\Templates文件夹中。

我想在运行时确定这个文件路径,但是使用的技术可以在单元测试和生产中使用(我不想在NUnit中禁用阴影复制!)

Assembly.Location不好,因为它在NUnit下运行时会返回阴影复制程序集的路径。

Environment.CommandLine的用途也很有限,因为在NUnit等人中它会返回NUnit的路径,而不是我的项目。

Assembly.CodeBase看起来很有希望,但它是一条UNC路径:

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

现在我可以使用字符串操作将其转换为本地文件系统路径,但我怀疑有一种更简洁的方法可以将它隐藏在.NET框架中。有人知道这样做的推荐方法吗?

(如果UNC路径不是file:/// URL,则抛出异常在此上下文中绝对正常)

5 个答案:

答案 0 :(得分:104)

您需要使用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;

答案 1 :(得分:22)

  

Assembly.CodeBase看起来很有希望,但它是一个UNC路径:

请注意,这是近似于file uri而不是 UNC path的内容。


您可以通过手动操作字符串来解决此问题。 严重。

使用以下目录(逐字)尝试在SO上找到的所有其他方法:

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

这是一个有效的,如果有点不寻常的Windows路径。 (有些人在路径中有这些字符中的任何一个,你会希望你的方法适用于所有那些,对吗?)

然后是可用的代码库(we do not want Location, right?)属性(在我的带有.NET 4的Win7上):

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 scheme的缺点):
    • 请注意空格字符()如何转换为%20
    • %20序列转换为%20! (%%根本没有转义)
    • 没有人可以从这个错位的表单重建原文!

对于本地文件(而且我真的只关心CodeBase内容,因为如果文件属于本地文件,您可能希望使用{{1无论如何,以下适用于我(请注意,它不是最漂亮的:

.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并完成它,我说这个解决方案足够好,如果肯定真的很难看。

答案 2 :(得分:5)

这应该有效:

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

我使用它可以使用独立的log4net.config文件从dll库中进行登录。

答案 3 :(得分:3)

另一个解决方案,包括复杂路径:

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

答案 4 :(得分:2)

由于您标记了此问题 NUnit ,您还可以使用AssemblyHelper.GetDirectoryName获取正在执行的程序集的原始目录:

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