Como obtenho o caminho do assembly em que o código está?

782

Existe uma maneira de obter o caminho para o assembly no qual o código atual reside? Eu não quero o caminho do assembly de chamada, apenas aquele que contém o código.

Basicamente, meu teste de unidade precisa ler alguns arquivos de teste xml que estão localizados em relação à dll. Quero que o caminho seja sempre resolvido corretamente, independentemente de a dll de teste ser executada no TestDriven.NET, na GUI do MbUnit ou em qualquer outra coisa.

Edit : As pessoas parecem estar entendendo mal o que estou perguntando.

Minha biblioteca de teste está localizada em digamos

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

e eu gostaria de pegar esse caminho:

C: \ projetos \ myapplication \ daotests \ bin \ Debug \

Até agora, as três sugestões falham quando executo o MbUnit Gui:

  • Environment.CurrentDirectory fornece c: \ Arquivos de programas \ MbUnit

  • System.Reflection.Assembly.GetAssembly(typeof(DaoTests)).Location fornece C: \ Documents and Settings \ george \ Configurações locais \ Temp \ .... \ DaoTests.dll

  • System.Reflection.Assembly.GetExecutingAssembly().Location dá o mesmo que o anterior.

George Mauer
fonte
102
Essa é a sua solução: var dir = AppDomain.CurrentDomain.BaseDirectory;
Jalal El-Shaer
7
Essa deve ser a solução aceita. AppDomain.CurrentDomain.BaseDirectory é a abordagem correta.
aBetterGamer
2
Eu vim aqui procurando uma solução para um pacote nuget para ler um arquivo JSON em seu diretório pacakge. Parece que quando um pacote de nuget é executado, o "AppDomain.CurrentDomain.BaseDirectory" aponta para o diretório de projetos em execução, e não para o diretório do pacote de nuget. Nenhum desses parece direcionar o diretório do pacote de nuget corretamente.
Lucas
@ Lucas, não, porque não era essa a questão (na verdade, quando foi perguntada, a pepita não existia) - sinta-se à vontade para iniciar uma nova pergunta e fazer ping comigo, mas posso dizer agora que é impossível na maioria dos casos. Para a maioria dos projetos, o diretório nuget fica packagesao lado do arquivo sln. Mas quando você compila e distribui coisas, não há arquivo sln nem diretório de pacotes. Durante a compilação, as coisas necessárias (mas não tudo) são copiadas no diretório bin. Sua melhor aposta é usar um script postbuild para copiar o arquivo que você deseja.
George Mauer 29/07

Respostas:

1037

Eu defini a propriedade a seguir, pois usamos isso frequentemente em testes de unidade.

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

A Assembly.Locationpropriedade, por vezes, dá-lhe alguns resultados engraçados quando usando NUnit (onde montagens executado a partir de uma pasta temporária), então eu prefiro usar CodeBaseo que lhe dá o caminho em formato URI, em seguida, UriBuild.UnescapeDataStringremove o File://no início, e GetDirectoryNamemuda-lo para o formato de janelas normais .

John Sibly
fonte
29
Esse é um problema que encontrei, se o nome do diretório for: c: \ My% 20Directory, o Uri.UnescapeDataString retornará: c: \ My Directory Isso significa que File.Exists ("c: \ My Directory \ MyFile.txt ") retornará false, pois o caminho correto é realmente" c: \ My% 20Directory \ MyFile.txt "Me deparei com isso, pois nossos caminhos SVN têm espaços neles e, quando examinamos, codifica os espaços.
row1
5
Tenha cuidado ao usar isso para verificar File.Exist (), pois esse método retornará false no caminho UNC. Use a resposta de @ Keith.
AZ.
3
Não sabia que você poderia colocar estática antes do público. Bom saber e eu acho que eu prefiro para facilitar a leitura
Valamas
5
Nota: isso não funciona com locais de rede (por exemplo, \\ REMOT_EPC \ Folder)
Muxa 27/01
5
Além disso, isso não funcionará se o diretório tiver sinais de número '#'. Os sinais numéricos são permitidos nos nomes de diretório e arquivo no Windows.
Huemac
322

Isso ajuda?

