OnShow-Event while Construction? - delphi

I have a Form (fsMDIChild). This is inside the OnShow-Event of TForm4.FormShow:
if targetDatabase.hasItems then
Unfortunately OnShow is called indirectly on instantiation:
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas' {Form2},
Unit3 in 'Unit3.pas' {Form3},
Unit4 in 'Unit4.pas' {Form4};
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TForm2, Form2);
Application.CreateForm(TForm3, Form3);
Application.CreateForm(TForm4, Form4); // <-- calls TForm4.OnShow! Why?
Application.Run;
end.
I put a Breakpoint inside fo the ShowForm-Handler. This is the Output of my Stack-View:
TForm4.FormShow(???)
Project1
My Question is:
Why is OnShow called on an fsMDIChild?

In the normal way of things, for standard forms, you would not expect this to happen. So clearly there is some code in your project that triggers this behaviour.
As some general advice, you can use the debugger to find out why this happens.
In the project options, check the Debug DCUs option.
Set a breakpoint in your OnShow event handler.
Run the program under the debugger.
When the breakpoint fires inspect the call stack.
By enabling debug DCUs you ensure that you will get a complete call stack including functions in the VCL. Follow the call stack to work out why the event is firing.
Now, the extra specific information is that the form in question is an MDI child. They cannot be made invisible, and so are shown immediately. In other words, the behaviour that you observe is expected. MDI children are always visible. Hence the OnShow event fires during construction.

Related

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.

Why main form can't access to DataModule's images and actions when project is open in Delphi XE?

My Delphi XE application was fine till a couple of days and I can't figure out what is wrong. My project layout:
Visual controls are on the main form
Actions and image lists for those controls are on a data module
When I open my project, the main form doesn't have any image or actions associated to any of the controls, even though they should be. When I compile I get the error message: "Module 'winMain' links to module 'modGeneral' which cannot be found in the current project. Do you wish to remove/redirect the links to another module?".
The work around: close the main form after I've opened the project, then open the module in the IDE by double-clicking it in the project manager (yes, it is present in the current project), then re-open the main form: all my actions and images are now correctly displayed.
What do you think, is that a known Delphi bug ? A problem with my project ?
Check your .dpr file. One way to reproduce your problem is to change the uses clause in it.
Consider this example which works fine:
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas' {DataModule2: TDataModule};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TDataModule2, DataModule2);
Application.Run;
end.
The icon in the Project Manager looks as usual for a module with a dfm:
If you remove the comment, or comment it out:
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas'; // {DataModule2: TDataModule};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TDataModule2, DataModule2);
Application.Run;
end.
Here, the icon has changed:
...and you get the errors you describe.
You need to close and reopen the project for your changes to take effect.
TOndrej's answer is complete and correct. I have just a small addition which I maybe should post as comment but I'm afraid it won't be noticeable.
I faced with the described error after unit renaming and project file refactoring (actually I removed all the in and comments from uses section). I returned the required pattern in the uses section but I still got the error. My mistake was an alignment that I added to comments in uses section:
DMMain in 'DMMain.pas' {fdmMain : TDataModule},
DMIndex in 'DMIndex.pas' {fdmIndex : TDataModule},
Surprisingly it really matters. Removing the alignment
DMMain in 'DMMain.pas' {fdmMain: TDataModule},
DMIndex in 'DMIndex.pas' {fdmIndex: TDataModule},
fixed the error and I got things working.

How do you deal with IFDEFs in .dpr uses section

