¿Cómo obtengo la ruta del ensamblado en el que se encuentra el código?

782

¿Hay alguna manera de obtener la ruta para el ensamblado en el que reside el código actual? No quiero la ruta del ensamblado de llamada, solo la que contiene el código.

Básicamente, mi prueba unitaria necesita leer algunos archivos de prueba xml que se encuentran en relación con el dll. Quiero que la ruta siempre se resuelva correctamente, independientemente de si el dll de prueba se ejecuta desde TestDriven.NET, la GUI MbUnit u otra cosa.

Editar : La gente parece estar malentendiendo lo que estoy preguntando.

Mi biblioteca de prueba está ubicada en say

C: \ projects \ myapplication \ daotests \ bin \ Debug \ daotests.dll

y me gustaría obtener este camino:

C: \ projects \ myapplication \ daotests \ bin \ Debug \

Las tres sugerencias hasta ahora me fallan cuando corro desde MbUnit Gui:

  • Environment.CurrentDirectory da c: \ Archivos de programa \ MbUnit

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

  • System.Reflection.Assembly.GetExecutingAssembly().Location da lo mismo que el anterior.

George Mauer
fuente
102
Esta es su solución: var dir = AppDomain.CurrentDomain.BaseDirectory;
Jalal El-Shaer
77
Esta debería ser la solución aceptada. AppDomain.CurrentDomain.BaseDirectory es el enfoque correcto.
aBetterGamer
2
Vine aquí buscando una solución para un paquete nuget para leer un archivo JSON de su directorio pacakge. Parece que cuando se ejecuta un paquete nuget, el "AppDomain.CurrentDomain.BaseDirectory" apunta al directorio de proyectos en ejecución, y no al directorio del paquete nuget. Ninguno de estos parece apuntar al directorio del paquete nuget correctamente.
Lucas
@Lucas, no, porque no era de eso de lo que se trataba esta pregunta (de hecho, cuando se hizo, Nuget no existía), siéntase libre de comenzar una nueva pregunta y enviarme un mensaje, pero puedo decirle ahora que Es imposible en la mayoría de los casos. Para la mayoría de los proyectos, el directorio nuget está al packageslado del archivo sln. PERO cuando compila y distribuye cosas, no hay archivos sln ni directorios de paquetes. Durante la compilación, las cosas que se necesitan (pero no todo) se copian en el directorio bin. Su mejor opción es utilizar una secuencia de comandos posterior a la creación para copiar el archivo que desee.
George Mauer

Respuestas:

1037

He definido la siguiente propiedad ya que la usamos a menudo en pruebas unitarias.

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.Locationpropiedad a veces le da algunos resultados divertidos cuando usa NUnit (donde los ensamblajes se ejecutan desde una carpeta temporal), por lo que prefiero usar el CodeBaseque le da la ruta en formato URI, luego UriBuild.UnescapeDataStringelimina el File://al principio y lo GetDirectoryNamecambia al formato normal de Windows .

John Sibly
fuente
29
Este es un problema que encontré, si el nombre de su directorio es: c: \ My% 20Directory, Uri.UnescapeDataString devolverá: c: \ My Directory Esto significa que File.Exists ("c: \ My Directory \ MyFile.txt ") devolverá falso ya que la ruta correcta es en realidad" c: \ My% 20Directory \ MyFile.txt "Encontré esto ya que nuestras rutas SVN tienen espacios en ellas y cuando las revisamos codifica los espacios.
fila1
55
Tenga cuidado cuando use esto para verificar File.Exist () ya que este método devolverá falso en la ruta UNC. Utilice la respuesta de @ Keith en su lugar.
AZ.
3
No sabía que podía poner estática antes que pública. Es bueno saberlo y creo que prefiero la legibilidad
Valamas
55
Nota: esto no funciona con ubicaciones de red (por ejemplo, \\ REMOT_EPC \ Folder)
Muxa
55
Además, esto no funcionará si el directorio tiene signos de número '#'. Se permiten signos numéricos en los nombres de directorio y archivo en Windows.
Huemac
322

¿Esto ayuda?

//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 );
Keith
fuente
vea mi edición, no es así, ¿es algo extraño acerca de cómo MbUnit hace las cosas?
George Mauer
3
Configure los archivos xml para que sean contenidos, copiados con el dll o recursos, leídos desde el dll.
Keith
22
O simplementetypeof(DaoTests).Assembly
SLaks
44
@SLaks @JohnySkovdal @Keith: Hola chicos, usen Assembly.GetExecutingAssembly(). Es "recibe el ensamblado que contiene el código que se ejecuta actualmente" (a partir de la descripción del método). Lo uso en mi complemento " EntitiesToDTOs ". Ver AssemblyHelper.cs para un ejemplo real.
kzfabi
44
Tuve un problema con la publicación de @John Silby, ya que no parece que funcione para rutas UNC ... por ejemplo, \\ Server \ Folder \ File.ext. Este hizo el truco. +1
Arándano
312

