Solved

C++Builder3: Terminating app in TForm1

Posted on 1998-09-17
16
549 Views
Last Modified: 2008-02-01
Hi!

This is my problem:  In the TForm1 constructor (or in the
TForm1::OnCreate handler) I run a dialog box to see if the user
wants to continue (installation or something). If he doesn't, I
want to quit the application. I use this code:

        if (Application->MessageBox("Do you want to continue?",
                "Question", MB_OKCANCEL)!=idOk) //or something
                Application->Terminate();

This works, BUT! The TForm1 IS drawn in a flash and then killed.
I want the program to exit cleanly, not with a lot of flashing windows
after I already clicked 'cancel'.

There are 2 options i've tried. Either I put the question in the
main program file (the one that contains Application->FormCreate(....)
and Application->Run()), but this stinks since I have 2 places I want to
be able to quit and I do a lot of initializing.

The other option is to change the Application->Terminate() to:

        {
                Application->Terminate();
                while (!Application->Terminated());
        }

This SEEMS to work just fine, the window does not get drawn, BUT!
the IDE still thinks the program is running, and if you press
ctrl-alt-del, the project is still running! I assume that there is
one thread still running because i did not return from the event handler
until the program had terminated.

Can anyone help me with this please?

Thanks a lot!

Greetings,
        Kenney aka Forge.
0
Comment
Question by:Forge
  • 8
  • 7
16 Comments
 
LVL 2

Expert Comment

by:gysbert1
Comment Utility
Why not put it in the project source file ?
This is the best place to put it.

I suggest you create the dialog before any Initialisation. Have a look at the modal result of the dialog box and do something like:
 
  if (OkToRun) {
     Application.Run();
  }

  To prevent the Form1 from popping up.

On the other place in your code you can use the same dialog, check the modal result and call Terminate() if you want to quit.

If this is not OK and you can explain to me why not I can work on another solution ...
0
 

Author Comment

by:Forge
Comment Utility
Thanks for the answer. That IS a solution, but not what i'm looking for.
I don't even want to look at the project source file. The IDE strongly discourages it, because if I open it the whole project gets reloaded.

The program I am making first initialised some data (reads an inifile etc) and then asks
for a username. The user can then back out. Then it shows a list of lessons to the user.
The user must choose one of them and then continues with that lesson, or can still back
out.

The amount of initialising I do is too much to put just in the WinProc procedure. Also, I
would be calling the dialog box before I call Application->Run(), which is bad since there is
no message loop then! I would have to use the WinApi MessageBox routine.
So this is not what i'm looking for.

Check out the Earth game in Examples/Games/EarthPong or something like that.
It has the same problem.

I also tried to set the Visible property to zero, but that didn't help either.

HELP! :)
0
 
LVL 2

Expert Comment

by:gysbert1
Comment Utility
How about making the bailing out dialog a form (the main form of the Application that is), then you only show the real main form if necesarry and you can keep the dialog hidden during normal operation ?


0
 

Author Comment

by:Forge
Comment Utility
I would like to keep the program structured. Your solution is nice, but.. Say I make the main form the 'Enter username' form,
then, how do I run the 2nd form, the 'Choose Lesson' form?
I want to close this form and then run the next form.
Can i do that with

      Close();
      FOrm2=new TForm2;
      Form2->ShowModal();
? (I'm not at home right now so i can't try it out :)

I am used to do all initialsing in the main form and then call some other forms. That way the main form is visible and the other forms will get created and shown over the main form when needed. I never passed on the focus to another form like this before.

Windows coding is strange. Its all event driven but some things need to be done anyhow.

Say I run some strange program. It does some initial checking and
finds out that you dont have something installed and therefore cannot run. Then it shows a messagebox to tell you that and exits cleanly. If the checking went ok then it'll open the main form and continue the program as planned.
How do you implement that? I dont want to the checking in the project sourcecode. I'd say that the case that the <something> is NOT installed is an exception so I assume that the program will show the main form.

What i want is a form that is not shown, just a procedure that is called after startup, when the messageloop is already active, in which i can do my initialisations and show some windows if necessary.


0
 
LVL 2

Expert Comment

by:gysbert1
Comment Utility
Try it like this :
      Hide();                        // To hide the "Enter Username" form
      InitializeApplication();  // Your Init functions
      FOrm2=new TForm2;  //
      Form2->ShowModal(); // Show your Main form Modally, returns when Form2 is closed
      Close();                      // To close Form1 and exit the application after Form2 was
                                       // closed.

Since Form1 is the MainWindow of the Application you can Terminate the Application at any time by doing a Form1->Close(); Although Form2 is not the MainWindow it has focus since it is shown Modally. Form1 is not gone, it is still active, but it is not visible.

