Come ottengo il percorso dell'assembly in cui si trova il codice?


782

C'è un modo per ottenere il percorso dell'assembly in cui risiede il codice corrente? Non voglio il percorso dell'assembly chiamante, ma solo quello contenente il codice.

Fondamentalmente il mio test unitario deve leggere alcuni file di test XML che si trovano relativamente alla DLL. Voglio che il percorso si risolva sempre correttamente indipendentemente dal fatto che la DLL di test sia eseguita da TestDriven.NET, dalla GUI MbUnit o da qualcos'altro.

Modifica : le persone sembrano fraintendere ciò che sto chiedendo.

La mia libreria di test si trova in dire

C: \ Projects \ MyApplication \ daotests \ bin \ Debug \ daotests.dll

e vorrei ottenere questo percorso:

C: \ projects \ MyApplication \ daotests \ bin \ Debug \

I tre suggerimenti finora mi mancano quando corro dalla MbUnit Gui:

  • Environment.CurrentDirectoryc: \ Programmi \ MbUnit

  • System.Reflection.Assembly.GetAssembly(typeof(DaoTests)).Location fornisce C: \ Documents and Settings \ george \ Local Settings \ Temp \ .... \ DaoTests.dll

  • System.Reflection.Assembly.GetExecutingAssembly().Location dà lo stesso del precedente.


102
Questa è la tua soluzione: var dir = AppDomain.CurrentDomain.BaseDirectory;
Jalal El-Shaer,

7
Questa dovrebbe essere la soluzione accettata. AppDomain.CurrentDomain.BaseDirectory è l'approccio corretto.
aBetterGamer il


2
Sono venuto qui alla ricerca di una soluzione per un pacchetto nuget per leggere un file JSON dalla sua directory pacakge. Sembra che quando viene eseguito un pacchetto nuget "AppDomain.CurrentDomain.BaseDirectory" punta alla directory dei progetti in esecuzione e non alla directory del pacchetto nuget. Nessuno di questi sembra indirizzare correttamente la directory del pacchetto nuget.
Lucas,

@Lucas no, perché non è questa la domanda (in effetti quando è stata posta, nuget non esisteva) - sentiti libero di iniziare una nuova domanda e di farmi un ping, ma posso dirti ora che è impossibile nella maggior parte dei casi. Per la maggior parte dei progetti la directory nuget si trova packagesaccanto al file sln. MA quando compili e distribuisci cose non c'è nessun file sln e nessuna directory dei pacchetti. Durante la compilazione, le cose necessarie (ma non tutto) vengono copiate nella directory bin. La soluzione migliore è utilizzare uno script postbuild per copiare il file desiderato.
George Mauer,

Risposte:


1037

Ho definito la seguente proprietà poiché la usiamo spesso nei test unitari.

public static string AssemblyDirectory
{
    get
    {
        string codeBase = Assembly.GetExecutingAssembly().CodeBase;
        UriBuilder uri = new UriBuilder(codeBase);
        string path = Uri.UnescapeDataString(uri.Path);
        return Path.GetDirectoryName(path);
    }
}

La Assembly.Locationproprietà a volte ti dà alcuni risultati divertenti quando si utilizza NUnit (dove gli assembly vengono eseguiti da una cartella temporanea), quindi preferisco usare quello CodeBaseche ti dà il percorso in formato URI, quindi UriBuild.UnescapeDataStringrimuove File://l'inizio e lo GetDirectoryNamecambia nel normale formato di Windows .


29
Questo ha un problema che ho riscontrato, se il nome della tua directory è: c: \ My% 20Directory, Uri.UnescapeDataString restituirà: c: \ My Directory Ciò significa che File.Exists ("c: \ My Directory \ MyFile.txt ") restituirà false poiché il percorso corretto è in realtà" c: \ My% 20Directory \ MyFile.txt "Mi sono imbattuto in questo perché i nostri percorsi SVN contengono spazi e quando li controlliamo codifica gli spazi.
row1,