Es tan simple como esto:

var dir = AppDomain.CurrentDomain.BaseDirectory;
Jalal El-Shaer
fuente
11
Esta debería ser la solución aceptada. AppDomain.CurrentDomain.BaseDirectory es el enfoque correcto.
aBetterGamer
55
gracias por llamar mi atención sobre esto, no estoy seguro de si eso estaba disponible en el momento en que hice la pregunta, pero ahora está
George Mauer
120
No, esto está mal. Esto devuelve la ruta del PUNTO DE ENTRADA ORIGINAL no el código que se está ejecutando actualmente. Si ha cargado un ensamblaje manualmente desde una ruta diferente, o si se ha cargado desde GAC, devolverá el resultado incorrecto. Esta respuesta es correcta: stackoverflow.com/a/283917/243557 Más rápido aún lo es Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location).
nathanchere
99
En realidad, esto no funcionará en aplicaciones web, pero por lo que he encontrado, el siguiente aumento debería funcionar para cualquier tipo de aplicación:AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory
Ilya Chernomordik
44
Esto es excelente para las pruebas unitarias si solo desea obtener la ruta original del conjunto de pruebas (por ejemplo, para llegar a archivos de datos auxiliares en subcarpetas). El conjunto de prueba es el punto de entrada de su código.
MarioDS
68

Igual que la respuesta de John, pero un método de extensión un poco menos detallado.

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

Ahora puedes hacer:

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

o si lo prefieres:

var localDir = typeof(DaoTests).Assembly.GetDirectoryPath();
Sneal
fuente
66
¿Querías decir en assemblylugar de Assembly.GetExecutingAssembly()?
Amigo Pascalou
3
Como señala Dude, pasaste una discusión y no pudiste usarla.
Chris Moschini
44
Esta respuesta es simplemente incorrecta para la pregunta en cuestión. Una versión modificada de esta respuesta podría darle la ruta de un ensamblado dado. Sin embargo, aquí, estamos buscando específicamente el ensamblaje en ejecución, por lo que pasar un ensamblado no tiene sentido. Un método de extensión es la herramienta incorrecta para el trabajo.
Edward Brey
46

La única solución que me funcionó al usar CodeBase y los recursos compartidos de la red UNC fue:

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

También funciona con URI normales también.

Ignacio Soler García
fuente
55
Esta debería ser la respuesta aceptada. Es realmente molesto que la base de código predeterminada no maneje los recursos compartidos UNC correctamente.
Daniel Gilbert
Esto se viene abajo cuando la carpeta contiene espacios y Dios sabe qué otros personajes ...
MarioDS
1
He estado usando esto mucho y he encontrado un escenario en el que falla: si esta línea de código en sí misma es parte de un paquete NuGet que luego es utilizado por una aplicación. También podemos apoyar ese escenario reemplazando GetExecutingAssembly()por GetCallingAssembly().
Timo
@Timo: ¿has verificado si este cambio tiene efectos secundarios? Si es así, edite la respuesta para incluir la solución.
Ignacio Soler Garcia
@IgnacioSolerGarcia Lamentablemente, debo informar que solo funcionó una capa de profundidad, es decir, falla si el paquete NuGet fue llamado por otro paquete NuGet. Ahora estoy usando este (de un comentario en esta página por Chernomordik): AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory. La primera parte es para aplicaciones web y la segunda para otras aplicaciones.
Timo
32

Esto debería funcionar, a menos que el ensamblaje se copie de forma oculta :

string path = System.Reflection.Assembly.GetExecutingAssembly().Location
jodonnell
fuente
14

¿Qué hay de esto?

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

Sospecho que el problema real aquí es que su corredor de prueba está copiando su ensamblaje en una ubicación diferente. No hay forma en el tiempo de ejecución de saber de dónde se copió el ensamblaje, pero probablemente pueda accionar un interruptor para decirle al corredor de prueba que ejecute el ensamblaje desde donde está y que no lo copie en un directorio oculto.

Es probable que dicho cambio sea diferente para cada corredor de prueba, por supuesto.

¿Ha considerado incrustar sus datos XML como recursos dentro de su ensamblaje de prueba?

Curt Hagenlocher
fuente
+1 para señalar el problema con las instantáneas. Sin embargo, es posible determinar el lugar original a partir de Assembly.CodeBase.
tm1
11
AppDomain.CurrentDomain.BaseDirectory

funciona con MbUnit GUI.

