 |
Club Builder Foro de C++Builder en español
|
| Ver tema anterior :: Ver tema siguiente |
| Autor |
Mensaje |
elyeyo1000 Cdt

Registrado: 20 Dic 2006Mensajes: 17
|
Publicado: Mar 17 May, 2011 4:25 pm Asunto: DLLs congeladas |
|
|
Hola a todos,
Tengo un pequeño problema con una aplicación que funciona mediante DLLs. Esta aplicación utiliza la red para conectarse con un MSSQL, y actualmente la utilizan unos 30 usuarios. Llevo muchos meses buscando sin exito la solución al problema que voy a intentar detallar aquí.
La estructura de la aplicación es la siguiente:
Una aplicación principal en la que se inicializa un Módulo de datos estableciendo el ConnectionString del TADOConnection, cuyos datos introduce el usuario mediante el formulario principal. Hecho esto, se inicializan las variables y los datos de inicio, etc...
El siguiente paso es llamar a la primera DLL, que será una u otra dependiendo de algunos datos leídos de la base de datos correspondientes al estado del usuario actual.
Después de cargar la primera DLL, el formulario de la aplicación principal se queda en modo Visible=false hasta que se sale completamente de todas las DLLs.
Así, cuando una DLL llama a otra, esta también queda en Visible=false hasta que se le devuelve el control, y así sucesivamente, pudiendo llamar incluso a 5 o 6 DLL recursivamente.
EL PROBLEMA:
Si has llegado hasta aquí, te doy las gracias. Ahora quiero que entiendas el problema. Resulta que en la transición de una DLL a otra, e incluso, en ocasiones, del formulario principal a la primera DLL, lo que hago es un Visible=false del formualrio (o DLL) que realiza la llamada y un ShowModal() del formulario (o DLL) que se muestra. Pues bien, en ocasiones todo transcurre correctamente, mientras que otras veces, la DLL que se debe mostrar se queda congelada en segundo plano, hasta que a los 5 o 6 minutos aparece como si no hubiera pasado nada. Dicho sea de paso, que el formulario o la DLL que realiza la llamada ya se ha puesto en Visible=false.
Este problema es aleatorio, es decir, a veces pasa en una transición, y otras veces pasa en otra, o incluso a veces, ni siquiera ocurre, pero estaréis conmigo en que es algo molesto al no tener solución.
He comprobado la carga del MSSQL y cuando se congela la aplicación, no existen consultas ni procesos activos que estén bloqueando la base de datos. También cabe decir que todas las aplicaciones corren en WindowsXP.
En fin, para no seguir mareando la perdiz, pondré el código que utilizo para las llamadas a las DLL. Todas las llamadas son iguales:
Aplicación principal:
====================
| Código: |
...
#include <vcl>
#pragma hdrstop
#include "Login.h"
#include "ModDatos.h"
...
//---------------------------------------------------------------------------
//Declaro la funcion de la DLL
typedef int __stdcall (*MenuPrincipal)(TADOConnection *var1, AnsiString Usuario);
//---------------------------------------------------------------------------
void __fastcall TFLogin::FormShow(TObject *Sender)
{
AnsiString Usuario = "yomismo";
...
//LLAMADA A LA DLL
int niRet;
HINSTANCE hinstDLL = LoadLibrary("MenuPrincipal.dll");
if(!hinstDLL){ShowMessage("ERROR. No se pudo encontrar el archivo MenuPrincipal.dll"); Close(); return;}
MenuPrincipal menuPpal = reinterpret_cast<MenuPrincipal>(GetProcAddress(hinstDLL, "LoadForm"));
this->Visible = false;
iRet = menuPpal(MDATOS->ADOConnection, Usuario);
this->Visible = true;
FreeLibrary(hinstDLL);
if(iRet!=0) ShowMessage("ERROR. La aplicación no se ha cerrado correctamente.");
//FIN LLAMADA A LA DLL
}
|
MenuPrincipal.dll: (Archivo .cpp)
=======================
| Código: |
...
//---------------------------------------------------------------------------
// Definimos la función que vamos a exportar
extern "C" __declspec(dllexport) __stdcall int LoadForm(TADOConnection *var1);
//---------------------------------------------------------------------------
// DLL entry function (called on load, unload, ...)
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
return TRUE;
}
//---------------------------------------------------------------------------
int __stdcall LoadForm(TADOConnection *ADOAux)
{
TFInicio *FInicio;
TMDATOS *MDATOSIni;
try{
CoInitialize(NULL);
// Inicializa el modulo de datos
MDATOSIni = new TMDATOS(NULL);
MDATOSIni->ADOCon = ADOAux;
//Muestra el formulario principal de la DLL
FInicio = new TFInicio(NULL);
FInicio->MDATOS = MDATOSIni;
FInicio->ShowModal();
delete FInicio;
delete MDATOSIni;
return 1;
}catch(...){
ShowMessage("ERROR. La DLL se cerró inesperadamente.");
if(FInicio) delete FInicio;
if(MDATOSIni) delete MDATOSIni;
return 0;
}
}
|
Dentro de la misma DLL, esta el formulario FInicio que es el que se muestra:
Inicio.h
===========
| Código: |
#ifndef InicioH
#define InicioH
//---------------------------------------------------------------------------
#include "ModDatos.h"
//---------------------------------------------------------------------------
...
__published:
...
public:
...
public: // User declarations
__fastcall TFInicio(TComponent* Owner);
HINSTANCE hinstDLL;
TMDATOS *MDATOS;
|
Inicio.cpp
============
| Código: |
#include <vcl>
#pragma hdrstop
#include "Inicio.h"
#include "ModDatos.h"
//---------------------------------------------------------------------------
typedef int __stdcall (*GetFecha)(AnsiString *Fecha, AnsiString *Hora, int tipo);
//---------------------------------------------------------------------------
__fastcall TFInicio::TFInicio(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcallTFInicio::FormShow(TObject *Sender)
{
...
}
//---------------------------------------------------------------------------
void __fastcall TFInicio::BFlota_DClick(TObject *Sender)
{
//Esto es otra llamada a una DLL externa, que como vereis es igual a las anteriores
int iRetFecha;
AnsiString Fecha="";
AnsiString Hora="";
// Obtener fecha
hinstDLL = LoadLibrary("Fecha.dll");
if(!hinstDLL){ShowMessage("ERROR. No se pudo encontrar el archivo Fecha.dll"); return;}
GetFecha fec = reinterpret_cast<GetFecha>(GetProcAddress(hinstDLL, "LoadForm"));
iRetFecha = fec(&Fecha, &Hora, 1); //tipo=1 -> Obtener fecha y hora
FreeLibrary(hinstDLL);
if(iRetFecha!=1) return;
}
|
Bueno, pues eso es todo, espero haberlo dejado claro, ya que como digo, llevo meses con este problema y no he sido capaz de solucionarlo. Aún no he probado a utilizar la llamada estática utilizando el .lib, ya que todas las DLL son mías y puedo generarlo sin problemas, pero esto ya se ha convertido en algo personal y necesito solucionarlo cuanto antes para conseguir dormir por las noches.
Cualquier sugerencia será bien recibida. Así como utilizar una forma más elegante para inicializar el módulo de datos en las DLLs externas sin tener que declararlo tanto en el .h como en el .cpp
Saludos. |
|
| Volver arriba |
|
 |
elyeyo1000 Cdt

Registrado: 20 Dic 2006Mensajes: 17
|
Publicado: Lun 23 May, 2011 11:35 am Asunto: |
|
|
¿No le ha pasado esto a nadie?
Empiezo a pensar que sea un problema del Builder al gestionar la memoria de las DLLs, pero si a nadie más le ha pasado, casi que tendré que enfocarme a que soy yo y los 30 usuarios que la utilizan.
Estoy descolocado con esto.
Saludos. |
|
| Volver arriba |
|
 |
elyeyo1000 Cdt

Registrado: 20 Dic 2006Mensajes: 17
|
Publicado: Lun 23 May, 2011 10:04 pm Asunto: |
|
|
En fin, en vista del exito, me respondere yo mismo... o mas bien, haré un pensamiento para mis adentros
He leido cientos de formas para llamar a las funciones de las DLLs, de forma estática, de forma dinámica, etc.
El caso es que usando unas u otras, nunca había probado a añadir los .lib al proyecto, y de momento parece que esta opción está funcionando. El problema es que como este error de acabar la DLL congelada nunca tiene el mismo momento ni el mismo formulario, no se si es coincidencia o es que realmente ese era el problema y he conseguido dar con la solución.
En resumen, como dije antes, y en vista del exito, dejare morir el hilo.
Saludos a todos. |
|
| Volver arriba |
|
 |
_Leo Site Admin

Registrado: 11 Ago 2002Mensajes: 1875Ubicación: Lanzarote - Islas Canarias (España)
|
Publicado: Lun 23 May, 2011 11:02 pm Asunto: |
|
|
Hombre, yo creo que más que nada no te responden porque quizás no hayan tenido tu mismo problema, o en mi caso por ejemplo porque a veces estoy muy perezoso o un poco mas ocupado para ponerme a mirar código que necesite un poco más de atención/trabajo, etc..
A veces con este tipo de cosas aleatorias es más comodo proporcionar un pequeño proyecto que reproduzca el problema y subirlo en algún sitio para probarlo y no tener que trabajar en preparar el proyecto y copiar código y quitar las dependencias etc.. solo para hacer alguna comprobación. Con esto me refiero a que si consigues reproducir el problema sin usar MSSQL etc.. mejor aún, porque ahorras a los demás tener que estarse instalando un motor de base de datos que quizás no utiliza..
Pero la experiencia me dice que los problemas aleatorios casi siempre están relacionados con recursos que se quedan sin liberar. O se sigue usando después de liberarse. Mi consejo en este sentido es que trates de utilizar punteros “inteligentes” donde te sea posible, por ejemplo usar “auto_ptr” de la librería estándar, etc.
Lo difícil de este tipo de dudas es que el fallo quizás ni siquiera este en como usas las DLLs sino en cualquier otro recurso.
A veces mucha gente supone que son fallos del compilador o las librerías de Embarcadero, pero esto hay que pensarlo en el último caso, ya que casi nunca es así. Hay fallos en sus librerías (como en casi todas las de otros fabricantes), pero generalmente no están en las cosas de uso más común, sino veríamos un millón de quejas.
O vemos muchas veces quejas que en realidad no es más que el desconocimiento de algún cambio en las librerías, que sobretodo producen dolores de cabeza al pasar un proyecto de una versión a otra, pero no son errores, son cambios. |
|
| Volver arriba |
|
 |
elyeyo1000 Cdt

Registrado: 20 Dic 2006Mensajes: 17
|
Publicado: Lun 23 May, 2011 11:44 pm Asunto: |
|
|
Primero que nada, agradecerte el tiempo empleado en responder. En serio, espero que no me malentiendan con mi anterior mensaje, tengo mucha experiencia en comunidades como esta (a decir verdad, hay pocas como esta ) y se lo difícil que es encontrar respuestas a dudas tan poco específicas... es sólo que al lanzar la pregunta, ya me estaba temiendo la dificultad de la situación.
Igualmente, como digo, te doy las gracias por responder e incluso a los demás por haber leido. Probaré con la opción que me has dicho, y en caso de volver a ver fallos, intentaré subir un proyecto totalmente funcional sin conexión a base de datos.
Saludos a todos, y de nuevo, pido disculpas si se ha entendido un tono ofendido o algo parecido. |
|
| Volver arriba |
|
 |
_Leo Site Admin

Registrado: 11 Ago 2002Mensajes: 1875Ubicación: Lanzarote - Islas Canarias (España)
|
Publicado: Lun 23 May, 2011 11:53 pm Asunto: |
|
|
| elyeyo1000 escribió: | | pido disculpas si se ha entendido un tono ofendido o algo parecido |
No lo entendí así, sino en un tono un poco resignado, de todas formas es bueno conocer la evolución del problema, y por lo menos contabas que era lo que ibas haciendo.
Quizás mi respuesta suena un poco gruñona, pero no iba en ese tono aunque con los años me vuelvo cada día más gruñón y menos paciente.
Yo si te fijas en mis respuestas casi siempre huyo un poco cuando son problemas que van ligados a bases de datos, ya que es un tema que no me apasiona mucho, solo lo veo como trabajo mientras que otros problemas son más "entretenidos".
Saludos. |
|
| Volver arriba |
|
 |
elyeyo1000 Cdt

Registrado: 20 Dic 2006Mensajes: 17
|
Publicado: Mar 24 May, 2011 12:25 pm Asunto: |
|
|
Hola otra vez,
Supongo que todos los canarios somos igual de cabezones, así que ahí van mis últimos avances con el tema.
1.- He creado un formulario principal, que carga una DLL pasandole un parámetro entero. Esta DLL, al descargarse, pasa de nuevo el control al formulario principal, que acto seguido, carga otra DLL pasandole otro entero, y así sucesivamente hasta que una de las DLL dice que ya no sigue "jugando", y la aplicación termina. El caso es que con esta prueba, cuyo código no tiene mayor complicación, todo va perfecto.
2.- He añadido un Modulo de datos al formulario principal, añadiendo en el mismo un objeto TADOConnection, cuyo ConnectionString me conecta con mi base de datos MSSQL. En cuanto a las DLLs, también he añadido un modulo de datos a cada una de ellas con su TADOConnection individual, cada una con su ConnectionString. Hechas las prubeas, la aplicación sigue funcionando sin quedarse colgada.
3.- Avanzando más en el tema, desde el formulario principal, he pasado el TADOConnection como parametro en las llamadas a las DLLs, para que compartan la misma conexión entre todos. Hechas las pruebas, todo funciona correctamente... de momento.
4.- Como veo ke todo va bien, añado más código, así que en cada una de las DLLs, que ya comparten el TADOConnection, hago una consulta sencilla a la base de datos. El coste de carga aumenta un poco, pero no lo suficiente para dejar congelada la aplicación. Por lo tanto, sigue funcionando sin problemas.
5.- Ahora viene lo weno... Debido al aumento de carga, decidí añadir una barra de precarga. Cada una de las DLLs tiene su propio formulario de precarga, que consiste en un label y una barra que va de un lado a otro. Este formulario se carga en un hilo independiente para no bloquear la aplicación mientras se realiza la consulta. Concretamente se trata de la barra que tan amablemente (como siempre) nos facilitó Cesar : https://cppbuilder.es/viewtopic.php?t=10380&highlight=thbarra. Pues en cuanto he introducido la barra, la aplicación me ha realizado unas 20 llamadas a las DLLs y finalmente se ha congelado durante 30 segundos, continuando después como si nada hubiera ocurrido.
En fin, definitivamente, he encontrado el error. Ahora me gustaría solucionarlo, ya que necesito ese Preloader para que los usuarios sepan que algo esta pasando mientras esperan a la carga de los datos. Voy a intentar realizar un proyecto con las barras de precarga pero sin conexión a la base de datos, para comprobar si falla y poder subir el código, quizá de esa forma, alguno pueda ayudarme.
Saludos. |
|
| Volver arriba |
|
 |
elyeyo1000 Cdt

Registrado: 20 Dic 2006Mensajes: 17
|
Publicado: Mar 24 May, 2011 12:39 pm Asunto: |
|
|
Pruebas finalizadas:
Sin conexión a la base de datos y utilizando los formularios de precarga, todo funciona perfectamente... Creo que no tengo más opción que mostrar un formulario sin más y comenzar a realizar las consultas mientras los usuarios visualizan un paisaje
Si alguien tiene alguna otra idea... aquí dejo el código de inicio de las dos DLL (ambas con iguales). Este código está en el formulario interno de la DLL que es el que muestro al llamar a la funcion LoadForm de la DLL:
| Código: | // Mostramos la barra PRECARGA
TFBarra *FB = new TFBarra(this);
FB->Label1->Caption = "Cargando Datos..."; //Mensaje
FB->Show();
AnsiString Consulta = "exec I3_PPartes_SEL_Cab 'IdEmpleado=371'";
MDATOS->AbrirQuery(Consulta, MDATOS->QTemporal);
delete FB; // borramos la barra |
El resto del código es el que tengo al principio del hilo, y el código de FBarra es el que viene en el enlace que nos facilitó Cesar (en el mensaje anterior).
Eso es todo, si alguien es capaz de hacer algo soy todo oidos.
Saludos. |
|
| Volver arriba |
|
 |
|
|
No puedes publicar temas nuevos en este foro No puedes responder a temas en este foro No puedes editar tus mensajes en este foro No puedes borrar tus mensajes en este foro No puedes votar en las encuestas de este foro
|
© 2002–2026 Club Builder · Funciona con phpBB.
|