//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
fonte
veja minha edição, não é, isso é algo estranho sobre como o MbUnit faz as coisas?
George Mauer
3
Defina os arquivos xml como conteúdo, copiados com a dll, ou recursos, lidos a partir da dll.
Keith
22
Ou apenastypeof(DaoTests).Assembly
SLaks
4
@SLaks @JohnySkovdal @Keith: Ei pessoal, use Assembly.GetExecutingAssembly(). Ele "recebe o assembly que contém o código que está em execução" (da descrição do método). Eu uso isso no meu AddIn " EntitiesToDTOs ". Consulte AssemblyHelper.cs para obter um exemplo real.
kzfabi
4
Ocorreu um problema com a postagem de @John Silby, pois ela não parece funcionar para caminhos UNC ... por exemplo, \\ Server \ Folder \ File.ext. Este fez o truque. +1
Blueberry
312

É simples assim:

var dir = AppDomain.CurrentDomain.BaseDirectory;
Jalal El-Shaer
fonte
11
Essa deve ser a solução aceita. AppDomain.CurrentDomain.BaseDirectory é a abordagem correta.
aBetterGamer
5
obrigado por trazer minha atenção de volta para isso - não tenho certeza se isso estava disponível no momento em que fiz a pergunta, mas é agora.
precisa
120
Não, isso está errado. Isso retorna o caminho do PONTO DE ENTRADA ORIGINAL e não o código em execução no momento. Se você carregou um assembly manualmente de um caminho diferente ou se ele foi carregado do GAC, ele retornará o resultado errado. Esta resposta está correta: stackoverflow.com/a/283917/243557 Mais rápido ainda é Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location).
Nathanchere
9
Na verdade, isso não vai funcionar em aplicações web, mas tanto quanto eu encontrei o seguinte aumento deve funcionar para qualquer tipo de aplicação:AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory
Ilya Chernomordik
4
Isso é excelente para teste de unidade, se você deseja apenas obter o caminho da bandeja original do seu conjunto de teste (por exemplo, para acessar arquivos de dados auxiliares em subpastas). O conjunto de teste é o ponto de entrada do seu código.
MarioDS
68

O mesmo que a resposta de John, mas um método de extensão um pouco menos detalhado.

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

Agora você pode fazer:

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

ou se você preferir:

var localDir = typeof(DaoTests).Assembly.GetDirectoryPath();
Sneal
fonte
6
Você quis dizer em assemblyvez de Assembly.GetExecutingAssembly()?
Dude Pascalou
3
Como Dude aponta, você passou em uma discussão e não conseguiu usá-la.
Chris Moschini
4
Esta resposta é simplesmente errada para a pergunta em questão. Uma versão modificada desta resposta pode fornecer o caminho de um determinado assembly. No entanto, aqui, estamos procurando especificamente o assembly em execução e, portanto, passar um assembly não faz sentido. Um método de extensão é a ferramenta errada para o trabalho.
Edward Brey 27/05
46

A única solução que funcionou para mim ao usar os compartilhamentos de rede CodeBase e UNC foi:

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

Também funciona com URIs normais.

Ignacio Soler Garcia
fonte
5
Essa deve ser a resposta aceita. É realmente irritante que a base de código padrão não lide com os compartilhamentos UNC corretamente.
Daniel Gilbert
Esta desmorona quando a pasta contém espaços e deus sabe o que outros personagens ...
MarioDS
1
Eu tenho usado muito isso e encontrei um cenário em que falha: se essa linha de código em si faz parte de um pacote NuGet que é usado por um aplicativo! Também podemos apoiar esse cenário substituindo GetExecutingAssembly()por GetCallingAssembly().
Timo
@Timo: você verificou se essa alteração tem efeitos colaterais? Nesse caso, edite a resposta para incluir a correção.
Ignacio Soler Garcia
@IgnacioSolerGarcia Infelizmente, devo relatar que funcionou apenas uma camada, ou seja, falha se o pacote NuGet foi chamado por outro pacote NuGet! Agora estou usando isso (a partir de um comentário sobre esta página, Chernomordik): AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory. A primeira parte é para aplicativos da Web e a segunda para outros aplicativos.
Timo
32

Isso deve funcionar, a menos que a montagem seja copiada pela sombra :

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

Que tal isso:

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

Eu suspeito que o problema real aqui é que o executor de teste está copiando sua montagem para um local diferente. No tempo de execução, não há como dizer de onde a montagem foi copiada, mas você provavelmente pode ativar um botão para informar ao executor de teste que execute a montagem de onde está e não copiá-la para um diretório de sombra.

É provável que essa mudança seja diferente para cada corredor de teste, é claro.

Você já pensou em incorporar seus dados XML como recursos dentro do seu conjunto de teste?

Curt Hagenlocher
fonte
+1 por apontar o problema com a cópia de sombra. No entanto, é realmente possível determinar o local original a partir do Assembly.CodeBase.
TM1
11
AppDomain.CurrentDomain.BaseDirectory