usuario368021
fuente
Esto funcionó muy bien para escribir un archivo relativo al directorio raíz en una aplicación web asp.net
Philip Pittle
He encontrado que este funciona mejor en general. Elígelo si no estás seguro.
Erik Bergstedt
10

Creo que esto funcionaría para cualquier tipo de aplicación:

AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory
Ilya Chernomordik
fuente
1
Mis experimentos muestran que esta es la respuesta más infalible, que cubre no solo las aplicaciones web y de consola, sino también las llamadas de pruebas unitarias y paquetes NuGet (anidados a cualquier nivel de recursión).
Timo
8

Por lo que puedo decir, la mayoría de las otras respuestas tienen algunos problemas.

La forma correcta de hacer esto para un ensamblado no GACed basado en disco (en lugar de basado en web) es utilizar el ensamblado que se está ejecutando actualmenteCodeBase propiedad .

Esto devuelve una URL ( file://). En lugar de perder el tiempo con la manipulación de cadenas o UnescapeDataString, esto se puede convertir con un mínimo de alboroto aprovechando la LocalPathpropiedad de Uri.

var codeBaseUrl = Assembly.GetExecutingAssembly().CodeBase;
var filePathToCodeBase = new Uri(codeBaseUrl).LocalPath;
var directoryPath = Path.GetDirectoryName(filePathToCodeBase);
gastador
fuente
1
No funciona si la ruta contiene #( EscapedCodeBasefunciona, pero EscapedCodeBase no funciona si la ruta contiene, por ejemplo, %20textualmente (que es una secuencia de caracteres permitida en una ruta de Windows)
Martin Ba
Si queremos tener este código en un paquete NuGet, podemos arreglar ese escenario reemplazándolo GetExecutingAssembly()por GetCallingAssembly().
Timo
8

Qué tal esto ...

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

Luego simplemente corta lo que no necesitas

David C Fuchs
fuente
7
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var assemblyPath = assembly.GetFiles()[0].Name;
var assemblyDir = System.IO.Path.GetDirectoryName(assemblyPath);
Mark Cidade
fuente
7

Aquí hay un puerto VB.NET del código de John Sibly. Visual Basic no distingue entre mayúsculas y minúsculas, por lo que un par de sus nombres de variables colisionaron con los nombres de tipo.

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
Mike Schall
fuente
6

En todos estos años, nadie ha mencionado este. Un truco que aprendí del impresionante proyecto ApprovalTests . El truco es que usa la información de depuración en el ensamblado para encontrar el directorio original.

Esto no funcionará en modo RELEASE, ni con optimizaciones habilitadas, ni en una máquina diferente de la que fue compilada.

Pero esto le dará rutas relacionadas con la ubicación del archivo de código fuente desde el que lo llama

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;
    }
}
George Mauer
fuente
5

El directorio actual donde existes.

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

Si copia el archivo .xml con build, debería encontrarlo.

o

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

// The location of the Assembly
assembly.Location;
David Basarab
fuente
Esto será problemático si el ensamblaje ha sido copiado en la sombra .
gastador
+1520! Environment.CurrentDirectoryfunciona si está utilizando la reflexión en la clase de tarea MSBuild, donde el ensamblado en ejecución reside en GAC y su código está en otro lugar.
Cuervo vulcano
44
En general, CurrentDirectory no le dice dónde residen sus ejecutables. Para eso no se usa. Simplemente sucede que con frecuencia es la misma ubicación en la que se encuentran los ejecutables, por lo que muchos programadores no entienden la diferencia. Luego terminan creando problemas para algunos de los usuarios finales que esperaban que la aplicación entendiera el uso adecuado de CurrentDirectory.
Doblado Tranberg
5

He estado usando Assembly.CodeBase en lugar de 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("/", "\\");

Ha estado funcionando, pero ya no estoy seguro de que sea 100% correcto. La página en http://blogs.msdn.com/suzcook/archive/2003/06/26/assembly-codebase-vs-assembly-location.aspx dice:

"CodeBase es una URL del lugar donde se encontró el archivo, mientras que la ubicación es la ruta donde realmente se cargó. Por ejemplo, si el ensamblado se descargó de Internet, su CodeBase puede comenzar con" http: // " , pero su ubicación puede comenzar con "C: \". Si el archivo se ha copiado en la sombra, la ubicación sería la ruta a la copia del archivo en el directorio de copia de la sombra. También es bueno saber que CodeBase no está garantizado Sin embargo, la ubicación siempre se establecerá para los ensamblados cargados desde el disco. "

Es posible que desee utilizar CodeBase en lugar de Ubicación.

dan gibson
fuente
1
@ Kiquenet: tanto código solo para convertir un URI en una ruta. Claro que podría mejorarse. Mira la respuesta de Mike Schall o SoMoS. No debe intentar convertir los URI a nivel de cadena, sino utilizar los objetos adecuados. OK, también es torpe que Assembly.CodeBase devuelve una cadena en lugar de un objeto más adecuado, como URI o FileInfo.
Siete
2

