2008-12-12 16 views
8

Tengo una dependencia circular entre dos funciones. Me gustaría que cada una de estas funciones residan en su propia dll. ¿Es posible construir esto con Visual Studio?dependencias circulares entre dlls con visual studio

foo(int i) 
{ 
    if (i > 0) 
     bar(i -i); 
} 

-> debería compilar en foo.dll

bar(int i) 
{ 
    if (i > 0) 
     foo(i - i); 
} 

-> debería compilar en bar.dll

He creado dos proyectos en Visual Studio, uno para foo y uno para la barra . Al jugar con las 'Referencias' y compilar algunas veces, logré obtener el dll que quiero. Sin embargo, me gustaría saber si Visual Studio ofrece una forma de hacerlo de una manera limpia.

Si cambia foo, la barra no necesita ser recompilada, porque solo dependo de la firma de la barra, no de la implementación de la barra. Si ambos dll tienen presente la lib, puedo recompilar la nueva funcionalidad en cualquiera de los dos y todo el sistema aún funciona.

La razón por la que intento esto es porque tengo un sistema heredado con dependencias circulares, que actualmente está vinculado estáticamente. Queremos avanzar hacia dll por varias razones. No queremos esperar hasta que limpiemos todas las dependencias circulares. Estaba pensando en soluciones y probé algunas cosas con gcc en Linux y allí es posible hacer lo que sugiero. De modo que puede tener dos bibliotecas compartidas que dependen unas de otras y pueden construirse independientemente una de la otra.

Sé que las dependencias circulares no son buenas, pero esa no es la discusión que quiero tener.

+0

Pregunta muy interesante. Sé que algo puede gustar esto es posible, pero realmente no sé cómo, creo que implica algunos parámetros especiales de línea de comandos para el enlazador de UNO de los dll y el otro dll debería tener una dependencia simple. – Paulius

+0

@PM: si ese es el caso, entonces es el tipo de problema que será olvidado constantemente, mal configurado, etc. – annakata

Respuesta

0

No es posible hacerlo limpiamente. Como ambos dependen el uno del otro, si A cambia, entonces B debe recompilarse. Debido a que B fue recompilado, ha cambiado y A necesita ser recompilado, y así sucesivamente.

Esa es una de las razones por las que las dependencias circulares son malas y, desee o no, no puede dejar eso fuera de discusión.

+0

Si A cambia, B no necesita ser recompilado, porque solo dependo de la firma de A, no en la implementación de A. Si ambos dll tienen presente la lib, puedo recompilar nueva funcionalidad en cualquiera de los dos y todo el sistema aún funciona. –

+0

Ha mencionado trabajar con Referencias, así que asumí que estaba trabajando con código administrado. El código administrado no usa los encabezados de otros proyectos y funciona de esta manera. –

7

profundamente simpatizo con su situación (como se aclara por tu edición), sino como un firme creyente en hacer lo correcto, no es lo que funciona por ahora, si hay alguna posibilidad en absoluto creo que es necesario para refactor estos proyectos.

Solucione el problema, no el síntoma.

1

¿Qué tal esto:

Proyecto A

Public Class A Implementa C.IA

Public Function foo(ByVal value As C.IB) As Integer Implements C.IA.foo 
    Return value.bar(Me) 
End Function 

End Class

Proyecto B

Clase pública B Implementos C.IB

Public Function bar(ByVal value As C.IA) As Integer Implements C.IB.bar 
    Return value.foo(Me) 
End Function 

End Class

Proyecto C

Public Interface IA 
    Function foo(ByVal value As IB) As Integer 
End Interface 

Public Interface IB 
    Function bar(ByVal value As IA) As Integer 
End Interface 

Proyecto D

Sub Main() 

    Dim a As New A.A 
    Dim b As New B.B 

    a.foo(b) 

End Sub 
+0

Gracias por las respuestas rápidas. Esto funcionaría, pero no es lo que quiero hacer. Estoy buscando el truco de compilación que me permite hacer de una manera limpia, lo que hice al compilar algunas veces y jugar con las dependencias. –

0

Visual Studio hará cumplir las dependencias, en general, ya que las direcciones de función puede cambiar dentro de la DLL compilada recientemente. Aunque la firma puede ser la misma, la dirección expuesta puede cambiar.

Sin embargo, si observa que Visual Studio normalmente logra mantener las mismas direcciones de función entre compilaciones, entonces puede usar una de las configuraciones de compilación "Project Only" (ignora las dependencias). Si haces eso y obtienes un error acerca de no poder cargar la DLL de dependencia, entonces simplemente reconstruye ambos.

11

La razón por la que funciona en sistemas de tipo Unix se debe a que realizan la resolución de enlace real en el momento de la carga. Una biblioteca compartida no sabe de dónde vendrá la definición de su función hasta que se cargue en un proceso. La desventaja de esto es que tampoco lo sabes. Una biblioteca puede encontrar y llamar funciones en cualquier otra biblioteca (o incluso el binario principal que lanzó el proceso en primer lugar). También por defecto todo lo que se encuentra en una biblioteca compartida se exporta.