funciona com a interface gráfica do usuário MbUnit.

user368021
fonte
Isso funcionou muito bem para escrever um arquivo relativo ao diretório raiz em um aplicativo asp.net web
Philip Pittle
Eu descobri que este funciona melhor em geral. Escolha se não tiver certeza.
Erik Bergstedt
10

Acredito que isso funcionaria para qualquer tipo de aplicativo:

AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory
Ilya Chernomordik
fonte
1
Minhas experiências mostram que essa é a resposta mais infalível, cobrindo não apenas aplicativos da Web e de console, mas também chamadas de testes de unidade e pacotes NuGet (aninhados a qualquer nível de recursão).
Timo
8

Até onde eu sei, a maioria das outras respostas tem alguns problemas.

A maneira correta de fazer isso para um assembly não baseado em disco (baseado na Web) e não no GAC é usar o assembly atualmente em execuçãoCodeBase propriedade .

Isso retorna um URL ( file://). Em vez de mexer com a manipulação de strings ou UnescapeDataString, isso pode ser convertido com o mínimo de barulho, aproveitando a LocalPathpropriedade de Uri.

var codeBaseUrl = Assembly.GetExecutingAssembly().CodeBase;
var filePathToCodeBase = new Uri(codeBaseUrl).LocalPath;
var directoryPath = Path.GetDirectoryName(filePathToCodeBase);
gastador
fonte
1
Não funciona se caminho contém #( EscapedCodeBaseobras, mas EscapedCodeBase não funciona se o caminho contiver por exemplo %20verbatim (que é uma seqüência permitido personagem em um caminho Windows)
Martin Ba
Se quisermos ter esse código em um pacote NuGet, podemos corrigir esse cenário substituindo GetExecutingAssembly()por GetCallingAssembly().
Timo
8

Que tal agora ...

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

Em seguida, basta cortar o que você não precisa

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

Aqui está uma porta VB.NET do código de John Sibly. O Visual Basic não diferencia maiúsculas de minúsculas; portanto, alguns de seus nomes de variáveis ​​estavam colidindo com nomes de tipos.

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
fonte
6

Em todos esses anos, ninguém mencionou esse. Um truque que aprendi com o incrível projeto ApprovalTests . O truque é que você use as informações de depuração no assembly para encontrar o diretório original.

Isso não funcionará no modo RELEASE, nem com as otimizações ativadas, nem em uma máquina diferente daquela em que foi compilada.

Mas você obterá caminhos relativos ao local do arquivo de código-fonte do qual você o chama

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
fonte
5

O diretório atual em que você existe.

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

Se você copiar o arquivo .xml com o build, deverá encontrá-lo.

ou

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

// The location of the Assembly
assembly.Location;
David Basarab
fonte
isso será problemático se a montagem tiver sido copiada pela sombra .
spender
+1520! Environment.CurrentDirectoryfunciona se você estiver usando o reflexo na classe de tarefa MSBuild, onde o assembly em execução reside no GAC e seu código está em outro lugar.
quer
4
Em geral, o CurrentDirectory não informa onde seus executáveis ​​residem. Não é para isso que é usado. Por acaso, freqüentemente, é o mesmo local em que os executáveis ​​estão, então muitos programadores não entendem a diferença. Em seguida, eles acabam criando problemas para alguns dos usuários finais que esperavam que o aplicativo entendesse o uso adequado do CurrentDirectory.
Bent Tranberg 6/11
5

Estou usando Assembly.CodeBase em vez 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("/", "\\");

Está funcionando, mas não tenho mais certeza de que está 100% correto. A página em http://blogs.msdn.com/suzcook/archive/2003/06/26/assembly-codebase-vs-assembly-location.aspx diz:

"O CodeBase é um URL para o local onde o arquivo foi encontrado, enquanto o Local é o caminho em que ele foi realmente carregado. Por exemplo, se o assembly foi baixado da Internet, seu CodeBase pode começar com" http: // " , mas o local pode começar com "C: \". Se o arquivo foi copiado na sombra, o local seria o caminho para a cópia do arquivo no diretório da cópia de sombra. Também é bom saber que o CodeBase não é garantido no entanto, o local sempre será definido para os assemblies carregados do disco. "

Você pode usar o CodeBase em vez de Local.

dan gibson
fonte
1
@ Kiquenet: Muito código apenas para converter um URI em um caminho. Claro que poderia ser melhorado. Veja a resposta de Mike Schall ou SoMoS. Você não deve tentar converter URIs no nível da string, mas use os objetos adequados. OK, também é desajeitado que Assembly.CodeBase retorne uma string em vez de um objeto mais adequado, como URI ou FileInfo.
Sete
2

Você pode obter o caminho da lixeira por AppDomain.CurrentDomain.RelativeSearchPath

resumir
fonte
2

Todas as respostas propostas funcionam quando o desenvolvedor pode alterar o código para incluir o snippet necessário, mas se você quiser fazer isso sem alterar nenhum código, poderá usar o Process Explorer.

Ele listará todas as dlls em execução no sistema; talvez seja necessário determinar a identificação do processo do aplicativo em execução, mas isso geralmente não é muito difícil.

Eu escrevi uma descrição completa de como fazer isso para uma dll dentro do II - http://nodogmablog.bryanhogan.net/2016/09/locating-and-checking-an-executing-dll-on-a-running-web -servidor/

Bryan
fonte
Observe que, em primeiro lugar, o código do artigo é bastante centrado no IIS e, em segundo lugar, fornece (acredito) todas as DLLs carregadas no momento , não o que está sendo executado a qualquer momento.
George Mauer
O exemplo dado refere-se ao iis, mas as mesmas etapas se aplicam se a dll estiver executando em um processo fora do iis. É apenas uma questão de identificar o ID do processo. Vou atualizar o artigo para observar isso. Obrigado pela sugestão.
Bryan
2

em um aplicativo de formulário do Windows, você pode simplesmente usar Application.StartupPath

mas para DLLs e aplicativos de console o código é muito mais difícil de lembrar ...

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

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

Você receberá um diretório incorreto se um caminho contiver o símbolo '#'. Então, eu uso uma modificação da resposta John Sibly que é a combinação 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);
     }
}
Fedor Shihantsov
fonte
0