Puede obtener la ruta bin por AppDomain.CurrentDomain.RelativeSearchPath

Añádelo
fuente
2

Todas las respuestas propuestas funcionan cuando el desarrollador puede cambiar el código para incluir el fragmento requerido, pero si desea hacerlo sin cambiar ningún código, puede usar Process Explorer.

Enumerará todos los dlls en ejecución en el sistema, es posible que deba determinar la identificación del proceso de su aplicación en ejecución, pero eso generalmente no es demasiado difícil.

He escrito una descripción completa de cómo hacer esto para un dll dentro de II - http://nodogmablog.bryanhogan.net/2016/09/locating-and-checking-an-executing-dll-on-a-running-web -servidor/

Bryan
fuente
Tenga en cuenta que, en primer lugar, el código en el artículo está bastante centrado en IIS y, en segundo lugar, le brinda (creo) todos los archivos DLL cargados actualmente , no lo que se está ejecutando en cualquier momento.
George Mauer
El ejemplo dado se relaciona con iis, pero se aplican los mismos pasos si el dll se ejecuta en un proceso fuera de iis. Es solo una cuestión de identificar la identificación del proceso. Actualizaré el artículo para tener en cuenta eso. Gracias por la sugerencia.
Bryan
2

en una aplicación de formulario de Windows, simplemente puede usar Application.StartupPath

pero para las DLL y las aplicaciones de consola, el código es mucho más difícil de recordar ...

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

root += slash;
string settingsIni = root + "settings.ini"
Shane J
fuente
1
string path = Path.GetDirectoryName(typeof(DaoTests).Module.FullyQualifiedName);
Jesse C. Slicer
fuente
1

Obtendrá un directorio incorrecto si una ruta contiene el símbolo '#'. Entonces uso una modificación de la respuesta de John Sibly que es la combinación UriBuilder.Path y 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);
     }
}
Fedor Shihantsov
fuente
0

Esto es lo que se me ocurrió. Entre proyectos web, pruebas unitarias (nunit y runner test runner) ; Encontré que esto funcionó para mí.

He estado buscando código para detectar qué configuración es la acumulación en, Debug/Release/CustomName. Por desgracia, el #if DEBUG. Entonces, si alguien puede mejorar eso !

Siéntase libre de editar y mejorar.

Obteniendo la carpeta de la aplicación . Útil para raíces web, pruebas unitarias para obtener la carpeta de archivos de prueba.

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

Cómo obtener la carpeta bin : útil para ejecutar ensamblajes usando la reflexión. Si los archivos se copian allí debido a las propiedades de compilación.

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;
    }
}
Valamas
fuente
0

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 implementar bibliotecas de archivos DLL junto con algún archivo de configuración (esto es para usar log4net desde dentro del archivo DLL).

mmmmmm
fuente
¿Para qué se fileMapusa aquí?
George Mauer
0

Encuentro mi solución adecuada para la recuperación de la ubicación.

var executingAssembly = new FileInfo((Assembly.GetExecutingAssembly().Location)).Directory.FullName;
Tez Wingfield
fuente
Esta ya es una de las respuestas mejor calificadas y se menciona explícitamente en la pregunta como algo que no funciona en esta situación.
George Mauer
Disculpas debe de haber perdido eso! Obviamente no lo leí completamente.
Tez Wingfield
0

Obtuve el mismo comportamiento NUniten el pasado. Por defecto NUnitcopia su ensamblaje en el directorio temporal. Puede cambiar este comportamiento en la NUnitconfiguración:

ingrese la descripción de la imagen aquí

Quizás TestDriven.NETy MbUnitGUI tienen la misma configuración.

Andrey Bushman
fuente
-3

Lo uso para obtener la ruta al directorio Bin:

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

Obtienes este resultado:

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

rcooley56
fuente
66
No veo una razón para evitar Path.getDirectoryName aquí
Max Keller
@MaxKeller Si no ve razones, no significa que sea correcto. Este método alternativo de Path.GetDirectoryName es diez veces más rápido.
Ruslan Veselov
-3

¿Aplicación web?

Server.MapPath("~/MyDir/MyFile.ext")
usuario2009677
fuente
2
@christiandev esta es una respuesta, pero tal vez parece ser una respuesta a la pregunta equivocada. De la pregunta es bastante claro que no se trata de una aplicación web, sino de un ensamblado que se ejecuta con MbUnit. Dicho esto, la respuesta aún no es realmente correcta debido a la copia oculta de Asp.Net (aunque podría ser lo que alguien está buscando en esta pregunta).
George Mauer