Windows no funciona así en absoluto. Solo se exportan elementos exportados explícitamente, y todas las importaciones deben resolverse en el tiempo de enlace de la biblioteca, en cuyo punto se ha determinado la identidad de la DLL que suministrará cada función importada. Esto requiere una biblioteca de importación para enlazar.

Sin embargo, puede (con algo de trabajo adicional) solucionar esto. Use LoadLibrary para abrir cualquier DLL que desee, y luego use GetProcAddress para localizar las funciones que desea llamar. De esta manera, no hay restricciones. Pero las restricciones en el método normal están ahí por una razón.

Como desea pasar de bibliotecas estáticas a DLL, parece que supone que debe convertir cada biblioteca estática en una DLL. Esa no es tu única opción. ¿Por qué no comenzar a mover el código a las DLL solo cuando lo identifica como un módulo autónomo que encaja en un diseño en capas sin circularidad? De esa forma, puede comenzar el proceso ahora pero aún así atacarlo a la vez.

+0

Creo que la opción "Delay-Loaded" en Visual Studio resuelve la pregunta de OP también, de la misma manera que Daniel escribió aquí. – TCS

0

Necesita desacoplar las dos DLL, colocando las interfaces y la implementación en dos DLL diferentes, y luego usando el enlace tardío para instanciar la clase.


// IFoo.cs: (build IFoo.dll) 
    interface IFoo { 
     void foo(int i); 
    } 

    public class FooFactory { 
     public static IFoo CreateInstance() 
     { 
     return (IFoo)Activator.CreateInstance("Foo", "foo").Unwrap(); 
     } 
    } 

// IBar.cs: (build IBar.dll) 
    interface IBar { 
     void bar(int i); 
    } 

    public class BarFactory { 
     public static IBar CreateInstance() 
     { 
     return (IBar)Activator.CreateInstance("Bar", "bar").Unwrap(); 
     } 
    } 

// foo.cs: (build Foo.dll, references IFoo.dll and IBar.dll) 
    public class Foo : IFoo { 
     void foo(int i) { 
     IBar objBar = BarFactory.CreateInstance(); 
     if (i > 0) objBar.bar(i -i); 
     } 
    } 

// bar.cs: (build Bar.dll, references IBar.dll and IFoo.dll) 
    public class Bar : IBar { 
     void bar(int i) { 
     IFoo objFoo = FooFactory.CreateInstance(); 
     if (i > 0) objFoo.foo(i -i); 
     } 
    } 

Las clases "de fábrica" ​​no son técnicamente necesarias, pero es mucho más agradable que decir:

IFoo objFoo = FooFactory.CreateInstance(); 

en el código de aplicación que:

IFoo objFoo = (IFoo)Activator.CreateInstance("Foo", "foo").Unwrap(); 

debido a las siguientes razones:

  1. Usted puede evitar un "molde" en el código de aplicación, que es una buena cosa
  2. Si la DLL que aloja los cambios de clase, no tiene que cambiar todos los clientes, solo la fábrica.
  3. Finalización del código todavía se ejecuta.
  4. Dependiendo de sus necesidades, debe llamar a los DLL con reconocimiento de cultura o con clave de acceso, en cuyo caso puede agregar más parámetros a la llamada CreateInstance en la fábrica en un solo lugar.

- Kenneth Kasajian

0

La única manera de que obtendrá alrededor de esta "limpia" (y utilizo el término vagamente) será la de eliminar una de las dependencias estáticas /-tiempo de enlace y el cambio a una dependencia de tiempo de ejecución.

Tal vez algo como esto:

// foo.h 
#if defined(COMPILING_BAR_DLL) 
inline void foo(int x) 
{ 
    HMODULE hm = LoadLibrary(_T("foo.dll"); 
    typedef void (*PFOO)(int); 
    PFOO pfoo = (PFOO)GetProcAddress(hm, "foo"); 
    pfoo(x); // call the function! 
    FreeLibrary(hm); 
} 
#else 
extern "C" { 
__declspec(dllexport) void foo(int); 
} 
#endif 

Foo.dll exportará la función. Bar.dll ya no intenta importar la función; en cambio, resuelve la dirección de la función en tiempo de ejecución.

Implementa tu propio manejo de errores y mejoras de rendimiento.

+0

Ugh. Ahora haces que el círculo sea invisible para las herramientas de análisis y otros programadores, ocultando el problema en lugar de resolverlo. – reinierpost

3

Es posible usar la utilidad LIB con archivos .EXP para "arrancar" (compilar sin archivos .LIB anteriores) un conjunto de archivos DLL con una referencia circular como esta. Ver MSDN article para más detalles.

Estoy de acuerdo con otras personas anteriores que este tipo de situación se debe evitar mediante la revisión del diseño.

Cuestiones relacionadas