![]() |
![]() |
![]() |
![]() |
![]() |
FTP | ![]() |
![]() |
CCD | ![]() |
![]() |
Buscar | ![]() |
![]() |
Trucos | ![]() |
![]() |
Trabajo | ![]() |
![]() |
Foros | ![]() |
|
Registrarse | FAQ | Miembros | Calendario | Guía de estilo | Temas de Hoy |
![]() |
|
Herramientas | Buscar en Tema | Desplegado |
|
#1
|
|||
|
|||
Ayuda con paquetes por favor!!
Hola a todos.
Estoy vuelto un 8 con los paquetes ![]() Voy a intentar explicar de la forma más clara, y lo mejor posible lo que quiero hacer... Espero que me explique bien... Estoy haciendo una aplicación muy grande... y quiero partirla en paquetes. Tengo un DataModule (DMDatosZeos) donde residen todas las tablas, algunos querys y los datasources. Tengo otro DataModule (DMGlobal) donde residen algunos componentes no visuales que uso de forma global al programa, por ejemplo para el aspecto visual de los formularios (efectos en botones y cosas así…). Los mantenimientos de las tablas (listados con filtros, modificaciones, eliminaciones y algunas inserciones) los he programado en un formulario "general" y todos los mantenimientos heredan de éste. Cada formulario de mantenimiento tiene un formulario de edición asociado, y éste también hereda de un formulario de edición "general". Básicamente para “personalizar” un mantenimiento, tengo que decirle qué DataSource es el asociado, y qué campos tiene y el resto es más o menos automático. En el programa tengo un objeto global, llamado _BD, de tipo TBD, que encapsula al DataModule (DMDatosZeos) y algunos métodos para tratado de datos. (No encapsula a DMGlobal… eso es aparte) Hasta aquí todo bien... Espero que todo se entienda. Ahora, lo que quiero hacer es... Hacer un paquete para la base de datos (BD.bpl). Dicho paquete contendría la clase TBD, y el DataModule (DMDatosZeos) que es un campo de la clase. Adicionalmente en la misma unit de TBD, tengo también algunas rutinas para trabajar con datos. Recordemos en este punto que en el programa tengo un objeto “global” _BD de tipo TBD. La pregunta es… Cómo hago para desde el ejecutable “importar” el objeto _BD? En el paquete BD.bpl creo el objeto _BD al cargarlo, osea… hago algo tal que así… Código:
initialization RegisterClass(TBD); _BD := TBD.Create; Y tengo una función BD que me devuelve el objeto en cuestión: Código:
function BD: TBD; begin Result := _BD; end; Esta función está en UBD (donde está definido TBD, y se encuentra en la clausula contains de BD.bpl, osea… está en el paquete BD.bpl). Lo que se me ocurrió fue, cargar el paquete y llamar a esta función que me devuelve el objeto desde el programa de la siguiente manera: Código:
procedure CargarPaqueteBD; type TVarBD = function: TBD; var Lib: String; H: HModule; FuncBD: TVarBD; begin Lib := ExtractFilePath(ParamStr(0)) + 'BD.bpl'; if not FileExists(Lib) then ShowMessage('No existe'); H := LoadPackage(PChar(Lib)); if H <> 0 then begin @FuncBD := GetProcAddress(H, 'BD'); if not Assigned(FuncBD) then ShowMessage('FuncBD NO ASIGNADA!!!'); _BD := FuncBD; end else begin ShowMessage('Error cargando'); end; UnloadPackage(H); end; El problema es que no funciona, siempre me dice “FuncBD NO ASIGNADA!!!”, pero sí logra cargar el paquete correctamente. No se si me expliqué lo suficiente, tal vez confundí en lugar de aclarar… En general, necesito desde el principal recibir el objeto _BD de BD.bpl, y luego enviárselo a todos los paquetes que dependan de la base de datos. Igual pasaría con el modulo de datos Global. Por favor, realmente necesito ayuda con esto… Llevo semanas pensando y probando y todavía no logro dar con la solución… ![]() Muchas gracias de antemano. Un cordial saludo a todos, y disculpen si el post es muy grande. |
#2
|
||||
|
||||
Solo por curiosidad... ¿Te acordaste de exportar la función "BD"? Es decir, fíjate si tienes la siguiente sentencia después de la implementación de la función:
(Te lo comento porque la función de Windows "GetProcAddress" solo carga funciones que han sido "exportadas", ya sea que éstas se encuentren en una .bpl o .dll). Edito: Como había dado una explicación que se me quedó algo liosa (no se si alguien llegó a leerla) prefiero adjuntarte un ejemplo de como podrías hacer lo que quieres de una forma más sencilla. Para ver el ejemplo haz lo siguiente: - Descomprime el contenido del archivo en alguna carpeta del disco duro (por ejemplo: "C:\EjemploBPL") - Y, por último, abre el archivo de proyecto "pgPrueba.bpg" para ver todas las fuentes y la aplicación de ejemplo. (Si quieres ver la aplicación funcionar compila todos los paquetes, del primero al último. Cuando compiles, los paquetes .bpl se almacenarán en la carpeta por defecto de Delphi, que suele ser: "C:\Archivos de programa\Borland\Delphi7\Projects\Bpl"). Para ver una pequeña descripción del ejemplo lee el archivo "Leeme.txt" que encontrarás en la carpeta donde descomprimiste el archivo. Espero que te sirva! Saludos! Última edición por jmariano fecha: 24-08-2005 a las 05:15:22. |
#3
|
|||
|
|||
Gracias, pero sigo teniendo problemas :(
Muchas gracias por tu ayuda.
Efectivamente, me faltaba exportar la función BD, pero sigo teniendo problemas... Te cuento... El DataModule de datos (DMDatosZeos) lo utilizo en: · Los paquetes (pero al ponerlo en la cláusula requires, no hay problema y lo puedo ver). · El programa principal. Esto es lo que me está volviendo loco ![]() Para hacer la asignación de _BD del principal a _BD del paquete he probado con :=, con Assign, incluso haciendome un metodo de asignación que asigne también las otras propiedades como el DataModule, y la conexión, y nada de nada... Depuro mostrando mensajes con las direcciones de los punteros Format('@_BD=%p', [@_BD]) y cosas así, y hay referencias que no me salen iguales en el principal y en el paquete... Extrapolando mi problema a tu código fuente, sería algo así: en mi caso tengo que agregar CPrueba.pas al proyecto prueba.exe pues en ese se utiliza la base de datos, con lo cual tengo 2 objetos _Prueba, uno en el paquete y otro en el programa, el del paquete es el que se crea en realidad, pero el del programa debe apuntar (incluidos todos los campos métodos y todo) al del paquete (es decir debe ser como una copia exacta, pero sin ser copia, pero sin ser copia, pues la base de datos es común). Cómo puedo hacer esto? Tal vez deba distinguir si el código de UBD es el del ejecutable o el del paquete mediante alguna directiva IFDEF (o algo así) pues dicha unidad es compartida a los paquetes y al ejecutable algo así como: Código:
{$IFDEF PROJECT=BD.BPL} ShowMessage('Estoy en el paquete'); {$ELSEIF PROJECT=PRINCIPAL.EXE} ShowMessage('Estoy en el ejecutable'); {$ELSE} ShowMessage('Estoy PERDIDO!!!'); {$IFEND} Muchas gracias por tu ayuda, te lo agradezco de verdad. Y gracias a todos los que se han molestado en leer todo este rollo... ![]() Un cordial saludo a todos. |
#4
|
||||
|
||||
Saludos otra vez!
No entiendo el porqué duplicas ambos objetos (es decir, porqué has de tener el objeto _BD en el principal y en el paquete) si desde el principal puedes usar el objeto _BD del paquete (sin tener que pasar la referencia de un objeto a otro). Seguramente tu problema sea que quieres usar el objeto _BD del paquete desde el mismo archivo de proyecto .dpr. Para hacer esto lo único que tienes que hacer es especificar el unit "UBD" (donde se encuentra declarado el objeto _BD) en la cláusula "uses" del .dpr pero sin añadir "UBD" al proyecto (porque si lo añades, entonces, no usará el del paquete). Siguiendo con el ejemplo que te mandé, el .dpr quedaría así para usar _Prueba desde el principal:
(El ejemplo mostraría el mensaje "Hola" antes de mostrar el formulario principal). Como te comenté antes, no añadas "CPrueba" al proyecto Prueba.exe para que utilice el objeto _Prueba del paquete "BDPrueba". Y acuerdate también que has de añadir, en las opciones del proyecto Prueba.exe (pestaña "Packages", sección "Runtime Packages"), el paquete "BDPrueba" (más el del formulario "FormPrueba", y en realidad cualquier paquete que necesitemos usar). Edito: Me faltó aclarar (por si aún no has entendido bien como va todo este tema de paquetes) que para que un paquete use las clases u objetos definidos en otro paquete tendremos que añadir a su cláusula "Requires" aquellos paquetes donde se encuentren dichas clases u objetos (después, para hacer uso de la clase u objeto, sólo es necerio especificar, en las cláusulas "uses" correspondientes, aquellas units que contengan el objeto). Además, será necesario especificar en las opciones del archivo de proyecto .dpr (pestaña "Packages", sección "Runtime Packages") aquellos paquetes utilizados de forma dinámica. Y para utilizar una clase u objeto desde el mismo .dpr sólo es necesario (al igual que antes) especificar en la cláusula "uses" las units necesarias. Cualquier duda vuelveme a preguntar! Chao! Última edición por jmariano fecha: 24-08-2005 a las 20:21:27. |
#5
|
||||
|
||||
Hola, voy a hablar un poco en el aire porque no he revisado esto con detenimiento.
A mi me parece que hay una confusión de conceptos. Cita:
Al hacer esto (carga estática de paquetes), sigues teniendo la ventaja de la modularidad que proporcionan los paquetes: puedes hacer cambios en el paquete sin tener que recompilar ni redistribuir toda la aplicación porque el paquete como tal no está en el ejecutable, únicamente el enlace a él. Así que, antes de proseguir te conviene preguntarte si no te es suficiente esto. Posiblemente lo sea y ya no has de darle más vueltas al asunto. El uso de paquetes dinámicos- aquellos cargados dinámicamente con LoadPackage -tiene otra finalidad, una ventaja extra sobre los paquetes estáticos. Veamos un ejemplo. Tienes una aplicación que exporta datos a una base. Para ello tienes una rutina en tu aplicación principal que recibe un DataSet:
Supón ahora que la aplicación debe poder exportar los datos bien sea a MySql, Firebird o cualquier otro motor que el cliente desee en un futuro. Para ello debes proporcionar a la rutina de exportación el DataSet adecuado al motor (TZTable, TIBTable, etc...). Claro que lo más fácil sería:
pero esto condena tu aplicación a usar sólo esos dos motores. Para cualquier otro tendrás que agregar la condición correspondiente, recompilar y redistribuir toda la aplicación. Claro que puedes obtener el DataSet de un paquete; pero si el enlace es estático el cliente tendría que cambiar uno por otro cada vez y reiniciar la aplicación. Aquí es donde la carga dinámica adquiere sentido. El cliente proporciona, desde la aplicación, el nombre del paquete, lo cargas con LoadPackage y obtienes el DataSet que proporcione ese paquete. Si una necesidad similar es la que realmente requieres entonces sí tendrás que estudiar como obtener el objeto BD del cuál hablas. Regresando al principio; incluir la unidad en tu aplicación no sirve porque fuerzas un enlace estático. La solución Cita:
Para usar paquetes dinámicos- evitando el enlace estático, tu aplicación no puede contener ninguna referencia al paquete, ni en la lista de run time packages ni en las cláusulas uses. Si no hay ninguna referencia entonces ¿cómo puedes usar los objetos de los paquetes? Una posible solución es mediante el uso de clases abstractas. Defines una clase base que declare los métodos que requiera tu aplicación pero que no los implemente:
Esta clase la colocas o bien directamente en tu aplicación o bien en un paquete enlazado estáticamente. Con esto tu aplicación compila sin problemas y al ejecutable sólo le estás agregando, por así decirlo, la definición de los métodos. La implementación real de la clase (una clase descendiente) la colocarías en un paquete que, éste sí, cargarás dinámicamente:
Como ves, el paquete también enlaza estáticamente la clase base. Pero esto no es una carga ya ésta clase sólo define pero no implementa. Nota que la clase descendiente TDBZeos ni siquiera tiene que estar en la sección interface. En tu aplicación principal cargas el paquete con LoadPackage tal como lo has hecho y obtienes la dirección de la función CrearObjetoDB que exporta el paquete:
Como la aplicación sí usa la unidad DB_Base que define la clase base, el compilador no protesta ni en la declaración de la variable BD ni en la llamada al método HazAlgo. Pero el polimorfismo hace que el objeto real sea de tipo TDBZeos. En cualquier momento puedes programar otro paquete para otro motor de datos y tu aplicación estará lista para usarlo tan sólo especificando el nombre del paquete. Nota además, que ni siquiera se requiere el uso de RegisterClass ni GetClass con lo cual no estás restringido a usar descendientes de TPersistent. Espero no estar equivocado en los conceptos y que esto te aclare las cosas. // Saludos |
#6
|
||||
|
||||
Cita:
Tened en cuenta que cuando se compila un paquete se genera, a parte del .bpl, un archivo de extensión .dcp (que equivaldría a los .dcu de las unit). Este archivo es el que especificamos en la cláusula "Requires" de los paquetes y en la lista "Runtime packages" del archivo de proyecto, y realizar esto sirve precisamente para indicar a la aplicación que paquetes han de ser cargargados dinámicamente (y no enlazarlos estáticamente), evitándonos así todo el proceso que representa la carga manual de paquetes. (Para verlo más fácilmente, imaginemos que cada vez que especificamos un archivo .dcp se está añadiendo a una "lista", dentro del ejecutable o .bpl, aquellos paquetes que han de ser cargados dinámicamente). Por último, aclarar nuevamente que es necesario especificar en la lista "Runtime packages" del archivo de proyecto aquellos paquetes que se desean usar de forma dinámica (porque sino, entonces, sí que se enlazaran estáticamente). (Esta es una de las razones del porqué los ejecutables son tan grandes en Delphi, y cuando hacemos uso de los paquetes de ejecución se "encogen"). Última edición por jmariano fecha: 25-08-2005 a las 14:51:13. |
![]() |
|
|
![]() |
|