PDA

Ver la Versión Completa : Access violation el cerrar forms


franroju
30-05-2012, 20:40:09
Hola gente. Se que el asunto ya marca que el tema es aburrido, je, pero tengo la siguiente situación:

Tengo un form principal, y todos los demás son creados por éste a medida que se necesitan, de la siguiente manera:

TForm5.Create(Application).ShowModal;

Ahora, todos los demás forms tienen un botón para cerrarse y volver al form principal, pero cuando invoco a Close, me arroja el error de Access violation.

Ya cambié en Proyecto->Opciones los forms para que solo sean creados cuando el principal lo indica.

Alguien me puede explicar la forma correcta de cerrar un form, volver al principal, sin errores??

Gracias!

maeyanes
30-05-2012, 21:03:22
Hola...

¿Podrías mostrar más código? Por ejemplo, dónde y cuando invocas el método Close.


Saludos...

franroju
30-05-2012, 21:47:00
El método Close lo invoco en un botón. Generalmente, en el form que quiero cerrar realizo consultas a querys o tablas que están en un DataModule.

Puede ser que el error sea por el DataModule y no por el form?

No se, he probado varias cosas, como por ejemplo:

if (Form5 <> nil) then begin
DataModule2.queryLogin.Close;
Form5:=nil;
Form5.Close;
end;

Pero no hay caso. De última, si el código que ingreso no alcanza, solo diganme como harían uds. para tener un Form principal, y otros disponibles para cuando se necesiten, y cuando estos deban cerrarse para volver al principal, no ocurra error de acceso.

Gracias!

LoPiTaL
30-05-2012, 22:12:05
Aquí hay muchas cosas involucradas, y como dice el amigo maeyanes, si no pones un poco de código, no podemos ayudarte más que dando palos de ciego.

Se me ocurre una cosa (así a bote pronto), y es que:

1º) Si los creas a medida que se necesitan, lo más común es usar código del siguiente estilo, para crearlo Y DESTRUIRLO:


with TMiFormQueNecesito.Create(nil) do begin
try
//Lo que hagas con ese form.
ShowModal(); //o Show o lo que sea...
finally
Free;
end;
end;


Fíjate que el Owner del form es nil, ya que lo destruimos manualmente.

2º) Al cerrar un formulario, pues eso, sólo se cierra, no se destruye. Por tanto, si tienes un botón (o menú item o event handler en general) que haga lo que indicas:


TForm5.Create(Application).ShowModal;


Al cerrar el form, éste no se destruirá, sólo se cerrará. Se destruirá al salir de la aplicación. Te remito al apartado 1º para ver cómo crearlos y destruirlos de golpe.

3º) Si lo que quieres es que se comporte como se indica en el apartado 2º, entonces deberías guardar el form en una variable y crearlo una única vez:


if not assigned(FMiForm) then
FMiForm:=TForm5.Create(Application);
FMiForm.ShowModal;


4º) Por supuesto que si creas un form con Owner Application, entonces NO debes destruirlo al cerrar la aplicación. Ya se encargará de eso la VCL. En el ejemplo del apartado anterior, NO debes destruir FMiForm.
Si quieres controlar tú cúando destruirlos, entonces créalos con Owner=nil y al cerrar la aplicación los destruyes.

Espero ayudarte, pero sin más datos, poco más se puede hacer.

Un saludo,
LoPiTaL

champy
30-05-2012, 22:46:35
El problema es que estás poniendo a NIL el valor de form5, a lo que te da el error al mandar el CLOSE porque vale NIL.

Mi consejo es, llama a Form5.Close, y en el evento Onclose del formulario haces el Form5 := NIL;


if (Form5 <> nil) then begin
DataModule2.queryLogin.Close;
Form5.Close;
end;

Procedure Form5.Onclose(SENDER : TObject);
begin
Form5 := NIL;
end;

