Pergunta

Problema (simplificado para tornar as coisas mais claras):

    1.existe um static.lib vinculado estaticamente que possui uma função que incrementa:
    
        extern int CallCount = 0;
        int TheFunction()
        {
            void *p = &CallCount;
            printf("Function called");
            return CallCount++;
        }
    
    2.static.lib está vinculado a um gerenciado C++/CLI gerenciado.dll que envolve o método TheFunction:
    
        int Managed::CallLibFunc()
        {
            return TheFunction();
        }
    
    3.O aplicativo de teste tem uma referência a gerenciado.dll e cria vários domínios que chamam o wrapper C++/CLI:
    
        static void Main(string[] args)
        {
            Managed c1 = new Managed();
            int val1 = c1.CallLibFunc();
            // value is zero
    
            AppDomain ad = AppDomain.CreateDomain("NewDomain");
            Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed;
            int val2 = c.CallLibFunc();
            // value is one 
        }
    

Pergunta:

Com base no que li em Essential .NET Vol1 The CLR de Don Box, esperaria que val2 fosse zero, já que uma nova cópia de gerenciado.dll/static.lib é carregada quando CreateInstanceAndUnwrap é chamado.Estou entendendo mal o que está acontecendo?A biblioteca estática não parece respeitar os limites do appdomain, pois é um código não gerenciado.Existe uma maneira de contornar esse problema além da criação de um novo processo para instanciar o Managed?

Muito obrigado a todos!

Foi útil?

Solução

Meu palpite era que, como você suspeitava, as DLLs não gerenciadas são carregadas no contexto do processo e não no contexto do AppDomain, portanto, quaisquer dados estáticos no código não gerenciado são compartilhados entre os AppDomains.

Esse link mostra alguém com o mesmo problema que você, ainda não há 100% de verificação disso, mas provavelmente é esse o caso.

Esse link trata-se de criar um retorno de chamada de código não gerenciado em um AppDomain usando um truque de conversão.Não tenho certeza se isso pode ajudá-lo, mas talvez você ache isso útil para criar algum tipo de solução alternativa.

Outras dicas

Depois de ligar

Managed c1 = new Managed(); 

Seu wrapper gerenciado.dll será carregado no domínio principal do seu aplicativo.Até que chegue lá, as coisas não gerenciadas do domínio de static.lib serão compartilhadas com outros domínios.Em vez de criar um processo separado, você só precisa ter certeza (antes de cada chamada) de que o gerenciado.dll não está carregado em nenhum domínio do aplicativo.

Compare com isso

static void Main(string[] args)
{

    {       
                AppDomain ad = AppDomain.CreateDomain("NewDomain");
                Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed;
                int val2 = c.CallLibFunc();
                //  Value is zero

               AppDomain.Unload(ad)
    }
    {       
                AppDomain ad = AppDomain.CreateDomain("NewDomain");
                Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed;
                int val2 = c.CallLibFunc();
                //  I think value is zero

               AppDomain.Unload(ad)
    }


}
`

IMPORTANTE e:Se você adicionar apenas uma linha, o compilador JIT carregará o gerenciado.dll e a mágica desaparecerá.

static void Main(string[] args)
{

    {       
                AppDomain ad = AppDomain.CreateDomain("NewDomain");
                Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed;
                int val2 = c.CallLibFunc();
                //  Value is zero 

               AppDomain.Unload(ad)
    }
    {       
                AppDomain ad = AppDomain.CreateDomain("NewDomain");
                Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed;
                int val2 = c.CallLibFunc();
                //  I think value is one

               AppDomain.Unload(ad)
    }
Managed c1 = new Managed(); 


}

Se você não quiser depender de tais linhas, você pode criar outro wrapper ManagedIsolated.dll que fará referência a Managed.dll e fará cada chamada em um domínio separado com descarregamento de domínio logo após a chamada.O aplicativo principal dependerá apenas dos tipos ManagedIsolated.dll e o Managed.dll não será carregado no domínio do aplicativo principal.

Parece um truque, mas pode ser útil para alguém.`

Em suma, talvez.AppDomains são puramente um conceito gerenciado.Quando um AppDomain é instanciado, ele não mapeia novas cópias das DLLs subjacentes, ele pode reutilizar o código já na memória (por exemplo, você não esperaria que ele carregasse novas cópias de todos os assemblies System.*, certo ?)

Dentro do mundo gerenciado, todas as variáveis ​​estáticas têm escopo definido por AppDomain, mas como você apontou, isso não se aplica ao mundo não gerenciado.

Você poderia fazer algo complexo que forçasse o carregamento de um gerenciado.dll exclusivo para cada domínio do aplicativo, o que resultaria em uma nova versão da biblioteca estática sendo trazida para o passeio.Por exemplo, talvez usar Assembly.Load com uma matriz de bytes funcionaria, mas não sei como o CLR tentará lidar com a colisão de tipos se o mesmo assembly for carregado duas vezes.

Não creio que estejamos chegando ao verdadeiro problema aqui - veja este artigo do DDJ.

O valor padrão do atributo de otimização do carregador é SingleDomain, que "faz com que o AppDomain carregue uma cópia privada do código de cada assembly necessário".Mesmo que fosse um dos valores de vários domínios, "cada AppDomain sempre mantém uma cópia distinta dos campos estáticos".

'gerenciado.dll' é (como o próprio nome indica) é um assembly gerenciado.O código em static.lib foi compilado estaticamente (como código IL) em 'gerenciado.dll', então eu esperaria o mesmo comportamento que Lenik espera....

...a menos que static.lib seja uma biblioteca de exportação estática para uma DLL não gerenciada.Lenik diz que este não é o caso, então ainda não tenho certeza do que está acontecendo aqui.

Você já tentou executar em processos separados?Uma biblioteca estática não deve compartilhar instâncias de memória fora de seu próprio processo.

Isso pode ser difícil de administrar, eu sei.Não tenho certeza de quais seriam suas outras opções neste caso.

Editar:Depois de dar uma olhada ao redor, acho que você poderia fazer tudo o que precisava com o Sistema.Diagnóstico.Processo aula.Você teria muitas opções neste momento para comunicação, mas Comunicação remota .NET ou WCF provavelmente seriam escolhas boas e fáceis.

Estes são os dois melhores artigos que encontrei sobre o assunto

A parte importante é:

Os campos estáticos baseados em RVA são globais de processo.Eles são restritos a escalares e tipos de valor, porque não queremos permitir que objetos ultrapassem os limites do AppDomain.Isso causaria todos os tipos de problemas, especialmente durante descarregamentos de AppDomain.Algumas linguagens como ILASM e MC++ tornam conveniente a definição de campos estáticos baseados em RVA.A maioria das línguas não.

Ok, então se você controla o código no .lib, eu tentaria

class CallCountHolder {
   public:
     CallCountHolder(int i) : count(i) {}
     int count;
};

static CallCountHolder cc(0);
int TheFunction()
{
    printf("Function called");
    return cc.count++;
}

Já que ele disse que os campos estáticos baseados em RVA são limitados a escalares e tipos de valor.Uma matriz int também pode funcionar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top