Can't set Fire Monkey Form property - delphi

I am trying to initialize form properties in the program source file in a Fire Monkey application and it throws an exception. Here is the code:
uses
System.StartUpCopy,
FMX.Forms,
uMainForm in 'Units\uMainForm.pas' {MainForm},
UDataModule in 'Units\UDataModule.pas' {DataMod: TDataModule},
DataHelperClasses in 'Units\DataHelperClasses.pas',
EXDIntf in 'Units\EXDIntf.pas',
Exd in 'Units\Exd.pas';
{$R *.res}
var
ViewModel: TEXDViewModel;
begin
Application.Initialize;
Application.CreateForm(TDataMod, DataMod);
Application.CreateForm(TMainForm, MainForm);
ViewModel := TEXDViewModel.Create;
MainForm.Data := DataMod;
MainForm.ViewModel := ViewModel; //This throws an access violation exception
ViewModel.Data := DataMod;
Application.Run;
end.
I have no problem doing this in a VCL app. How do I fix it?

There is difference in behavior between VCL and FMX - FireMonkey Application.CreateForm method. While in VCL CreateForm actually creates form and after that call form variable is fully initialized and ready to be used, in FMX CreateForm does not create form and form variable would still be uninitialized - nil - after that call. Because of that using form variable throws AV.
FMX.TApplication.CreateForm
CreateForm does not create the given form immediately. It just adds a
request to the pending list. RealCreateForms creates the real forms.
FMX has Application.RealCreateForms method that is automatically called in Application.Run. If you need to use form variables before that, you can call Application.RealCreateForms yourself. After that call you can safely use form variables you added to the list with Application.CreateForm
Keep in mind that Application.RealCreateForms will go through form creation process only once, so you have to call it after you made all calls to Application.CreateForm or you will end up with some unitialized forms.
begin
Application.Initialize;
Application.CreateForm(TDataMod, DataMod);
Application.CreateForm(TMainForm, MainForm);
// this forces creation of FireMonkey forms
Application.RealCreateForms;
....
Note: On Windows and OSX platforms RealCreateForms is first thing that is called in Application.Run, so it does not matter whether it is called by you or automatically. However, on Android and iOS platforms additional (initialization) logic happens before RealCreateForms is called in Application.Run, if you develop for those platforms, you should proceed with caution when using RealCreateForms and watch for potential side-effects. Probably the best option for mobile platforms would be to move your custom initialization into Form OnCreate event.

Related

Getting access violation error in FormClose using VCL Themes

I developed an application in VCL which is using VCL Themes. This application requires TPageControl and inner(child) forms in it.
Each child form has same way in OnClose: Parent.Destroy;
MsgResp := MessageDlg('Closing info....', mtWarning, [mbYes, mbNo, mbCancel], 0);
case MsgResp of
mrYes:
begin
DoSomething; {Save something}
Parent.Destroy;
end;
mrNo:
begin
Parent.Destroy;
end;
mrCancel:
begin
Exit;
end;
end;
If I set a theme to application like Sapphire Kamri (or something else), I get access violation error in destroying parent component. But if I use default style (Windows), this code works fine.
This is entirely to be expected. Your code is just as broken without VCL styles, but you get away with it.
The problem is the calls to Parent.Destroy. When these occur, the parent is destroyed, and so are all of its children, including the control that owns the code seen in the question. When the call to Parent.Destroy returns, execution continues in methods of objects that have been destroyed. That is the source of the runtime error.
You need to schedule the destruction to happen after the OnClose event handler has completed. The VCL Release method exists for this very purpose.

Form appears after 'Application.CreateForm' step