Whenever you add a new unit to the project Delphi rebuilds the .dpr file and all the IFDEFs in the uses section are gone.
To work around this I typically use NotePad to create new .pas files, and add it to the .dpr manually. If I need a form I use File->New->Form and then revert the .dpr file to the previous version. Not very RAD if you ask me ;-)
How do you deal with that? Is there a way to add a unit in the IDE while keeping the IFDEFs?
Sometimes I create a unit specifically as a place for all the IFDEFs and other stuff the IDE would mess up if it were in the dpr. This unit typically goes to the top of the dpr's uses clause. This trick doesn't cater for all scenarios but it sometimes saves a lot of tedious work.
I don't put any ifdefs into a dpr file. If I want to use different units/forms in a project, depending on some condition, I split the project in two.
I spent quite a while trying to work that one out,
I ended up have a project file (.dpr) for each build type,
with the Conditions in Project|Project Options|Directories/Conditionals
and only the units i wanted added in to the project
this dose have the down side that if you have custom code in the .dpr, it will have to be manually copied to the other project files when it changes.
as noted by Rob Kennedy, this can handled by putting the custom code into its own unit, which is called by a single procedure. thus minimizing the .dpr code size/changes to be made
Also, another bonus you get is that if you add all your .dpr files to a project group, you can build all your different versions with one click / cmd line
You can add it manually from within the IDE. (Use the "view source" option on the project).
Normally the dpr is "hidden". You are not expected to change anything in there.
And if you do, you better make sure all your changes are manual else you are losing some information.
For forms, datamodules, and other units which contain a single class by which functionaity will be replaced, the solution is rather simple. Just DON'T add the custom units directly to the product, but do save them some place in the search path (or modify the project search path to include thier location).
1) Create a NEW unit, which contains either the parent for all of the other classes, or the interfaces that they all will implement (I generally prefer the later as it allows easier customization) [for example purposes this is called uSpecialParent.pas]
2) Add a class variable which will referenced when you need to create the new functionality. for instance if you just were going to show modal a bunch of forms, so didn't care about any other methods then you could have a variable that looked like the following:
TYPE
TMySpecialFormClass : class of TForm;
VAR
TMySpecialForm : TMySpecialFormClass;
3) Create another unit which will contain all of the IFDEFS. It could look something like the following:
Unit uRegisterSpecialForms;
interface
uses
{$IFDF SPECIAL1}
uSpecial1,
{$ENDIF}
{$IFDEF SPECIAL2}
uSpecial2,
{$ENDIF}
uSpecialParent;
implementation
// no code needed.
initialization
{$IFDEF SPECIAL1}
TMySpecialForm := uSpecial1.TSpecialForm1;
{$ENDIF}
{$IFDEF SPECIAL2}
TMySpecialForm := uSpecial2.TSPecialForm2;
{$ENDIF}
end.
4) To reference this in your code you only need the uSpecialParent added to the unit which will be requesting a special form and then create it dynamically for example to show this modal you could invoke the following:
var
frm : TForm;
begin
frm := TMySpecialForm.Create(nil);
try
frm.showmodal;
finally
frm.free;
end;
end;
And here's the lo-tech approach for completeness' sake:
After the IDE has messed up your uses clause again:
close the project
go to your version control tool of choice and diff the DPR against the latest checked-in
version using a merge-enabled diff tool like WinMerge
revert the IDE changes
save the DPR
get on with it
(Delphi 7)
I've just tried the same.
Take a look at the first code version and at my comments below:
program Project1;
{$IFDEF TESTIFDEF}
uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas' {Form2};
{$ELSE}
uses
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$ENDIF TESTIFDEF}
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TForm2, Form2);
Application.Run;
end.
At that point, I've just inserted the 2nd Form and noticed that the corresponding unit (Unit2.pas) was inserted inside the first part of the IFDEF i.e. inside the "TESTIFDEF" labeled part - hence not overriding the second block (after the {$ELSE}).
Thus your solution should be:
define a IFDEF statement like "{$IFDEF DELPHIBASISCONFIGURATION}" in place of my "{$IFDEF TESTIFDEF}" where all the forms will be added.
define as many alternative LABELS for the different configurations you want to work with.
each time you've added a form to the project, copy the inserted line of the first block into the corresponding blocks below - depending on your needs...
activate the required configuration using the define statement or the option dialog
NEVER DEFINE "DELPHIBASISCONFIGURATION" ;)
Hence, it should look like this:
program Project1;
{$DEFINE MYCONFIG1} // THIS ONE IS NOW ACTIVE
{$IFDEF DELPHIBASISCONFIGURATION}
uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas' {Form2},
Unit3 in 'Unit3.pas' {Form3};
{$ELSE}
// THIS IS A "COMMON TO ALL CONFIG" PART
uses
Forms,
// FIRST CONFIGURATION
{$IFDEF MYCONFIG1}
Unit1 in 'Unit1.pas' {Form1},
Unit3 in 'Unit3.pas' {Form3}
{$ENDIF MYCONFIG1}
// SECOND CONFIGURATION
{$IFDEF MYCONFIG2}
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas' {Form2}
{$ENDIF MYCONFIG2}
// THIS IS THE "COMMON TO ALL CONFIG" END :)
;
{$ENDIF TESTIFDEF}
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
//Application.CreateForm(TForm3, Form3);
//Application.CreateForm(TForm2, Form2);
Application.Run;
end.
As you can see, I've discarded the calls to Application.CreateForm(...) for Form2 and Form3.
IMHO, it's usually better to dynamically create the supplemental forms at the moment you really need them i.e. not all the forms at program start...

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