Pregunta

Problema (simplificado para aclarar las cosas):

    1.hay un static.lib vinculado estáticamente que tiene una función que incrementa:
    
        extern int CallCount = 0;
        int TheFunction()
        {
            void *p = &CallCount;
            printf("Function called");
            return CallCount++;
        }
    
    2.static.lib está vinculado a un C++/CLI administrado.dll administrado que envuelve el método TheFunction:
    
        int Managed::CallLibFunc()
        {
            return TheFunction();
        }
    
    3.La aplicación de prueba tiene una referencia a Managed.dll y crea múltiples dominios que llaman al contenedor 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 
        }
    

Pregunta:

Según lo que he leído en Essential .NET Vol1 The CLR de Don Box, esperaría que val2 fuera cero ya que se carga una copia nueva de Managed.dll/static.lib cuando se llama a CreateInstanceAndUnwrap.¿Estoy entendiendo mal lo que está pasando?La biblioteca estática no parece respetar los límites del dominio de aplicación ya que es código no administrado.¿Existe alguna forma de solucionar este problema que no sea crear un proceso completamente nuevo para crear instancias de Managed?

¡Muchas gracias a todos!

¿Fue útil?

Solución

Mi corazonada fue que, como sospechaba, las DLL no administradas se cargan en el contexto del proceso y no en el contexto del dominio de aplicación, por lo que cualquier dato estático en el código no administrado se comparte entre los dominios de aplicación.

Este enlace muestra a alguien con el mismo problema que usted, todavía no se ha verificado al 100%, pero probablemente este sea el caso.

Este enlace se trata de crear una devolución de llamada desde código no administrado a un dominio de aplicación mediante un truco de procesamiento.No estoy seguro de que esto pueda ayudarle, pero tal vez le resulte útil para crear algún tipo de solución alternativa.

Otros consejos

después de llamar

Managed c1 = new Managed(); 

Su contenedor administrado.dll se cargará en el dominio de aplicación principal de su aplicación.Hasta que esté allí, las cosas no administradas del dominio de static.lib se compartirán con otros dominios.En lugar de crear un proceso separado, solo necesita asegurarse (antes de cada llamada) de que administrado.dll no esté cargado en ningún dominio de aplicación.

comparar con eso

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 y:Si agrega solo una línea, el compilador JIT cargará Managed.dll y la magia 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(); 


}

Si no desea depender de dichas líneas, puede crear otro contenedor ManagedIsolated.dll que hará referencia a Managed.dll y realizará cada llamada en un dominio separado con la descarga del dominio justo después de la llamada.La aplicación principal dependerá únicamente de los tipos de ManagedIsolated.dll y Managed.dll no se cargará en el dominio de la aplicación principal.

Parece un truco, pero puede que le resulte útil a alguien.`

En resumen, tal vez.Los AppDomains son puramente un concepto administrado.Cuando se crea una instancia de un AppDomain, no se asigna nuevas copias de las DLL subyacentes, puede reutilizar el código que ya está en la memoria (por ejemplo, no esperaría que cargue nuevas copias de todos los ensamblados System.*, ¿verdad? ?)

Dentro del mundo administrado, todas las variables estáticas tienen como alcance el dominio de aplicación, pero como usted señala, esto no se aplica en el mundo no administrado.

Podría hacer algo complejo que fuerce la carga de un archivo Managed.dll único para cada dominio de aplicación, lo que resultaría en una nueva versión de la biblioteca estática.Por ejemplo, tal vez usar Assembly.Load con una matriz de bytes funcionaría, pero no sé cómo intentará CLR lidiar con la colisión de tipos si el mismo ensamblado se carga dos veces.

No creo que estemos llegando al problema real aquí... ver este artículo de DDJ.

El valor predeterminado del atributo de optimización del cargador es SingleDomain, lo que "hace que AppDomain cargue una copia privada de cada código ensamblador necesario".Incluso si fuera uno de los valores de dominio múltiple, "cada dominio de aplicación siempre mantiene una copia distinta de los campos estáticos".

'managed.dll' es (como su nombre lo indica) un ensamblado administrado.El código en static.lib ha sido compilado estáticamente (como código IL) en 'managed.dll', por lo que esperaría el mismo comportamiento que espera Lenik...

...a menos que static.lib sea una biblioteca de exportación estática para una DLL no administrada.Lenik dice que este no es el caso, así que todavía no estoy seguro de qué está pasando aquí.

¿Has intentado ejecutar en procesos separados?Una biblioteca estática no debería compartir instancias de memoria fuera de su propio proceso.

Esto puede ser complicado de manejar, lo sé.Sin embargo, no estoy seguro de cuáles serían sus otras opciones en este caso.

Editar:Después de mirar un poco, creo que podrías hacer todo lo necesario con el Proceso.de.diagnóstico.del.sistema clase.Tendrías muchas opciones en este punto para comunicarte, pero .NET remoto o WCF probablemente serían opciones buenas y fáciles.

Estos son los dos mejores artículos que encontré sobre el tema.

La parte importante es:

Los campos estáticos basados ​​en RVA son procesos globales.Estos están restringidos a escalares y tipos de valores, porque no queremos permitir que los objetos traspasen los límites del AppDomain.Eso causaría todo tipo de problemas, especialmente durante las descargas de AppDomain.Algunos lenguajes como ILASM y MC++ hacen que sea conveniente definir campos estáticos basados ​​en RVA.La mayoría de los idiomas no lo hacen.

Ok, entonces si controlas el código en .lib, lo intentaría

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

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

Ya que dijo que los campos estáticos basados ​​en RVA están limitados a escalares y tipos de valores.Una matriz int también podría funcionar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top