.net - traductor - sistema binario ejercicios
¿Cómo hacer binarios dll consistentes en todas las versiones de VS? (3)
Por ejemplo, winsock libs funciona muy bien en todas las versiones del estudio visual. Pero estoy teniendo problemas para proporcionar un binario consistente en todas las versiones. El dll compilado con VS 2005 no funcionará cuando esté vinculado a una aplicación escrita en 2008. Actualicé tanto 2k5 como 2k8 a SP1, pero los resultados no han cambiado mucho. Funciona bastante bien. Pero cuando incluyen esto con una aplicación C #, la aplicación C # obtiene errores de violación de acceso, pero con la aplicación clásica C ++ funciona bien.
¿Hay una estrategia que debería saber cuando proporciono dlls?
No estoy de acuerdo con el punto de vista de Chris Becke, al ver las ventajas de su enfoque.
La desventaja es que no puede crear bibliotecas de objetos de utilidad, porque tiene prohibido compartirlos en las bibliotecas.
Expandiendo la solución de Chris
¿Cómo hacer binarios dll consistentes en todas las versiones de VS?
Sus elecciones dependen de cuánto son diferentes los compiladores. En un lado, las diferentes versiones del mismo compilador podrían manejar la alineación de datos de la misma manera, y por lo tanto, podría exponer estructuras y clases en sus archivos DLL. En el otro lado, podría desconfiar de los compiladores de otras bibliotecas o de las opciones de compilación.
En Windows Win32 API, manejaron el problema a través de "manejadores". Usted hace lo mismo por:
1 - Nunca expongas una estructura. Exponer solo punteros (es decir, un puntero void *)
2 - El acceso de esta estructura de datos es a través de funciones tomando el puntero como primer parámetro
3 - los datos de asignación / desasignación de puntero de esta estructura son a través de funciones
De esta forma, puedes evitar recompilar todo cuando cambies tu estructura.
La forma C ++ de hacer esto es el PImpl. Ver http://en.wikipedia.org/wiki/Opaque_pointer
Tiene el mismo comportamiento que el concepto de vacío * anterior, pero con el PImpl, puede usar RAII, encapsulación y obtener ganancias de la seguridad de tipo fuerte. Esto necesitaría una decoración compatible (el mismo compilador), pero no el mismo tiempo de ejecución o versión (si las decoraciones son las mismas entre versiones).
¿Otra solución?
Esperar mezclar archivos DLL de diferentes versiones de compiladores / compiladores es una receta para el desastre (como explicaste en tu pregunta) o tedioso ya que has soltado la mayoría (si no todas) las soluciones de C ++ a tu código para recurrir a la codificación C básica. , o ambos.
Mi solución sería:
1 - Asegúrese de que todos sus módulos estén compilados con el mismo compilador / versión . Período.
2 - Asegúrese de que todos sus módulos estén compilados para vincular dinámicamente con el mismo tiempo de ejecución
3 - Asegúrese de tener una "encapsulación" de todos los módulos de terceros sobre los cuales no tiene control (no puede compilar con su compilador), como Chris Becke explicó correctamente en Cómo hacer binarios dll consistentes en todas las versiones de VS? .
Tenga en cuenta que no es sorprendente ni escandaloso exigir que todos los módulos de su aplicación se compilen contra el mismo compilador y la misma versión del compilador.
No dejes que nadie te diga que mezclar compiladores es algo bueno. No lo es. La libertad de mezclar el compilador es, para la mayoría de las personas, el mismo tipo de libertad que uno puede disfrutar saltando desde la parte superior de un edificio: usted es libre de hacerlo, pero por lo general, simplemente no quiere eso.
Mi solución te permite:
1 - exporte clases y, por lo tanto, cree bibliotecas C ++ reales, no castradas (como se supone que debe hacer con __declspec (dllexport) de Visual C ++, por ejemplo)
2 - transfiere la propiedad de la asignación (lo que sucede sin su consentimiento cuando usa asignación y / o desasignación de código en línea, o STL)
3 - No se moleste con problemas relacionados con el hecho de que cada módulo tiene su propia versión del tiempo de ejecución (es decir, la asignación de memoria y algunos datos globales utilizados por la API C o C ++)
Tenga en cuenta que significa que no debe mezclar la versión de depuración de sus módulos con las versiones de lanzamiento de otros módulos. Su aplicación está completamente depurada o completamente en lanzamiento.
En primer lugar, no pase nada que no sea datos antiguos simples a través de enlaces DLL. es decir, las estructuras están bien. las clases no son En segundo lugar, asegúrese de que la propiedad no se transfiere, es decir, las estructuras pasadas a través de la delimitación dll nunca se desasignan fuera de la dll. Por lo tanto, si exporta una función X * GetX (), existe una función de tipo FreeX (X *) correspondiente que garantiza que el mismo tiempo de ejecución asignado sea responsable de la desasignación.
Siguiente: Obtenga sus DLL para vincular al tiempo de ejecución estático. Reunir un proyecto comprimising dls de varios terceros, cada uno vinculado y esperando diferentes tiempos de ejecución, potencialmente diferentes al tiempo de ejecución esperado por la aplicación, es una molestia, lo que puede forzar al programa instalador a instalar tiempos de ejecución para 7.0, 7.1, 8.0 y 9.0 - varios de los cuales existen en diferentes service packs que pueden o no causar problemas. Sea amable - enlace estáticamente sus proyectos dll.
- Editar: no se puede exportar una clase de C ++ directamente con este enfoque. Compartir definiciones de clases entre módulos significa que DEBE tener un entorno de tiempo de ejecución homogéneo, ya que los diferentes compiladores o versiones de compiladores generarán nombres decorados de forma diferente.
Puede omitir esta restricción exportando su clase en su lugar como una interfaz de estilo COM ... lo que quiere decir que, si bien no puede exportar una clase en tiempo de ejecución de manera independiente, PUEDE exportar una "interfaz", que puede hacer fácilmente declarando una clase que contiene solo funciones virtuales puras ...
struct IExportedMethods {
virtual long __stdcall AMethod(void)=0;
};
// with the win32 macros:
interface IExportedMethods {
STDMETHOD_(long,AMethod)(THIS)PURE;
};
En su definición de clase, hereda de esta interfaz:
class CMyObject: public IExportedMethods { ...
Puede exportar interfaces como esta haciendo los métodos de fábrica de C:
extern "C" __declspec(dllexport) IExportedClass* WINAPI CreateMyExportedObject(){
return new CMyObject;
}
Esta es una forma muy ligera de exportar la versión del compilador y las versiones de clase independientes en tiempo de ejecución. Tenga en cuenta que aún no puede eliminar uno de estos. Debe incluir una función de liberación como miembro de la dll o la interfaz. Como miembro de la interfaz, podría verse así:
interface IExportedMethods {
STDMETHOD_(void) Release(THIS) PURE; };
class CMyObject : public IExportedMethods {
STDMETHODIMP_(void) Release(){
delete this;
}
};
Puede aprovechar esta idea y seguir adelante con ella: herede su interfaz de IUnknown, implemente los métodos AddRef y Release contados, así como la capacidad de QueryInterface para las interfaces v2 u otras características. Y finalmente, use DllCreateClassObject como medio para crear su objeto y obtener el registro COM necesario. Sin embargo, todo esto es opcional, puedes salir fácilmente con una definición de interfaz simple a la que se accede a través de una función C.
con respecto al problema de pasar una estructura, esto es seguro siempre y cuando alinee su estructura como:
#pragma pack(push,4)
typedef myStruct {
int a;
char b;
float c;
}myStruct;
#pragma pack(pop)
Puede poner esta declaración en un archivo de encabezado e incluirla en ambos proyectos. De esta forma no tendrás ningún problema al pasar estructuras.
Además, asegúrese de vincular estáticamente con las bibliotecas de tiempo de ejecución y no intente algo como asignar memoria (ptr = malloc (1024)) en un módulo y luego liberar esa memoria en otro módulo (libre (ptr)).