The problem is that as my first executable statements I want to check if I can read from a databse. If I can't, I call MessageDlg to explain so, then I Halt;.
However, after closing the dialog, I still see the application in the tak manager (and if I stop it and re-run the application, the same thing occurs).
Any idea what I am doing wrong?
Global.ADQuery1 is an AnyDac database access component. I access the d/b by IP address. The code works fine when I set my PCs address to the d/b address and gives the reported problem when I change my IP address (hence, can't access the d/b, which throws an exception).
procedure TMainForm.FormCreate(Sender: TObject);
begin
try
Global.ADQuery1.Open('SHOW DATABASES');
except
On E: Exception do
begin
MessageDlg('Database access problem', mtError, [mbOK], 0);
Halt;
end;
end;
[update] when I run in the IDE, after catching
(EMySQLNativeException) : "[AnyDAC][Phys][MySQL] Can't connect to MySQL server on '10.21.18.211' (10060)"
I catch an EIdWinSockStubError either the program has not called wsastartup or wsastartup failed - but I don't udnertsand how it is thrown ... I guess that Application.Terminate calls may main form's FormClose, which doesn't do anything with my Indy components, but I guess that when the parent form is destroyed then its children will be too.
[further update]
My TMainForm.FormCreate now says only
Sleep(1000);
PostMessage(Handle, UM_PROGRAM_START, 0, 0);
And I moved all the code into the stat of function that handles that. Surely everything is created at that time? So, why does my Indy component throw an exception?
Maybe I should put the PostMessage() in my [application].pas after Application.Run(); ?
(Aside: 1) how do others generally handle application start in this way? 2) does anyone have an application skeleton? I was thinking of creating one with options to handle minimize to system tray, only allow one instance, recent files menu, etc, etc) - although that might be better as a separate question
The Halt procedure is not the immediate process-killer we sometimes mistake it for. It calls the unit-finalization sections of all your program's units, so your program might be stuck in one of those, perhaps waiting for something to happen to your form, which isn't going to happen since your OnCreate handler hasn't returned yet.
You could use the debugger to find out what your program is doing or waiting for.
To really get out of a program as fast as possible, skip Halt and go straight to ExitProcess. That's the final thing Halt calls.
Application.Terminate is actually farther from the point where any real termination occurs since it's really just an advisory command; the application won't terminate until it reaches the message loop.
Better yet, find a more graceful way to exit your program. For example, test your database before creating your form so you're not left in the awkward position of having a half-created form that you don't really want anymore.
Related
I am developing a Delphi 10 Seattle based application that performs multiple tasks using FireDAC's toolset to performe backup, restore and maintenance routines on Firebird databases.
The components included in this toolset (TFDIBBackup, TFDIBRestore and TFDIBValidade) all have the Verbose option, that lets you intercept the output of it and be able to output it to somewhere in order to read it.
I'm trying to output that to a memo, but I'm having some problems with it.
The main problem is: the process gets significantly slowed down the bigger the database, the more lines you output is directly related to the amount of time it takes to backup / restore the database. If you disable verbose completely the amount of time that the process takes drops down to like, 10% of the overall time. It's insane.
During my experiments I found out that if I use another thread to output the verbose to the memo, the process doesn't take that big of a hit in performance, it's like 90% of the non-verbose option, which is great. But I've ran into an issue, using threads makes the application create some sort of stack of threads that will eventually be outputted to the memo, but the problem is that the process itself is already done and the lines will keep being outputted to the memo for a long time.
If I don't use threads to do this, the process takes the big hit on it's performance and the application gets essentially locked down until the process is done.
Does anyone know of any good ways to output the verbose of this toolset without locking down the UI or cause the problem that I explained above?
I decided to implement AmigoJack's suggestion of using AllocConsole() and Writeln() to display Firebird's verbose output. It works quite well and doesn't lock my application's GUI.
To prevent the user from closing the application when closing the console you are able to get the handle to that console and then remove the close button.
You will need to declare this function first:
function GetConsoleWindow: HWND; stdcall; external kernel32;
And this is the code to allocate a new console and then hide the button:
var
handle: HWND;
consoleMenuItem: HMENU;
begin
AllocConsole();
handle := GetConsoleWindow;
if IsWindow(handle) then
begin
consoleMenuItem := GetSystemMenu(Handle, False);
if IsMenu(consoleMenuItem) then
begin
DeleteMenu(consoleMenuItem, SC_CLOSE, MF_BYCOMMAND);
end;
end;
end;
Just remember to add a way for the user to call FreeConsole() in order to get rid of the console when you don't need it anymore, or call it yourself.
I would like to terminate a Delphi application without executing any other code line and I'm wondering about which is the proper way to do this. Furthermore, I would like to know if there's something wrong in what I'm actually doing at the moment.
Basically, my code looks like this:
//Freeing all objects (Obj1.Free, etc..)
Application.Terminate;
Halt;
Is this the right way to stop a Delphi application or should it be done in another way?
Application.Terminate() breaks the message loops in TApplication.Run() and TForm.ShowModal(), allowing the main thread to exit normally, perform necessary cleanups, etc.
Vcl.Forms.TApplication.Terminate
Ends application execution.
Call Terminate to end the application programmatically. By calling Terminate rather than freeing the application object, you allow the application to shut down in an orderly fashion.
Terminate calls the Windows API PostQuitMessage function to perform an orderly shutdown of the application. Terminate is not immediate.
Terminate is called automatically on a WM_QUIT message and when the main form closes.
Halt(), on the other hand, is an immediate abnormal termination. Basically, ripping the process out of memory. Use it only in extreme situations where no other option is available.
System.Halt
Initiates the abnormal termination of a program.
Halt performs an abnormal termination of a program and returns to the operating system.
To perform a normal termination of a Delphi application, call the Terminate method on the global Application object. If the application does not use a unit that provides an Application object, call the Exit procedure from the main Program block.
I would like to terminate a Delphi application without executing any other code.
Neither Application.Terminate nor Halt will achieve that. The former performs an orderly termination. Lots of code will execute. Calling Halt is more hopeful. That is an abnormal termination. But unit finalization code is executed.
If you wish to exit as quickly as possible, executing the minimum amount of code along the way, call ExitProcess. That's the final step of Halt and by calling ExitProcess directly you avoid all the steps that Halt takes before it calls ExitProcess.
I had some problems with Application.Terminate, because I had to start the Form Close procedure, so I did only:
Form1.Close;
I found a new solution inside .dproj
begin
ReportMemoryLeaksOnShutdown := True;
Application.Initialize;
Application.CreateForm(TFormMain, FormMain);
if Not(VerifyCode()) then
begin
ShowMessage('Software unregistered!');
Application.Terminate;
end
else
Application.Run;
end.
Just to leave a point on a extra problem if code must be on main form OnCreate.
Try such code on the Main Form OnCreate event. It does not work as expected, main form is shown, then the application is finished.
To be able to see it, add another form and put on its creation a long loop.
It seems like all the Application.CreateForm on the main project source are executed.
Sample code:
procedure TMyMainForm.FormCreate(Sender: TObject);
begin
ShowMessage('[1] This must allways be shown');
if mrOK=MessageDlg('Exit?',mtConfirmation,[mbOK,mbCancel],0)
then begin
Application.Terminate;
Exit;
end;
ShowMessage('[2] This must not allways be shown');
end;
procedure TMyOtherForm.FormCreate(Sender: TObject);
begin
ShowMessage('[3] This must not allways be shown');
end;
With that code messages [1] and [3] are allways shown.
Only way to not show [3] is to call Halt.
Note: Why such code on MainForm OnCreate? Simple answer could be, the exe checks conditions to be run and see they are not meet (missing files, etc), rude one (sorry for that), just because i want/need to.
I know this is an old thread, but would appreciate comments on this addition to the thread if anyone is still listening.
For quite some time I have called Application.Terminate followed by ExitProcess(0). My theory is that Application.Terminate does the graceful cleanup, but ExitProcess prevents any other code from executing. It seems to work, and I don't get reports of memory leaks or other ill effects. Code would be something like:
Procedure (KillTheApp);
begin
Application.Terminate;
Application.ProcessMessages;
ExitProcess(0);
end;
I have an application written in Delphi that uses an iSeries ODBC connection.
There are some workstations where I do not want to install the iSeries software, and on these workstations, I won't be updating any of these databases anyway.
Is there a way I can trap when this error message is generated? At that point, I can just set a variable like NoUpload to true and not allow the connection on the workstation.
It appears to happen before I ever attempt to even open one of the tables - just by having the ConnectionString set when the application starts fires the message.
Thanks in advance!
You can check the existing ADO providers of the system with ADODB.GetProviderNames
Ideally, you should look for an option to check your condition without an exception being raised. So Sir Rufo's answer is a good place to start.
Another option might be to not include the Provider in the ConnectionString, but set it independently via the Provider property at run-time (most likely only after confirming that it's supported).
However, since you mentioned you're getting an exception before you even attempt to open a table, there are a few things to check (assuming you've been setting up your components at design time):
Have any data sets accidentally been left Active at design time?
Has the Connection been left active at design time?
Are there any options in the ConnectionString that could immediately trigger the error?
Failing the above you could provide a hook for application exceptions. (And really more of a last ditch effort.)
Declare a handler method using with the following signature: TExceptionEvent = procedure (Sender: TObject; E: Exception) of object;. And assign it to Application.OnException. E.g.
procedure Handle(ASender: TObject; E: Exception);
begin
if ISeriesNotInstalledError(E) then
begin
FNoUpload := True;
end
else
begin
Application.ShowException(E);
end;
end;
NOTE: There are some important considerations in following this approach. Since you see this as a standard Use Case, you don't want to be bothering your users with messages. This is also much better than a localised exception handler (a common programming error) because if a caller routine triggers this error you don't want the caller to mistakenly run as if nothing went wrong; when quite clearly something did.
I've got some very old code (15+yr) that used to run ok, on older slower machines with older software versions. It doesn't work so well now because if fails a race condition. This is a general question: tell me why I should have known and expected the failure in this code, so that I can recognise the pattern in other code:
procedure TMainform.portset(iComNumber:word);
begin
windows.outputdebugstring(pchar('portset ' + inttostr(icomnumber)));
with mainform.comport do
try
if open then open := False; // close port
comnumber:=iComNumber;
baud:=baudrate[baudbox.itemindex];
parity:=pNone;
databits:=8;
stopbits:=1;
open:=true;
flushinbuffer;
flushoutbuffer;
if open then mainform.statusb.Panels[5].text:=st[1,langnum] {Port open}
else mainform.statusb.Panels[5].text:=st[2,langnum]; {port set OK}
except
on E: exception do begin
windows.OutputDebugString('exception in portset');
mainform.statusb.Panels[5].text:=st[3,langnum];
beep;
beep;
end;
end;
windows.outputdebugstring('portset exit');
end;
Note that flushinbuffer is protected with EnterCriticalSection(); AFAIK Nothing else is protected, and AFAIK there are no message handling sections. BUT
When this code is called from a click event, it gets part way through, then is interupted by a paint event.
The only tracing I have done is with outputdebugstring. I can see the first string repeated on entry before the second string is shown on exit. Is that real, or is it an illusion?
The trace looks like this:
4.2595 [4680] graph form click event
4.2602 [4680] portset 1 'from click event handler'
4.2606 [4680] graph form paint event
4.2608 [4680] portset 1 'from paint event handler'
4.2609 [4680] portset exit
4.3373 [4680] portset exit
This is a race condition: The paint event handler of the form is called before the click event handler code finishes, which causes failures. Serial code is AsyncPro. No thread code. Yes, there is more code, no it doesn't do anything in particular before "portset 1" but it does write to a form before it gets there:
with graphform do begin
if not waitlab.Visible then begin
waitlab.visible:=true;
waitprogress.position:=0;
waitprogress.visible:=true;
waitprogress.max:=214;
end;
end;
mainform.Statusb.panels[5].text:=gcap[10,langnum];
Don't hold back: What is it doing wrong, what should I be looking for?
This is expected behaviour - opening or closing a TApdComPort will service the message queue, specifically by calling a function it names SafeYield:
function SafeYield : LongInt;
{-Allow other processes a chance to run}
var
Msg : TMsg;
begin
SafeYield := 0;
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then begin
if Msg.Message = wm_Quit then
{Re-post quit message so main message loop will terminate}
PostQuitMessage(Msg.WParam)
else begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
{Return message so caller can act on message if necessary}
SafeYield := MAKELONG(Msg.Message, Msg.hwnd);
end;
end;
The TApdComPort is an async component - the com port is managed on background threads and opening or closing the port requires either starting or signaling those threads to stop. While waiting for them to free the component services the message queue in case it takes some time for things to synchronize (for example) :
if Assigned(ComThread) then
begin
{Force the comm thread to wake...}
FSerialEvent.SetEvent;
{... and wait for it to die}
ResetEvent(GeneralEvent);
while (ComThread <> nil) do
SafeYield;
end;
You haven't really show us enough of your own code to say why this is problematic in your case, however. I think David's point about com ports being manipulated in a paint handler is valid... we need to see the broader picture and what, exactly, the problem is that you are having.
A standard paint event cannot happen on its own, it can only be triggered by message retrieval. So the only way the code you showed could be interrupted the way you describe is if either the Serial component itself, or an event handler you have assigned to it, is doing something that pumps the calling thread's message queue for new messages.
Since you are closing the port in the beginning of your event handler, if there is any chance of triggering the event twice (i.e. by calling Application.ProcessMessages anywhere from your code, or calling TMainform.portset() directly from a worker thread), the new instance will close your port while the older one tries to communicate trough it, which will result in an error. AFAIS there are two solutions:
The faster but least bearable one is to protect your entire function with a Mutex (or event which is not a syncronisation object but can be used as one), but this only hides the coding error you have made.
The more pro solution is to find where the race condition gets raised, then fix your code. You can do it by searching all references to Application.ProcessMessages() and TMainform.portset(), and make sure that they won't get called paralelly. If no reference can be found on either mentioned function, the problem could still be caused by running multiple instances of your code ('cause it will not create multiple com ports :) ).
Remy Lebeau gets the credit for answering the question, because, as I asked for, it was a general reply to a general question. But it would have been inadequate without his comments in response to Uwe Raabe.
And what conclusively demonstrated that Remy Lebeau was correct was the exceptional answer from J, pointing out the specific point where the code failed.
Thanks also to David Heffernan for asking "why does code that responds to WM_PAINT call portset", which also makes a general point. And yes, the quick fix was just to block the path from the paint event handler to the comms code, but I'd done that without recognising the more general point.
I'll be having a look at the comms code, to see if there are more problems like this, and I'll be looking at the event handlers, to see if there are more problems like this, so thanks to everyone who read and considered the question.
i want to halt my application when my bitmap is completely saved?
Unless you are calling that method in a separate thread I'm fairly certain that it will be completely saved at the point at which the method returns:
bitmap.SaveToFile(..);
// file is completely saved at this point
What is it that leads you to believe that it isn't completely saved at this point?
The only wrinkle might be that with "lazy writes" Windows itself might not yet have completed the business of physically writing the file to the disc, but as far as your application (or any other application) is concerned, that is irrelevant. If you attempt to access the file before Windows has completed committing it to disc that application will simply "block" until it can safely access the file, but you don't need to specifically handle this, it "just works".
If you want to halt your application cleanly there are several options:
1) Close the main form. When you close the main form, the application will close.
procedure TForm1.SaveAndClose(Filename:String);
begin
Bitmap.SaveToFile(filename);
Application.Mainform.Close;
end;
2) Call Application.Terminate. This shuts down your application abruptly, but does run any finalization code. It is normally invoked when the application receives a WM_Quit message or the main form closes.
procedure TForm1.SaveAndClose(Filename:String);
begin
Bitmap.SaveToFile(filename);
Application.Terminate;
end;
3) Post wm_Close to your application/main form. This has the added advantage that any other messages in queue for the application are processed. It is equivalent to pressing CTRL-F4 or pressing the "X" in the upper right on your main form. The only issue is that if the message queue is full then it may not ever reach the form (rare).
procedure TForm1.SaveAndClose(Filename:String);
begin
Bitmap.SaveToFile(filename);
postMessage(Application.MainForm,wm_close,0,0):
end;
4) Call Halt( ExitCode:Integer ). This is a bit more extreme and will terminate the application. Finalization code will still be run, but memory is not guaranteed to be freed. Optionally you can set an exit code that will be returned back to the calling application.
procedure TForm1.SaveAndClose(Filename:String);
begin
Bitmap.SaveToFile(filename);
Halt(0);
end;