5
Fai attenzione quando lo usi per controllare File.Exist () poiché questo metodo restituirà false sul percorso UNC. Usa invece la risposta di @ Keith.
AZ.

3
Non sapevo che potresti mettere statico prima del pubblico. Bello da sapere e penso che preferisco per leggibilità
Valamas

5
Nota: questo non funziona con i percorsi di rete (es. \\ REMOT_EPC \ Folder)
Muxa

5
Inoltre, questo non funzionerà se la directory contiene segni numerici '#'. I segni numerici sono consentiti nei nomi di directory e file in Windows.
Huemac,

322

questo aiuta?

//get the full location of the assembly with DaoTests in it
string fullPath = System.Reflection.Assembly.GetAssembly(typeof(DaoTests)).Location;

//get the folder that's in
string theDirectory = Path.GetDirectoryName( fullPath );

vedi la mia modifica, no, è qualcosa di strano su come MbUnit fa le cose?
George Mauer,

3
Impostare i file xml in modo che siano contenuti, copiati con la dll o risorse letti dalla dll.
Keith,

22
O semplicementetypeof(DaoTests).Assembly
SLaks

4
@SLaks @JohnySkovdal @Keith: Hey ragazzi, usate Assembly.GetExecutingAssembly(). Esso "ottiene l'assembly che contiene il codice che è attualmente in esecuzione" (da descrizione del metodo). Lo uso nel mio AddIn " EntitiesToDTOs ". Vedi AssemblyHelper.cs per un vero esempio.
kzfabi,

4
Ho avuto un problema con il post di @John Silby, in quanto non sembra funzionare per i percorsi UNC ... ad esempio \\ Server \ Cartella \ File.ext. Questo ha funzionato. +1
Blueberry

312

È semplice come questo:

var dir = AppDomain.CurrentDomain.BaseDirectory;

11
Questa dovrebbe essere la soluzione accettata. AppDomain.CurrentDomain.BaseDirectory è l'approccio corretto.
aBetterGamer il

5
grazie per riportare la mia attenzione su questo - non sono sicuro che fosse disponibile al momento in cui ho posto la domanda, ma è ora.
George Mauer,

120
No, questo è sbagliato. Ciò restituisce il percorso del PUNTO DI ENTRATA ORIGINALE non il codice attualmente in esecuzione. Se un assieme è stato caricato manualmente da un percorso diverso o se è stato caricato da GAC, restituirà un risultato errato. Questa risposta è corretta: stackoverflow.com/a/283917/243557 Lo è ancora più veloce Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location).
Nathanchere,

9
In realtà questo non funzionerà nelle applicazioni web ma per quanto ho scoperto il seguente potenziamento dovrebbe funzionare per qualsiasi tipo di applicazione:AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory
Ilya Chernomordik

4
Questo è eccellente per i test unitari se si desidera solo ottenere il percorso bin originale del proprio assieme di test (ad esempio, per raggiungere i file di dati ausiliari nelle sottocartelle). Il gruppo di test è il punto di ingresso del codice.
MarioDS,

68

Come la risposta di John, ma un metodo di estensione leggermente meno dettagliato.

public static string GetDirectoryPath(this Assembly assembly)
{
    string filePath = new Uri(assembly.CodeBase).LocalPath;
    return Path.GetDirectoryName(filePath);            
}

Ora puoi fare:

var localDir = Assembly.GetExecutingAssembly().GetDirectoryPath();

o se preferisci:

var localDir = typeof(DaoTests).Assembly.GetDirectoryPath();

6
Intendevi assemblyinvece di Assembly.GetExecutingAssembly()?
Amico Pascalou, il

3
Come dice Dude, hai passato una discussione e non l'hai utilizzata.
Chris Moschini,