It is not Advisible to edit the project source since this cause some very hard to find bugs. It is sometimes the only way to do things though and it is definately permitted. In cases like this it is necesarry but it is good only to do it as a last resort.

I see you are creating your forms dynamically. That is good otherwise the initialisation will happen before the event loop is active.
0
 

Author Comment

by:Forge
Comment Utility
Hmz.  The Hide() function does not work!

void __fastcall TForm1::FormCreate(TObject *Sender)
{
    Hide();
    Application->Terminate();
}

It still flickers! I also tried to put it in the Constructor. Doesn't work either.
Maybe the Hide() function only works if the form is already drawn.
There is one more option.. I put the form outside the screen.. but this is a stupid solution.
Why doesn't the form stay hidden?

It works, basically, but i dont like the flicker, it seems unprofessional and it is not intended program behaviour: i want to QUIT immediately, not draw a window and then
quit.
Maybe the messages get queued, so first the window gets a WM_PAINT message and
next it gets a WM_QUIT message.. so its painted anyway. But i dont feel like writing an
OnMessage event handler to filter out the first WM_PAINT  just so the program exits cleanly. I think borland code bugs ;)

I bet you have BCB at home or at work. Try to make a new project quit from within Unit?.cpp without flickering. I have a pentium II 233, and even i can see it, so on slower systems it will definitely be noticed :)
0
 

Expert Comment

by:Vitenka
Comment Utility
The application has to init at least one form - the one with the
OK / cancel button.
So,in the bit where it lists the forms it will autoconstruct for you, turn them all off - (or just form1 might be enough)
Then make a new form, with your message, an OK and a cancel button, and make it the primary form for the project.
If he clicks OK - construct your form1 - and go with it.
If he clicks cancel, kill the app.
That should exit cleanly.
0
 
LVL 2

Expert Comment

by:gysbert1
Comment Utility
I did not mean for you to use the Hide() on the main Application form !

if you have a look at the comment next to Hide() you will see what I mean. I intended for you to hide the login form as Vitenka pointed out again.

Problem is that you will not be able to quit without showing anything. I suggest you do the following (since it is very bad to have an application not show anything and just quit, like you double click the Icon and nothing happens ...).

Make a Inforamtion (Ok/Cancel buttons) form. In it's onCreate do your initialisation. If it fails change the text on the form to read something like
"Initialisation failed, mydll.dll not installed !". Hide the Cancel button and change the Ok button's caption to "Quit".Also hide the Editboxes for Username and Password

If Init was Ok you change the Label to "Welcome to My Application, Please Log In", you change the size of the form and show the Editboxes for Username and Password. If the user logs in Ok you do what I have above in my comment.

You will always have to have a Form that tells the user what is going on even if something failed (hopefully causing an exeption that you catch). The trick is to create a working form that you manage dynamically, thus you change it's appearance to suit your needs by hiding, showing and moving the components around. It would be a little better if you created everyting on the form dynamically in stead of using the designer, and having an empty (frame only) main form.

0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 

Author Comment

by:Forge
Comment Utility
HM.. That's an option.. But its easyer to just create 2 different forms.

The problem is that i DO want to show a form, but I am not sure which one it will be until
i do some initialising. Well, i'll try to make the main form the Login window and do the initialisation in that window..

Ok, i'll try this: i change the project source code to call an initialisation function first.
Then i'll set the Application->MainForm property to my first window. I can't write to that property so i'll change the order in which the forms are created. This solution sucks..

There must be other programs, for instance 3Dstudio or some other big program, that when they start up and detect a fatal error (like missing DLL) they just ShowMessage() it and then quit, and if not they just start the program and init the main window, which is so significantly different from the Message box that you can't move around components to make the window look like either window?

I know that those programs are not created in BCB but... the way to create programs in BCB seems ok to me and all programs could be created using that way.
0
 
LVL 2

Expert Comment

by:gysbert1
Comment Utility
The best would be to set the MainWindow of the Application to be the one you want to show before you do Application.Run() and after your init functions in the application source. You can do this with traditional Windows programming by setting the applications' MainWindow (you have to do it) but BCB does it for you (see the help on TApplication) and you cannot change it ...

"Use MainForm to specify a form, different from the default, as the main window of the application. This value is set to be the first form created via the CreateForm method. MainForm cannot be modified at runtime because it is read-only."
Above from the BCB help.

If we want our Applications to look professional we have to do the professional thing and not always choos the easiest way but the way that gives the best results.

From the WIN32API helpfiles:
The WinMain function is called by the system as the initial entry point for a Win32-based application.