I am working on a project in delphi 2007 (CodeGear RAD Studio).
There are couple of forms in the application. Thouse forms are created as follows:
program MyProgram;
uses
Forms,
uMain in 'Source\uMain.pas' {MainForm},
uSettings in 'Source\uSettings.pas' {fSettings};
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
Application.CreateForm(TSettings, Settings);
Application.Run;
end.
the problem is, that on line Application.CreateForm(TSettings, Settings); Settings form appears (not modal). And the question is why it is happening?.
I know that it is probably not enough information, but I am ready to provide some, if it is needed.
P.S. I am currently re-wrighting programm logic so settings form will be created only before it is about to appear, and after that delete it. Still, I would like to know the answer to this question.
Your settings form's Visible property is set to true at design time, and thus it appears as soon as it's created.

"Cannot create form. No MDI forms are currently active" error

I have a MDI main (parent) form and a MDI child form. I create the child at runtime like this:
VAR
FrmDereplic: TFrmDereplic;
procedure TMainFrm.Button2Click(Sender: TObject);
begin
FrmDereplic:= TFrmDereplic.Create(MainFrm);
FrmDereplic.Show;
end;
Steps to reproduce the error:
I start the app, I press the button to create the child, I press the 'x' button on main (parent) form to close the application and I get an "Cannot create form. No MDI forms are currently active" error.
The line on which the error appears is in the child form:
procedure TFrmDereplic.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:= caFree;
end;
procedure TFrmDereplic.FormDestroy(Sender: TObject);
VAR MyIniFile: TCubicIniFile;
begin
MyIniFile:= TCubicIniFile.Create(AppINIFile);
TRY
with MyIniFile DO
begin
if WindowState<> wsMaximized then
begin
// save form's screen pos
...
end;
WriteInteger ('Dereplicator', 'fltExtensions', fltExtensions.ItemIndex); <----- HERE
FINALLY
FreeAndNil(MyIniFile);
END;
end;
I save lots of form's properties (and other's controls properties) to the INI file. But it only fails when I try to save fltExtensions.ItemIndex (which is a TFilterComboBox). If I comment that line it works perfectly.
Any idea why it tries to create a form when I actually closed the application?????????
I look on some web sites and just found the problem. It looks like it is preferably to have the Owner set to Application, instead of the main form. Remy Lebeau suggests that the real problem is in the OnDestroy of the the child form. There is no valid handle to the window that holds the filter then the OnDestroy is called. So, changing the destruction order gives a chance to TFrmDereplic.OnDestroy to execute properly.
So, here is the solution:
SOLUTION(S)
FrmDereplic:=
TFrmDereplic.Create(Application);
or
Do not save form's properties in
OnDestroy
The second one requires few extra lines of code as the OnClose even is not always called.
This was extracted from Delphi HELP:
Note: When the application shuts
down, the main form receives an
OnClose event, but any child forms do not receive the OnClose event.
If you use Application.Terminate, then onCloseQuery and onClose will not be called. Same for Halt (but... this is way too extreme, right?).
The error occurs when reading the fltExtensions.ItemIndex property because it requires fltExtensions to have an HWND, which requires its parent TFrmDereplic form to have a HWND, which requires the project's MainForm to have an HWND. But the app is in a state of shutdown, and the MainForm cannot allocate its HWND anymore, so TFrmDereplic raises an exception when it cannot obtain an HWND for itself.
Saving your INI data in the form's OnDestroy event is too late. You need to the OnClose event instead.
If the code you provided in your question is the real one then I guess the error is in this line:
FrmDereplic:= TFrmDereplic.Create(TMainFrm);
I never tried this and I am not sure if the compiler really buys it (can't test it now), but you are trying to set a class as owner of the MDI child form. Instead of that you should do either
FrmDereplic:= TFrmDereplic.Create(Application);
or
FrmDereplic:= TFrmDereplic.Create(self);
The first option sets the application as owner of the MDI child form, while the second one sets the instance of the MDI main form as owner.
Hope that helps. :-)

ExitProcess from the OnShow event of MainForm in Delphi

