2010-11-05 2 views
43

Ho un progetto che memorizza i modelli in una cartella \Templates accanto alle DLL e EXE.Come posso convertire Assembly.CodeBase in un percorso del file system in C#?

Voglio determinare questo percorso del file in fase di esecuzione, ma con una tecnica che funziona all'interno di una prova di unità così come nella produzione (e io non voglio disabilitare shadow-copia in NUnit!)

Assembly.Location non va bene perché restituisce il percorso del gruppo copiato dall'ombra durante l'esecuzione in NUnit.

Environment.CommandLine è anche di uso limitato perché in NUnit et al restituisce il percorso a NUnit, non al mio progetto.

Assembly.CodeBase sembra essere molto promettente, ma è un percorso UNC:

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

Ora potrebbe trasformare questo in un percorso file system locale utilizzando manipolazione delle stringhe, ma ho il sospetto c'è un modo più pulito di farlo sepolto nella .NET framework da qualche parte. Qualcuno conosce un modo consigliato per farlo?

(un'eccezione se il percorso UNC non è un URL file:/// è assolutamente bene in questo contesto)

+0

possibile dupl icate of [Esiste un metodo .NET Framework per convertire gli URI di file in percorsi con lettere di unità?] (http://stackoverflow.com/questions/278761/is-there-a-net-framework-method-for-converting- file-uris-to-path-with-drive-le) – nawfal

risposta

87

è necessario utilizzare System.Uri.LocalPath:

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

Quindi, se si desidera che il posizione originale del gruppo attualmente in esecuzione:

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

Bello. Grazie molto. –

+0

Awesomeballs! Grazie, grazie, grazie! –

+4

Si noti che questo non funziona quando una directory ha un '#' nel suo nome – drake7707

5

Questo dovrebbe funzionare:

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

Lo sto utilizzando per poter effettuare il log delle librerie dll utilizzando un file log4net.config standalone.

17

Assembly.CodeBase sembra essere molto promettente, ma è un percorso UNC:

Do atto che si tratta di qualcosa di approssimare un file uri, non un UNC path.


Si risolve eseguendo la manipolazione delle stringhe a mano. Seriamente.

Prova tutti gli altri metodi si possono trovare sul SO con la seguente directory (testualmente):

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

Questa è una, se un po 'insolita, percorso di Windows valido. (Alcune persone saranno hanno uno di questi personaggi in là percorsi, e si vorrebbe che si metodo di lavorare per tutte di questi, giusto?)

La base di codice a disposizione (we do not want Location, right?) proprietà sono poi (su il mio 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 

Noterete:

  • CodeBase non è sfuggito a tutti, è solo il percorso locale regolare prefisso file:/// e le barre inverse sostituito. Come tale, lo non funziona con per inviarlo a System.Uri.
  • EscapedCodeBase non è sfuggito completamente (io lo faccio non so se si tratta di un bug o se questo è un difetto della URI scheme):
    • Si noti come il carattere di spazio () si traduce in %20
    • ma la sequenza %20 corrisponde a %20! (% % non è sfuggito affatto)
    • Nessuno può ricostruire l'originale da questa forma deformata!

Per i file locali (E questo è davvero tutto quello che avevo a cuore per la roba CodeBase, perché se il file non è locale, probabilmente si desidera utilizzare .Location in ogni caso, i seguenti lavori per me (notare che non è la più bella sia:

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

sono sicuro che si può trovare soluzioni migliori, probabilmente si potrebbe anche trovare una soluzione che fa proprio URL it-/decodifica del CodeBase proprietà se è un percorso locale, ma dato che si può semplicemente togliere lo file:/// D sarebbe fatto con esso, direi che questa soluzione è abbastanza buona, se certamente davvero brutto.

+1

Se si tratta di un percorso UNC, è file: //server/share/path/to/assembly.dll (solo 2 barre). – Joshua

+0

@Joshua - intendi dire che un percorso '\\ server \ ...' è codificato con solo due barre, invece del 3 quando hai un'unità locale? –

+0

@Joshua - sai come i percorsi '\\? \ UNC \ server \ share' sono codificati? –

1

Dal momento che hai contrassegnato questa domanda NUnit, è anche possibile utilizzare AssemblyHelper.GetDirectoryName per ottenere la directory originale del gruppo eseguendo:

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

più Una soluzione, tra cui percorsi complessi:

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

e prove:

[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

Bello! Ma preferirei i backslash, quindi passerei il risultato da 'GetPathFromUri' a' Path.GetFullPath'. – tm1

+0

È necessario tenere in considerazione Uri.Host per supportare i percorsi di rete. – tm1