2010-11-05 11 views
43

Tengo un proyecto que almacena plantillas en una carpeta \Templates junto a las DLL y EXE.¿Cómo puedo convertir Assembly.CodeBase en una ruta del sistema de archivos en C#?

quiero para determinar esta ruta de archivo en tiempo de ejecución, pero utilizando una técnica que funcione dentro de una unidad de prueba, así como en la producción (y no quiero desactivar sombra-copia en NUnit!)

Assembly.Location no es bueno porque devuelve la ruta del ensamblado copiado con sombra cuando se ejecuta bajo NUnit.

Environment.CommandLine también es de uso limitado porque en NUnit et al devuelve el camino a NUnit, no a mi proyecto.

Assembly.CodeBase parece prometedor, pero es una ruta UNC:

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

Ahora podría convertir esto en una ruta del sistema de archivos local mediante la manipulación de cadenas, pero sospecho que hay una manera más limpia de hacerlo enterrado en el .NET framework en alguna parte. Alguien sabe una forma recomendada de hacer esto?

(lanzar una excepción si la ruta de acceso UNC no es una URL file:/// es absolutamente bien en este contexto)

+0

posible dupl icate de [¿Hay un método .NET Framework para convertir archivos URI a rutas con letras de unidad?] (http://stackoverflow.com/questions/278761/is-there-a-net-framework-method-for-converting- file-uris-to-paths-with-drive-le) – nawfal

Respuesta

87

Es necesario utilizar System.Uri.LocalPath:

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

lo tanto, si desea que el ubicación original de la asamblea que se está ejecutando:

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

Hermosa. Muchas gracias. –

+0

Awesomeballs! ¡Gracias, gracias, gracias! –

+4

Tenga en cuenta que esto no funciona cuando un directorio tiene un '#' en su nombre – drake7707

5

Esto debería funcionar:

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

Estoy usando esto para poder iniciar sesión desde las bibliotecas dll usando un archivo log4net.config independiente.

17

Assembly.CodeBase parece prometedor, pero es una ruta UNC:

Ten en cuenta que se trata de algo parecido a un file uri, no un UNC path.


Resuelve esto haciendo la manipulación de cuerdas a mano. en serio.

Tratar todos los demás métodos se pueden encontrar en SO con el siguiente directorio (textualmente):

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

Se trata de un caso, ruta válida, tanto inusual de Windows. (Algunas personas se tienen uno de estos personajes de allí caminos, y que le gustaría que el método funcione para todo de esos, ¿verdad?)

La base de código disponible (we do not want Location, right?) propiedades son (en mi Win7 con.NET 4):

assembly.CodeBase -> file:///C:/Test/Space()(h#)(p%20){[a&],[email protected],p%,+}.,/Release 

assembly.EscapedCodeBase -> file:///C:/Test/Space(%20)(h%23)(p%20)%7B%5Ba%26%5D,[email protected],p%,+%7D.,/Release 

Se le nota:

  • CodeBase no se escaparon en absoluto, es sólo la ruta local regulares con el prefijo file:/// y las barras invertidas reemplazado. Como tal, no funciona para alimentar esto a System.Uri.
  • EscapedCodeBase no se escaparon por completo (lo hago no se sabe si se trata de un fallo, o si se trata de un defecto de la URI scheme):
    • Nota cómo el carácter de espacio () se traduce en %20
    • pero la secuencia %20también se traduce en %20! (Porcentaje % no se escapó en absoluto)
    • Nadie puede reconstruir el original de esta forma destrozada!

Para los archivos locales (Y eso es todo lo que me importa para la materia CodeBase, porque si el archivo no es local, es probable que desee utilizar .Location todos modos, las siguientes obras para mí (en cuenta que no es el más bonito, ya sea:

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

estoy seguro de que uno puede llegar a mejores soluciones, probablemente incluso se podría llegar a una solución que lo hace adecuado en- URL/decodificación de la propiedad si CodeBase es un camino local, pero dado que uno puede simplemente quitarse el file:/// un Hecho esto, yo diría que esta solución es lo suficientemente buena, si ciertamente realmente feo.

+1

Si se trata de una ruta de acceso UNC, es file: //server/share/path/to/assembly.dll (solo 2 barras diagonales). – Joshua

+0

@Joshua - ¿Quiere decir que una ruta '\\ server \ ...' está codificada con solo dos barras diagonales, en lugar de la 3 cuando tiene una unidad local? –

+0

@Joshua - ¿sabes cómo se codifican las rutas '\\? \ UNC \ server \ share'? –

1

Desde ha etiquetado esta pregunta NUnit, también se puede utilizar AssemblyHelper.GetDirectoryName para obtener el directorio original de la asamblea ejecución:

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

Uno más solución, incluyendo trayectorias complejas:

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

y pruebas:

[Test] 
    public void GetPathFromUriTest() 
    { 
     Assert.AreEqual(@"C:/Test/Space()(h#)(p%20){[a&],[email protected],p%,+}.,/Release", @"file:///C:/Test/Space()(h#)(p%20){[a&],[email protected],p%,+}.,/Release".GetPathFromUri()); 
     Assert.AreEqual(@"C:/Test/Space()(h#)(p%20){[a&],[email protected],p%,+}.,/Release", @"file://C:/Test/Space()(h#)(p%20){[a&],[email protected],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)); 
    } 
+0

¡Agradable! Pero preferiría las barras diagonales inversas, así que pasaría el resultado de 'GetPathFromUri' a' Path.GetFullPath'. – tm1

+0

Deberá tener en cuenta Uri.Host para admitir rutas de red. – tm1

Cuestiones relacionadas