如何将Assembly.CodeBase转换为C#中的文件系统path?
我有一个将模板存储在DLL和EXE旁边的\Templates
文件夹中的项目。
我想在运行时确定这个文件path,但是使用一种可以在unit testing和生产环境中工作的技术(并且我不想在NUnit中禁用阴影复制!)
Assembly.Location
是不好的,因为它在NUnit下运行时返回阴影复制程序集的path。
Environment.CommandLine
也是有限的使用,因为在NUnit等它返回到NUnit的path,而不是我的项目。
Assembly.CodeBase
看起来很有前途,但它是一个UNCpath:
file:///D:/projects/MyApp/MyApp/bin/debug/MyApp.exe
现在我可以把它变成一个本地文件系统path使用string操作,但我怀疑有一个更干净的方式,把它埋在.NET框架的地方。 任何人都知道这样做的推荐方式?
(如果UNCpath不是一个file:///
抛出一个exceptionfile:///
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看起来很有前途,但它是一个UNCpath:
请注意,这是一个近似的文件uri , 而不是 UNCpath 。
你可以通过手工进行string操作来解决这个问题 认真。
尝试所有其他方法,你可以findSO与以下目录(逐字):
C:\Test\Space( )(h#)(p%20){[a&],t@,p%,+}.,\Release
这是一个有效的,如果有点不寻常的Windowspath。 (有些人会有这些angular色中的任何一个,你会想让你的方法为所有这些工作,对吧?)
可用的代码库( 我们不想要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:///
为前缀的常规本地path,并replace了反斜杠。 因此,它不能够将此提供给System.Uri
。 -
EscapedCodeBase
没有完全转义(我不知道这是一个错误还是这是URIscheme的一个缺点):- 注意空间字符(
)转换为
%20
- 但
%20
序列也转换为%20
! (百分比完全没有逃脱) - 没有人可以从这个破损的forms重build原来的!
- 注意空间字符(
对于本地文件(这真的是所有我关心的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);
我相信有人可以想出更好的解决scheme,如果是本地path的话,甚至可能会想出一个解决scheme,对CodeBase
属性进行正确的URL编码/解码,但是假设可以剥离file:///
并且完成它,我会说这个解决scheme足够好,如果真的很丑。
这应该工作:
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库中login。
还有一个解决scheme,包括复杂path:
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)); }
并testing:
[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())