4
Questa risposta è semplicemente sbagliata per la domanda in corso. Una versione modificata di questa risposta potrebbe fornire il percorso di un determinato assembly. Tuttavia, qui, stiamo specificamente cercando l'assembly in esecuzione, e quindi passare in un assembly non ha senso. Un metodo di estensione è lo strumento sbagliato per il lavoro.
Edward Brey,

46

L'unica soluzione che ha funzionato per me durante l'utilizzo di condivisioni di rete CodeBase e UNC era:

System.IO.Path.GetDirectoryName(new System.Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath);

Funziona anche con URI normali.


5
Questa dovrebbe essere la risposta accettata. È davvero fastidioso che la base di codice predefinita non gestisca correttamente le condivisioni UNC.
Daniel Gilbert,

Questo si blocca quando la cartella contiene spazi e dio sa quali altri personaggi ...
MarioDS

1
Ho usato molto questo e ho trovato uno scenario in cui fallisce: se questa riga di codice fa parte di un pacchetto NuGet che viene quindi utilizzato da un'applicazione! Possiamo supportare anche quello scenario sostituendo GetExecutingAssembly()con GetCallingAssembly().
Timo,

@Timo: hai verificato se questa modifica ha effetti collaterali? In tal caso, modifica la risposta per includere la correzione.
Ignacio Soler Garcia,

@IgnacioSolerGarcia Purtroppo devo dire che ha funzionato solo uno strato in profondità, cioè non riesce se il pacchetto NuGet è stato chiamato da un altro pacchetto NuGet! Ora sto usando questo (da un commento su questa pagina Chernomordik): AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory. La prima parte è per le applicazioni Web e la seconda per altre applicazioni.
Timo,

32

Questo dovrebbe funzionare, a meno che l'assembly non sia copiato nell'ombra :

string path = System.Reflection.Assembly.GetExecutingAssembly().Location

14

Che dire di questo:

System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

11

Sospetto che il vero problema qui sia che il test runner sta copiando l'assembly in una posizione diversa. In fase di runtime non c'è modo di dire da dove è stato copiato l'assembly, ma probabilmente è possibile ruotare un interruttore per dire al test runner di eseguire l'assembly da dove si trova e non copiarlo in una directory shadow.

Un simile interruttore è probabilmente diverso per ogni corridore di prova, ovviamente.

Hai preso in considerazione l'idea di incorporare i tuoi dati XML come risorse all'interno del tuo assieme di test?


+1 per evidenziare il problema con la copia shadow. Tuttavia, è davvero possibile determinare il luogo originale dal Assembly.CodeBase.
tm1

11
AppDomain.CurrentDomain.BaseDirectory

funziona con la GUI di MbUnit.


Questo ha funzionato benissimo per la scrittura di un file relativo alla directory principale in un'app web asp.net
Philip Pittle,

Ho scoperto che questo funziona meglio in generale. Sceglilo se non sei sicuro.
Erik Bergstedt,

10

Credo che questo funzionerebbe per qualsiasi tipo di applicazione:

AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory

1
I miei esperimenti dimostrano che questa è la risposta più sicura, coprendo non solo le applicazioni web e console, ma anche le chiamate da unit test e pacchetti NuGet (nidificati a qualsiasi livello di ricorsione).
Timo,

8

Per quanto ne so, la maggior parte delle altre risposte presenta alcuni problemi.

Il modo corretto di eseguire questa operazione per un assembly basato su disco (anziché su web), non GACed è utilizzare l'assembly attualmente in esecuzioneCodeBase proprietà .