int WINAPI WinMain(

    HINSTANCE hInstance,      // handle to current instance
    HINSTANCE hPrevInstance,      // handle to previous instance
    LPSTR lpCmdLine,      // pointer to command line
    int nCmdShow       // show state of window
   );      
    :
    :
    :
Remarks

WinMain initializes an application, displays its main window, and then enters a message retrieval-and-dispatch loop that is the top-level control structure for the remainder of the application's execution. The message loop terminates when a WM_QUIT message is received. At that point, WinMain exits the application, returning the value passed in the WM_QUIT message's wParam parameter. If WM_QUIT was received as a result of calling PostQuitMessage, the value of wParam is the value of the PostQuitMessage function's nExitCode parameter. For more information, see Creating a Message Loop.

This is the only way to start a WIN32 application and it shows the main window and creates the message loop automatically.

You can get around the flashing window problem by showing the window offscreen. This is
  a bit of a hack though. Set Left = -1000 for the form, showing it way to the left of the
  cisible screen area, do init and then set left to what you want it to be. If you do not move
  it you will not be able to access it either and you will only be able to close it by other
  means ...

As I definately do not know everything I suggest you wait for more comments and post to a few newsgroups. There might be a more elegant solution out there somewhere ...

As for your last statement I agree but remember that you CAN edit the project source and that is as I previously said often the only way to do things (like change the main window of the application before your application is Run() )

By the way, Windows messages are queued and if the que is full they get dropped.
0
 

Author Comment

by:Forge
Comment Utility
Well you sure are fast with your comments, are you on the net all of the time? :)

The Left=-1000 was one of the bad solutions i thought of.. But I guess its the easyest way to go if you want to stick with the traditional BCB coding style.

I solved the problem by introducing a Splash screen and do the initialisation (i hate to type that word) in that window. From there on I call the dialogs and then the Main Window (modeless) and close the splash screen. For THIS project that's a solution but without a splash screen.. :(
Anyway, i am already posting in borland.public.cppbuilder.vcl (via www.inprise.com -> feedback) and noone seems to have a good answer to this..

You seem to be right that there has to be one window open anyhow.

Well, i think i'll leave it at this, unless someone else knows a good answer. In the mean time i want to give you the points, so post an answer ;)

50 pts isn't a lot but i expected this to be a simple answer. It seems its a bit more fundamental than that.. so i'll give you 75, that ok? :) Thanks a lot!
0
 
LVL 2

Accepted Solution

by:
gysbert1 earned 100 total points
Comment Utility
Thanks for the points, very nice of you.

A splash screen is probably your best bet. I hope someone comes up with a better solution for you though. I seem to remember a book I have at home that explains how apps like 3Dstudio probably does it. I will go and have a look for you, maybe they could shed some light on the subject.

PS. Yes, I am always on the net, I am at work and we have a permanent connection. I get notified when I have mail and always read it Immediately.
0
 
LVL 2

Expert Comment

by:gysbert1
Comment Utility
O yes, Regarding the following:

The other option is to change the Application->Terminate() to:

            {
                    Application->Terminate();
                    while (!Application->Terminated());
            }

    This SEEMS to work just fine, the window does not get drawn, BUT!
    the IDE still thinks the program is running, and if you press
    ctrl-alt-del, the project is still running! I assume that there is
    one thread still running because i did not return from the event handler
    until the program had terminated.

The Application does not receive a WM_QUIT and that is why it is still active, not another thread. The messageloop is serviced on idle and you are keeping the processor so busy that the messageloop is never serviced. If you did :
                    while (!Application->Terminated())
                         Application->ProcessMessages();

Instead the application would quit but then it would of course also service the WM_PAINT that causes the flicker and you would be back to square 1 ...
0
 

Author Comment

by:Forge
Comment Utility
Well, 3d studio also uses a splash screen. so bad example ;) But.. some installation programs, they do:

 'This will install ...', do you want to continue'.

If the file is corrupted or something is missing they will say that and terminate..
But.. this is easily done by just modifying the label contents ;)

Anyway, i'd appreciate it if you found out something to help me some day.
In case this question has disappeared, mail me at kenneyw@sci.kun.nl.

And thanks!
0
 

Author Comment

by:Forge
Comment Utility
Well, 3d studio also uses a splash screen. so bad example ;) But.. some installation programs, they do:

 'This will install ...', do you want to continue'.

If the file is corrupted or something is missing they will say that and terminate..
But.. this is easily done by just modifying the label contents ;)

Anyway, i'd appreciate it if you found out something to help me some day.
In case this question has disappeared, mail me at kenneyw@sci.kun.nl.

And thanks!
0
 

Author Comment

by:Forge
Comment Utility
Hmz. My 'Reload' didn't do what i expected it to do.. :))))
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now