Quero carregar em um novo AppDomain
assembly que possui uma árvore de referências complexa (MyDll.dll -> Microsoft.Office.Interop.Excel.dll -> Microsoft.Vbe.Interop.dll -> Office.dll -> stdole.dll)
Pelo que eu entendi, quando um assembly está sendo carregado AppDomain
, suas referências não são carregadas automaticamente, e eu tenho que carregá-las manualmente. Então, quando eu faço:
string dir = @"SomePath"; // different from AppDomain.CurrentDomain.BaseDirectory
string path = System.IO.Path.Combine(dir, "MyDll.dll");
AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
setup.ApplicationBase = dir;
AppDomain domain = AppDomain.CreateDomain("SomeAppDomain", null, setup);
domain.Load(AssemblyName.GetAssemblyName(path));
e obteve FileNotFoundException
:
Não foi possível carregar o arquivo ou assembly 'MyDll, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null' ou uma de suas dependências. O sistema não pode encontrar o arquivo especificado.
Acho que a parte principal é uma de suas dependências .
Ok, eu faço a seguir antes domain.Load(AssemblyName.GetAssemblyName(path));
foreach (AssemblyName refAsmName in Assembly.ReflectionOnlyLoadFrom(path).GetReferencedAssemblies())
{
domain.Load(refAsmName);
}
Mas consegui FileNotFoundException
novamente, em outra montagem (referenciada).
Como carregar todas as referências recursivamente?
Preciso criar uma árvore de referências antes de carregar o assembly raiz? Como obter as referências de um assembly sem carregá-lo?
fonte
Respostas:
Você precisa invocar
CreateInstanceAndUnwrap
antes que seu objeto proxy seja executado no domínio de aplicativo externo.Além disso, observe que, se você usar,
LoadFrom
provavelmente obterá umaFileNotFound
exceção porque o resolvedor de Assembly tentará localizar o assembly que você está carregando no GAC ou na pasta bin do aplicativo atual. UseLoadFile
para carregar um arquivo de montagem arbitrário em vez disso - mas observe que se você fizer isso, você mesmo precisará carregar quaisquer dependências.fonte
AppDomain.CurrentDomain.AssemblyResolve
evento, conforme descrito nesta resposta do MSDN . No meu caso, eu estava tentando me conectar à implantação do SpecRun em execução no MSTest, mas acho que se aplica a muitas situações em que seu código pode não ser executado no AppDomain "primário" - extensões VS,assembly
variável fará referência ao assembly de "MyDomain"? Acho quevar assembly = value.GetAssembly(args[0]);
você carregará seuargs[0]
em ambos os domínios e aassembly
variável fará referência à cópia do domínio principal do aplicativohttp://support.microsoft.com/kb/837908/en-us
Versão C #:
Crie uma classe de moderador e herde-a de
MarshalByRefObject
:chamada do site do cliente
fonte
MarshalByRefObject
pode ser transmitido entre appdomains. Então eu acho queAssembly.LoadFrom
tenta carregar o assembly em um novo appdomain, o que só é possível, se o objeto de chamada puder ser passado entre esses appdomains. Isso também é chamado de remoting, conforme descrito aqui: msdn.microsoft.com/en-us/library/…MarshalByRefObject
não faz com que ele seja carregado magicamente um no outroAppDomain
, apenas informa ao .NET framework para criar um proxy remoto transparente em vez de usar a serialização quando você desembrulhar a referência de umAppDomain
no outroAppDomain
(o método típico é oCreateInstanceAndUnwrap
método). Não posso acreditar que esta resposta tenha mais de 30 votos positivos; o código aqui é apenas uma forma indireta de chamadaAssembly.LoadFrom
.Depois de passar a instância do assembly de volta para o domínio do chamador, o domínio do chamador tentará carregá-la! É por isso que você obtém a exceção. Isso acontece na sua última linha de código:
Portanto, tudo o que você deseja fazer com a montagem, deve ser feito em uma classe proxy - uma classe que herda MarshalByRefObject .
Considere que o domínio do chamador e o novo domínio criado devem ter acesso ao assembly da classe de proxy. Se o seu problema não for muito complicado, considere deixar a pasta ApplicationBase inalterada, para que seja igual à pasta do domínio do chamador (o novo domínio carregará apenas os assemblies necessários).
Em código simples:
Se você precisar carregar os assemblies de uma pasta diferente da pasta de domínio de aplicativo atual, crie o novo domínio de aplicativo com uma pasta de caminho de pesquisa dlls específica.
Por exemplo, a linha de criação de domínio de aplicativo do código acima deve ser substituída por:
Dessa forma, todas as dlls serão resolvidas automaticamente a partir de dllsSearchPath.
fonte
Em seu novo AppDomain, tente definir um manipulador de eventos AssemblyResolve . Esse evento é chamado quando uma dependência está faltando.
fonte
Você precisa manipular os eventos AppDomain.AssemblyResolve ou AppDomain.ReflectionOnlyAssemblyResolve (dependendo de qual carga você está fazendo) no caso de o assembly referenciado não estar no GAC ou no caminho de sondagem do CLR.
AppDomain.AssemblyResolve
AppDomain.ReflectionOnlyAssemblyResolve
fonte
Demorei um pouco para entender a resposta de @ user1996230, então decidi fornecer um exemplo mais explícito. No exemplo a seguir, faço um proxy para um objeto carregado em outro AppDomain e chamo um método nesse objeto de outro domínio.
fonte
A chave é o evento AssemblyResolve gerado pelo AppDomain.
fonte
Eu tive que fazer isso várias vezes e pesquisei muitas soluções diferentes.
A solução que acho mais elegante e fácil de realizar pode ser implementada como tal.
1. Crie um projeto em que você pode criar uma interface simples
a interface conterá assinaturas de todos os membros que você deseja chamar.
É importante manter este projeto limpo e leve. É um projeto que ambos
AppDomain
podem fazer referência e nos permitirá não fazer referência ao domínioAssembly
que desejamos carregar em seprate de nosso assembly cliente.2. Agora crie um projeto que tenha o código que deseja carregar separadamente
AppDomain
.Este projeto, assim como o proj cliente, fará referência ao proj proxy e você implementará a interface.
3. Em seguida, no projeto do cliente, carregue o código em outro
AppDomain
.Então, agora criamos um novo
AppDomain
. Pode especificar o local de base para referências de montagem. A investigação verificará os assemblies dependentes no GAC e no diretório atual e noAppDomain
loc de base.se for necessário, existem várias maneiras diferentes de carregar uma montagem. Você pode usar uma maneira diferente com esta solução. Se você tiver o nome qualificado do assembly, eu gosto de usar o,
CreateInstanceAndUnwrap
pois ele carrega os bytes do assembly e, em seguida, instancia o seu tipo para você e retorna umobject
que você pode converter para o seu tipo de proxy ou, se não, para um código fortemente tipado, pode use o tempo de execução de linguagem dinâmica e atribua o objeto retornado a umadynamic
variável digitada e, em seguida, chame os membros diretamente.Aí está.
Isso permite carregar uma montagem que o projeto do seu cliente não tenha referência em uma
AppDomain
e chamar membros do cliente.Para testar, gosto de usar a janela Módulos no Visual Studio. Ele mostrará seu domínio de assembly do cliente e quais módulos são carregados nesse domínio, bem como seu novo domínio de aplicativo e quais assemblies ou módulos são carregados nesse domínio.
A chave é certificar-se de que o código deriva
MarshalByRefObject
ou é serializável.`MarshalByRefObject permitirá que você configure o tempo de vida do domínio em que está. Por exemplo, digamos que você deseja que o domínio seja destruído se o proxy não for chamado em 20 minutos.
Eu espero que isso ajude.
fonte
Foo, FooAssembly
que tem uma propriedade do tipoBar, BarAssembly
, ou seja, 3 assemblies no total. Continuaria a funcionar?