LoPiTaL
30-05-2012, 22:52:09
Perdona, no había visto tu último post!!
Edito: champy se me adelantó :(

Evidentemente, el código que has puesto no te va a funcionar :p (supongo que será al escribirlo en el navegador, pero por si acaso...):


if (Form5 <> nil) then begin
DataModule2.queryLogin.Close;
Form5:=nil;
Form5.Close;
end;


¡¡¡pones a nil el formulario antes de destruirlo!!!!

Debe ser de la siguiente forma, incluyendo código para destruirlo, de tal forma que cuando pulses el botón TIENES que volver a crearlo:


if (Form5 <> nil) then begin
DataModule2.queryLogin.Close;
Form5.Close;
Form5.Free;
Form5:=nil;
end;


o alternativamente:


if (Form5 <> nil) then begin
DataModule2.queryLogin.Close;
Form5.Close;
FreeAndNil(Form5);
end;


Pero ya te digo, que lo más "elegante" es usar la opción 1 que te planteaba en el anterior post, creas el form cuando lo muestres, haces lo que sea con él (ShowModal, etc, etc...), cuando pulsas el botón ese de cerrar SÓLO llamas a Close, en el evento FormClose de este formulario cierras el DataModule, y cuando regrese del ShowModal, entonces se hace el Free.
Resumiendo:


//En Formulario principal

procedure TMainForm.OnClickDeMiBotónDeMostrarFormulario(Sender: TObject);
begin
with TMiFormQueNecesito.Create(nil) do begin
try
//Lo que hagas con ese form. Concretamente, aquí le podrías pasar variables del main a este form para que haga lo que sea con ellas (si necesita alguna)
ShowModal(); //o Show o lo que sea para que se muestre, ya con las variables que necesite inicializadas
//Lees las variables que necesites (si necesitas alguna) del form
finally
//Finalmente destruimos el form
Free;
end;
end;
end;

//En el formulario secundario
procedure TMiFormQueNecesito.FormClose(Sender: TObject; var Action: TCloseAction);
begin
//Aquí todo lo que tenga que hacer al cerrarse. Si tienes que hacer un Showmessage, debe ser aquí, NO en el formDestroy, ya que en el formDestroy ya no se puede cancelar el
//cierre del formuario, y aquí sí.
DataModule2.queryLogin.Close;
end;

procedure TMiFormQueNecesito.OnButtonClick(Sender: TObject);
begin
//Evento al hacer click en ese botón tuyo para cerrar.
Close;
end;



Fíjate que he separado lo que tú haces en el botón de cerrar en dos métodos FormClose y el evento de cerrar. ¿Por qué? Porque si cierras el formulario SIN pulsar el botón (por ejemplo Alt+F4), entonces no cerrarías el dataModule, y de ésta forma sí.

Una última cosa.

Si creas y destruyes formularios manualmente, te recomiendo que borres las variables que Delphi crea automáticamente al añadir un formulario nuevo al proyecto, como por ejemplo las del estilo Form1, Form2, etc... Así no te liarán, ya que estas variables las usa Delphi para crearte el formulario por tí, no para que tú lo hagas por Delphi.

Espero haberte ayudado.

Un saludo,
LoPiTaL

ecfisa
30-05-2012, 23:56:39
El método Close lo invoco en un botón
if (Form5 <> nil) then begin
DataModule2.queryLogin.Close;
Form5:=nil;
Form5.Close;
end;

Hola.

Además de lo dicho por los compañeros, no veo la necesidad de comprobar si Form5 existe en ese punto. Si pudiste hacer click en un botón situado en Form5, indudablemente existe :)

Otra forma en que podés simplificar la creación es:

...
type
TForm5 = class(TForm)
...
procedure Button1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormDestroy(Sender: TObject);
private
public
class function MostrarModal: TmodalResult;
end;

...
implementation
...

class function TForm5.MostrarModal: TModalResult;
begin
with TForm5.Create(nil) do
Result:= ShowModal;
end;

procedure TForm5.Button1Click(Sender: TObject);
begin
Close;
end;

procedure TForm5.FormClose(Sender: TObject; var Action: TCloseAction);
begin
//...
Action:= caFree;
end;

procedure TForm5.FormDestroy(Sender: TObject);
begin
//...
Form5:= nil;
end;
end.


Form principal, llamada de ejemplo:

if TForm5.MostrarModal = mrOk then // o simplemente: TForm5.MostrarModal;
....


Saludos.

franroju
31-05-2012, 16:20:35
Gracis eficsa y lopital, se solucionó el problema. Uno cree que con un simple show y close queda todo lindo, je.

Abrazo grande