Questo restituisce un URL ( file://). Invece di scherzare con la manipolazione di stringhe o UnescapeDataString, questo può essere convertito con il minimo sforzo sfruttando la LocalPathproprietà di Uri.

var codeBaseUrl = Assembly.GetExecutingAssembly().CodeBase;
var filePathToCodeBase = new Uri(codeBaseUrl).LocalPath;
var directoryPath = Path.GetDirectoryName(filePathToCodeBase);

1
Non funziona se il percorso contiene #( EscapedCodeBasefunziona, ma EscapedCodeBase non funziona se il percorso contiene ad esempio %20verbatim (che è una sequenza di caratteri consentita in un percorso di Windows)
Martin Ba

Se vogliamo avere questo codice in un pacchetto NuGet, possiamo correggere quello scenario sostituendolo GetExecutingAssembly()con GetCallingAssembly().
Timo,

8

Cosa ne pensi di questo ...

string ThisdllDirectory = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

Quindi taglia via quello che non ti serve


7
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var assemblyPath = assembly.GetFiles()[0].Name;
var assemblyDir = System.IO.Path.GetDirectoryName(assemblyPath);

7

Ecco una porta VB.NET del codice di John Sibly. Visual Basic non fa distinzione tra maiuscole e minuscole, quindi un paio dei suoi nomi di variabili si scontrano con nomi di tipi.

Public Shared ReadOnly Property AssemblyDirectory() As String
    Get
        Dim codeBase As String = Assembly.GetExecutingAssembly().CodeBase
        Dim uriBuilder As New UriBuilder(codeBase)
        Dim assemblyPath As String = Uri.UnescapeDataString(uriBuilder.Path)
        Return Path.GetDirectoryName(assemblyPath)
    End Get
End Property

6

In tutti questi anni, nessuno ha mai menzionato questo. Un trucco che ho imparato dal fantastico progetto ApprovalTests . Il trucco è che si utilizzano le informazioni di debug nell'assembly per trovare la directory originale.

Questo non funzionerà in modalità RELEASE, né con le ottimizzazioni abilitate, né su una macchina diversa da quella su cui è stata compilata.

Ma questo ti porterà percorsi relativi alla posizione del file di codice sorgente da cui lo chiami

public static class PathUtilities
{
    public static string GetAdjacentFile(string relativePath)
    {
        return GetDirectoryForCaller(1) + relativePath;
    }
    public static string GetDirectoryForCaller()
    {
        return GetDirectoryForCaller(1);
    }


    public static string GetDirectoryForCaller(int callerStackDepth)
    {
        var stackFrame = new StackTrace(true).GetFrame(callerStackDepth + 1);
        return GetDirectoryForStackFrame(stackFrame);
    }

    public static string GetDirectoryForStackFrame(StackFrame stackFrame)
    {
        return new FileInfo(stackFrame.GetFileName()).Directory.FullName + Path.DirectorySeparatorChar;
    }
}

5

La directory corrente in cui esisti.

Environment.CurrentDirectory;  // This is the current directory of your application

Se copi il file .xml con build dovresti trovarlo.

o

System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly(typeof(SomeObject));

// The location of the Assembly
assembly.Location;

questo sarà problematico se l'assembly è stato copiato in ombra .
spender

1520! Environment.CurrentDirectoryfunziona se si utilizza la riflessione nella classe di attività MSBuild, in cui l'assembly in esecuzione risiede in GAC e il codice è altrove.
corvo vulcaniano

4
In generale CurrentDirectory non ti dice dove risiedono i tuoi eseguibili. Non è quello per cui è usato. Capita spesso di trovarsi nella stessa posizione in cui si trovano gli eseguibili, quindi molti programmatori non capiscono la differenza. Quindi finiscono per creare problemi per alcuni degli utenti finali che si aspettavano che l'applicazione comprendesse l'uso corretto di CurrentDirectory.
Bent Tranberg,

5

Sto usando Assembly.CodeBase invece di Location:

Assembly a;
a = Assembly.GetAssembly(typeof(DaoTests));
string s = a.CodeBase.ToUpper(); // file:///c:/path/name.dll
Assert.AreEqual(true, s.StartsWith("FILE://"), "CodeBase is " + s);
s = s.Substring(7, s.LastIndexOf('/') - 7); // 7 = "file://"
while (s.StartsWith("/")) {
    s = s.Substring(1, s.Length - 1);
}
s = s.Replace("/", "\\");

Funziona, ma non sono più sicuro che sia corretto al 100%. La pagina all'indirizzo http://blogs.msdn.com/suzcook/archive/2003/06/26/assembly-codebase-vs-assembly-location.aspx dice:

"CodeBase è un URL del luogo in cui è stato trovato il file, mentre Location è il percorso in cui è stato effettivamente caricato. Ad esempio, se l'assembly è stato scaricato da Internet, il CodeBase potrebbe iniziare con" http: // " , ma la sua posizione potrebbe iniziare con "C: \". Se il file è stato copiato in ombra, la posizione sarebbe il percorso della copia del file nella directory della copia shadow. È anche utile sapere che CodeBase non è garantito da impostare per gli assembly nel GAC. Tuttavia, la posizione verrà sempre impostata per gli assembly caricati dal disco. "

Si potrebbe voler usare Codebase al posto di Posizione.


1
@Kiquenet: tanto codice solo per convertire un URI in un percorso. Sicuro che potrebbe essere migliorato. Guarda la risposta di Mike Schall o SoMoS. Non dovresti provare a convertire gli URI a livello di stringa, ma usa invece gli oggetti adatti. OK, è anche goffo che Assembly.CodeBase restituisca una stringa anziché un oggetto più adatto, come URI o FileInfo.
Sette

2

È possibile ottenere il percorso bin da AppDomain.CurrentDomain.RelativeSearchPath


2

Tutte le risposte proposte funzionano quando lo sviluppatore può modificare il codice per includere lo snippet richiesto, ma se si desidera farlo senza modificare alcun codice, è possibile utilizzare Process Explorer.

Elencherà tutte le DLL in esecuzione sul sistema, potrebbe essere necessario determinare l'ID del processo dell'applicazione in esecuzione, ma di solito non è troppo difficile.

Ho scritto una descrizione completa di come farlo per una DLL all'interno di II - http://nodogmablog.bryanhogan.net/2016/09/locating-and-checking-an-executing-dll-on-a-running-web -server/


Si noti che prima di tutto, il codice nell'articolo è abbastanza incentrato su IIS e in secondo luogo, ti dà (credo) tutte le DLL attualmente caricate , non ciò che è in esecuzione in qualsiasi momento.
George Mauer,

L'esempio fornito si riferisce a IIS, ma si applicano gli stessi passaggi se la DLL è in esecuzione in un processo esterno a IIS. Si tratta solo di identificare l'id del processo. Aggiornerò l'articolo per notare che. Grazie per il suggerimento
Bryan,

2

in un'app di Windows Form, puoi semplicemente usare Application.StartupPath

ma per le DLL e le app per console il codice è molto più difficile da ricordare ...

string slash = Path.DirectorySeparatorChar.ToString();
string root = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

root += slash;
string settingsIni = root + "settings.ini"

1
string path = Path.GetDirectoryName(typeof(DaoTests).Module.FullyQualifiedName);

1

Otterrai una directory errata se un percorso contiene il simbolo '#'. Quindi uso una modifica della risposta John Sibly che è la combinazione UriBuilder.Path e UriBuilder.Fragment:

public static string AssemblyDirectory
{
    get
    {
        string codeBase = Assembly.GetExecutingAssembly().CodeBase;
        UriBuilder uri = new UriBuilder(codeBase);
        //modification of the John Sibly answer    
        string path = Uri.UnescapeDataString(uri.Path.Replace("/", "\\") + 
          uri.Fragment.Replace("/", "\\"));
        return Path.GetDirectoryName(path);
     }
}

0

Questo è quello che mi è venuto in mente. Tra progetti web, unit test (nunit e resharper test runner) ; Ho trovato che ha funzionato per me.

Sono stato alla ricerca di codice per rilevare quale configurazione della build è in, Debug/Release/CustomName. Ahimè, il #if DEBUG. Quindi se qualcuno può migliorarlo !

Sentiti libero di modificare e migliorare.

Ottenere la cartella dell'app . Utile per web root, unittest per ottenere la cartella dei file di test.

public static string AppPath
{
    get
    {
        DirectoryInfo appPath = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory);

        while (appPath.FullName.Contains(@"\bin\", StringComparison.CurrentCultureIgnoreCase)
                || appPath.FullName.EndsWith(@"\bin", StringComparison.CurrentCultureIgnoreCase))
        {
            appPath = appPath.Parent;
        }
        return appPath.FullName;
    }
}

Ottenere cartella bin : utile per eseguire assiemi usando reflection. Se i file vengono copiati lì a causa delle proprietà di compilazione.

public static string BinPath
{
    get
    {
        string binPath = AppDomain.CurrentDomain.BaseDirectory;

        if (!binPath.Contains(@"\bin\", StringComparison.CurrentCultureIgnoreCase)
            && !binPath.EndsWith(@"\bin", StringComparison.CurrentCultureIgnoreCase))
        {
            binPath = Path.Combine(binPath, "bin");
            //-- Please improve this if there is a better way
            //-- Also note that apps like webapps do not have a debug or release folder. So we would just return bin.
#if DEBUG
            if (Directory.Exists(Path.Combine(binPath, "Debug"))) 
                        binPath = Path.Combine(binPath, "Debug");
#else
            if (Directory.Exists(Path.Combine(binPath, "Release"))) 
                        binPath = Path.Combine(binPath, "Release");
#endif
        }
            return binPath;
    }
}

0

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

Sto usando questo per distribuire librerie di file DLL insieme ad alcuni file di configurazione (questo è per usare log4net dall'interno del file DLL).


A cosa fileMapserve qui?
George Mauer

0

Trovo la mia soluzione adeguata per il recupero della posizione.

var executingAssembly = new FileInfo((Assembly.GetExecutingAssembly().Location)).Directory.FullName;

Questa è già una delle risposte più votate ed è esplicitamente menzionata nella domanda come qualcosa che non funziona in questa situazione.
George Mauer,

Ci scusiamo per averlo perso! Ovviamente non ho letto attentamente.
Tez Wingfield,

0

Ho avuto lo stesso comportamento in NUnitpassato. Per impostazione predefinita, NUnitcopia l'assembly nella directory temporanea. È possibile modificare questo comportamento nelle NUnitimpostazioni:

inserisci qui la descrizione dell'immagine

Forse TestDriven.NETe la MbUnitGUI hanno le stesse impostazioni.


-3

Lo uso per ottenere il percorso della directory bin:

var i = Environment.CurrentDirectory.LastIndexOf(@"\");
var path = Environment.CurrentDirectory.Substring(0,i); 

Ottieni questo risultato:

"c: \ users \ ricooley \ documenti \ visual studio 2010 \ Projects \ Windows_Test_Project \ Windows_Test_Project \ bin"


6
Non vedo un motivo per evitare Path.getDirectoryName qui
Max Keller,

@MaxKeller Se non vedi i motivi, ciò non significa che sia giusto. Questo metodo alternativo di Path.GetDirectoryName è dieci volte più veloce.
Ruslan Veselov,

-3

Applicazione web?

Server.MapPath("~/MyDir/MyFile.ext")

2
@christiandev questa è una risposta, ma forse sembra essere una risposta alla domanda sbagliata. Dalla domanda è abbastanza chiaro che questa non è un'applicazione web ma un assembly in esecuzione con MbUnit. Detto questo, la risposta non è ancora corretta a causa della copia shadow di Asp.Net (anche se potrebbe essere plausibilmente ciò che qualcuno sta cercando su questa domanda).
George Mauer,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.