Isto é o que eu vim com. Entre os projetos da web, testes de unidade (executor de teste de unidade de nunit e resharper) ; Eu achei que isso funcionou para mim.

Eu tenho procurado por código para detectar em qual configuração está a compilação Debug/Release/CustomName,. Infelizmente, o #if DEBUG. Então, se alguém puder melhorar isso !

Sinta-se livre para editar e melhorar.

Obtendo a pasta do aplicativo . Útil para raízes da web, unittests para obter a pasta de arquivos de teste.

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

Obtendo a pasta bin : Útil para executar montagens usando reflexão. Se os arquivos forem copiados lá devido às propriedades de construção.

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
fonte
0

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

Estou usando isso para implantar bibliotecas de arquivos DLL junto com algum arquivo de configuração (isso é para usar o log4net de dentro do arquivo DLL).

mmmmmm
fonte
Para que é fileMaputilizado aqui?
George Mauer
0

Acho minha solução adequada para a recuperação do local.

var executingAssembly = new FileInfo((Assembly.GetExecutingAssembly().Location)).Directory.FullName;
Tez Wingfield
fonte
Essa já é uma das respostas mais bem avaliadas e é mencionada explicitamente na pergunta como algo que não funciona nessa situação.
George Mauer 23/09
Desculpas deve ter perdido isso! Obviamente eu não li completamente.
Tez Wingfield
0

Eu tive o mesmo comportamento no NUnitpassado. Por padrão, NUnitcopia sua montagem no diretório temp. Você pode alterar esse comportamento nas NUnitconfigurações:

insira a descrição da imagem aqui

Talvez TestDriven.NETe a MbUnitGUI tenha as mesmas configurações.

Andrey Bushman
fonte
-3

Eu uso isso para obter o caminho para o diretório Bin:

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

Você obtém este resultado:

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

rcooley56
fonte
6
Eu não vejo uma razão para evitar Path.getDirectoryName aqui
Max Keller
@ MaxKeller Se você não vê razões, isso não significa que está certo. Esse método alternativo do Path.GetDirectoryName é dez vezes mais rápido.
Ruslan Veselov
-3

Aplicação web?

Server.MapPath("~/MyDir/MyFile.ext")
user2009677
fonte
2
@christiandev, esta é uma resposta, mas talvez pareça ser uma resposta para a pergunta errada. Da pergunta, é bem claro que esse não é um aplicativo da Web, mas um assembly sendo executado com o MbUnit. Dito isto, a resposta ainda não está realmente correta devido à cópia de sombra do Asp.Net (embora possa ser concebivelmente o que alguém que está pesquisando nesta questão está procurando).
George Mauer