我有一个项目,将模板存储在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,则抛出异常在此上下文中绝对正常)
您需要使用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;
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
stuff,因为如果文件不是本地文件,你可能还是想使用.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:///
并且是完成它,我会说这个解决方案足够好,如果真的很难看。
这应该工作:
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库中进行登录。
另一个解决方案,包括复杂路径:
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));
}
由于您标记了此问题NUnit,您还可以使用AssemblyHelper.GetDirectoryName
获取正在执行的程序集的原始目录:
using System.Reflection;
using NUnit.Framework.Internal;
...
string path = AssemblyHelper.GetDirectoryName(Assembly.GetExecutingAssembly())