67

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

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

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

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

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

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

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

(在这种情况下,如果 UNC 路径不是 URL,则抛出异常file:///是绝对可以的)

4

5 回答 5

119

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

LocalPath 包括程序集的文件名,例如,

D:\projects\MyApp\MyApp\bin\debug\MyApp.exe

如果您想要程序集的目录,请使用 System.IO.Path.GetDirectoryName():

string localDirectory = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath);

这会给你:

D:\projects\MyApp\MyApp\bin\debug
于 2010-11-05T16:12:12.047 回答
29

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

请注意,它近似于文件uri而不是UNC 路径


您可以通过手动进行字符串操作来解决这个问题。严重地。

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

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

这是一个有效的(如果有些不寻常的话)Windows 路径。(有些人在其中的路径中有这些角色之一,您希望您的方法适用于所有这些角色,对吗?)

可用的代码库(我们不想要Location,对吗?)属性是(在我的带有 .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 方案的一个缺点):
    • 请注意空格字符 ( ) 如何转换为%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);
    }

我确信可以提出更好的解决方案,甚至可以提出一种解决方案,CodeBase如果它是本地路径,则可以对属性进行正确的 URL 编码/解码,但鉴于可以剥离file:///并完成它,我会说这个解决方案足够好,如果肯定真的很难看的话。

于 2015-02-04T10:45:12.723 回答
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 库中登录。

于 2013-06-12T08:08:54.760 回答
4

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

    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));
    }
于 2016-12-17T21:30:59.053 回答
2

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

using System.Reflection;
using NUnit.Framework.Internal;
...
string path = AssemblyHelper.GetDirectoryName(Assembly.GetExecutingAssembly())
于 2016-03-12T12:54:37.320 回答