I have an application that on startup checks some conditions and launches an external program in the OnShow event of the Main Form. The problem is that if there is an error when launching the external program, I want the application to terminate immediately. But there is an issue with that, in that EurekaLog catches my exceptions and somehow disrupts the message loop there by negating all calls to Application.Teminate and any other normal shutdown methods.
So here is my question, would ExitProcess be the best route then to immediately terminating my application when this condition exists?
By the time OnShow has fired, you're too far into the program to decide that you don't really want the program to run. You should make that determination sooner. OnShow is not the place to decide that the form shouldn't be shown.
This is the sort of thing you should check before you even create the main form. Put your checks in the DPR file, and if you determine that the program shouldn't run, then simply call exit.
begin
Application.Initialize;
if not ApplicationShouldReallyStart then
exit;
Application.CreateForm(TMainAppForm, MainAppForm);
Application.Run;
end.
Fill in your own implementation of ApplicationShouldReallyStart. (And it really should be a separate function, not in-line in the DPR file. The IDE gets confused if the begin-end block in the DPR file gets too complex.)
Aside from that, do not call ExitProcess. Call Halt instead. Halt calls ExitProcess, but it also calls unit finalization sections and other Delphi-specific process-shutdown tasks.
Work WITH the system, not AGAINST it! You can't simply die in the middle of things. If you want to die do it within the rules--WM_CLOSE or maybe your own routine that says why it's dying and then sends a WM_CLOSE.
You better send a wmClose message to the window. Else you have a big chance to get into trouble because of other messages send to the form.
I wrote a small application to test a theory and here is what I would suggest.
Call the CLOSE method.
The following example unit closes the application with no errors in D2009.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormShow(Sender: TObject);
begin
close;
end;
end.
While I fully agree with Rob Kennedy here, I want to note, that you may use EurekaLog's routines to control error dialog behaviour.
For example:
uses
ExceptionLog, ECore;
...
begin
ForceApplicationTermination(tbTerminate);
// ... <- Bad code goes there
end;
That way, the application will be closed right after displaying error dialog.

Show message to user while Delphi program terminating

In some cases I should terminate application with
Application.Terminate;
In that case I want to show some message to user inside destructor of some TFrame.
I have tryed to use MessageBox, MessageBoxIndirect, ShowMessage functions with no success. Message box isn't appears on screen and application closes.
Is there any way to show some message to user while Application terminating?
Btw, Delphi XE used.
Like comments indicate for showing messages with e.g. MessageBox, MessageBoxIndirect or ShowMessage, your process needs to still run.
Delphi for .NET would have a suiting OnShutdown event, but when not compiling with the conditional CLR it is absent.
One can however use an exit procedure, like TApplication does itself with DoneApplication. This procedure is called at a point where the process still lives, before System.Halt is called. It is added by calling AddExitProc(Proc: TProcedure) in System.SysUtils. In code commentary for this is following:
{ AddExitProc adds the given procedure to the run-time library's exit
procedure list. When an application terminates, its exit procedures
are executed in reverse order of definition, i.e. the last procedure
passed to AddExitProc is the first one to get executed upon
termination. }
I would personally decide to use this, despite the warning from the documentation, as TApplication itself is still using it in Tokyo to have DoneApplication called. Excerpt from documentation:
[...]AddExitProc is not compatible with ULX package support and is
provided for backward compatibility only. Do not use AddExitProc in
new applications.[...]
The small code example of a VCL project will show a message on application termination:
program Project1;
uses
Vcl.Forms,
Vcl.Dialogs,
System.SysUtils,
Unit2 in 'Unit2.pas' {Form2};
{$R *.res}
procedure AppTerminated;
begin
MessageDlg('Message', mtInformation, [mbOk], 0);
end;
begin
AddExitProc(AppTerminated);
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm2, Form2);
Application.Run;
end.
Once you have called Application.Terminate any attempt to show a dialog fails. You can't have your cake and eat it. You can't terminate your process, and keep it alive to display a dialog.
So, the obvious solutions to that conundrum are:
Show your dialog before you terminate the application, or
Create a separate process to show the dialog, and then terminate the application.

Resources