The Application.Run procedure calls an infinite loop that handles windows messages:
repeat
try
HandleMessage;
except
HandleException(Self);
end;
until Terminated;
The Terminated property can be set to true only through Application.Terminate procedure, which sends PostQuitMesage.
I would like to change the message handling loop so that I can directly stop it using the global variable (without using messages queue):
var MyTerminated:Boolean
....
repeat
try
HandleMessage;
except
HandleException(Self);
end;
until Terminated or MyTerminated;
The question is, is it possible to make the program use your own version of Application.Run?
"Terminated" property is read-only. However it is a direct getter of the FTerminated field, therefore Application.Terminated directly reads from the boolean field. While the language disallows setting Application.Terminated, you can set the boolean value at that address using a pointer:
PBoolean(#Application.Terminated)^ := True;
You may also consider using Halt, which will pass over the message loop completely, for a more abrupt but less hacky solution.
Yes, you can make your application use own version of Application run, but this practice is discouraged, because it changes normal program flow, designed by the architects of Delphi.
Directly stopping Application.Run signifies that there is a need to restart Application.Run later, for example, after some action that is wanted to be done from the main thread. This makes the program puzzled, harder to understand by peer programmers and more error prone as a whole.
The program design should be simple and straightforward. If an application is big, for example two million lines of code, the overall design of the execution flow should be simple anyway:
If you need to do some longer actions, do them from the worker threads;
If you need to do an instant actions, do them from your main form or from the other forms.
So a Delphi application main loop should only be exited on the overall application exit, which is done by the PostQuitMessage. We don't have to avoid this message.
The reason why PostQuitMessage is wanted to be avoided, is probably an instantaneous exit. This is not how VCL applications are supposed to run. If one doesn't need forms (for example for a Windows Service application), just don't use the TApplication class an don't run forms, just make your own message loop based on MsgWaitForMultipleObjects.
Related
To demonstrate the problem I created a simple application that consists of 4 blank forms. One of them (Form1) is the main and shows automatically. Showing other forms I put in the code project:
…
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TForm2, Form2);
Application.CreateForm(TForm3, Form3);
Application.CreateForm(TForm4, Form4);
Form2.Show;
Form3.Show;
Form4.Show;
Application.Run;
end.
On the second form (or any other) Form2 I put a button that, when clicked, synchronously executes long (>5 seconds) SQL query (any, I open the stored procedure). Is it synchronous, that is, the app hangs for at least 10 seconds. At this time triggers the windows ghosting. Windows creates Windows Ghost for all Windows stale apps that you can minimize, move and close. After executing a query, the application resumes processing messages and everything returns to its original state.
It must be so, but for me it is not. In fact, after defrosting program Z-order of the Windows is changed, the order of the Windows mixed. Moreover, the top (or bottom) can get out of any window, I have not noticed any pattern. It seems that Windows does not correctly restore the Z-order when cancelling the ghosting.
This behavior is also found in Delphi 2007. Note that under the Delphi debugger mode ghosting is disabled and you will not see this behavior. Run the application without debugger to see this behaviour.
After disabling mode ghosting with DisableProcessWindowsGhosting - everything works correctly.
But I don't want to completely disable this mode, the program often hangs for more than 5 seconds, and this mode is useful: the user is still nicer to look at Windows that somehow redrawn than not redrawn at all. To rewrite half the program code for asynchronous execution of queries is very long and time-consuming task, I'm afraid we don't have enough resources, although it is undoubtedly the right approach.
MainFormOnTaskBar property has nothing to do with my question, there is no such property in delphi 7.
Does this effect anyone else besides me? If not – where did I go wrong? How to force Windows to properly reconstruct the sequence of Windows?
There is exactly one way to avoid your application becoming unresponsive. Don't block the main thread.
You are asking how to block the main thread, and keep the application responsive. The only way for an application to remain responsive is for it to process its message queue in a timely fashion. You simply cannot have it both ways. You can't both process messages and not process messages.
Probably the z-order gets changed because your application is not able to process the various clicks you make while the application is not processing messages. And then processes them when the application starts processing messages again.
Conclusion: if you want your application to remain responsive, arrange for the long running tasks to run asynchronously with respect to the main thread.
Disable the windows ghosting "feature" completely and it will fix the z-order issue (and other annoyances of this feature).
The Windows API function to call is named DisableProcessWindowsGhosting, in user32.dll: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-disableprocesswindowsghosting
Allen Bauer, former Delphi architect, described why this z-order issue happens. The page is not available anymore, but WayBackMachine comes to the rescue:
https://web.archive.org/web/20161128155906/https://community.embarcadero.com/blogs/entry/popupmode-and-popupparent-295
This function is not imported in Winapi.Windows.pas (at least not up to Delphi 10.2), so you need to do it yourself. Declare a procedure that imports user32.dll's DisableProcessWindowsGhosting function dynamically and call it before your application starts, like this:
procedure DisableProcessWindowsGhosting;
var
DisableProcessWindowsGhostingProc: procedure;
begin
DisableProcessWindowsGhostingProc := GetProcAddress(GetModuleHandle('user32.dll'), 'DisableProcessWindowsGhosting');
if Assigned(DisableProcessWindowsGhostingProc) then
DisableProcessWindowsGhostingProc;
end;
begin
// Disable windows ghosting feature (XP and newer)
DisableProcessWindowsGhosting;
Application.Initialize;
...
end.
my application uses two self-written components that perform actions during the "loaded" procedure.
The component created first is an single-instance control that should exit the application when another instance is found.
The component created secondly is my database access that establishes the database connection in the "loaded" procedure.
I try to end the application before anything else happens, if a first instance has been found.
Tryout 1:
In my instance control, i call "Application.Terminate" to stop my application. That does not work - my database connection still gets established. Seems that "Application.Terminate" uses a "PostMessage" call to itself, which won't get read until the message gets received (which will happen after all components are loaded...). So obviously, this solution does not work for me.
Tryout 2:
In my instance control, i call "Halt" to stop my application. This immediately stops my application, but seems to create a load of memory leaks - at least that's what delphi's "ReportMemoryLeaksOnShutdown" tells me. Afterwards, i get this annoying "This application has stopped working" windows dialogue. This solution does not work for me.
Question A:
Is there another way to end an Application?
Question B:
Does Delphi's memory leak report work correctly when using "Halt"? Are there really memory leaks after exiting the Application with "Halt"?
If Application.Terminate terminates too late, and you want to terminate post haste, then Halt seems to me to be the appropriate course of action. I don't particularly see why Halt should lead to This application has stopped working exceptions. That suggests that your program's unit finalization code is raising exceptions which are not handled. You could perhaps try to work out (using the debugger, say) why these exceptions are raised and protect against it. Or you could avoid unit finalization with a call to the even more brutal ExitProcess.
There will indeed be memory leaks when you call Halt. However, these can safely be ignored because the operating system reclaims all allocated memory when a process terminates. In other words, the memory is leaked in the sense that is not freed by the application, but the operating system frees it immediately in any case.
However, whether or not a brutal Halt is the correct course of action for you, I cannot say. That's for you to decide. FWIW, a call to Halt from inside a component's Loaded method sounds exceptionally dubious to me.
If all you want to do is ensure that just a single instance of your program runs, then you can make simple modifications to the .dpr file to achieve that. That subject has been covered over and over again here on Stack Overflow. You'll find good coverage and advice in Rob Kennedy's answer here: How can I tell if another instance of my program is already running?
I have an application that uses tabs like the Chrome browser. Now I want to be able to open more forms and not be limited to only one form. These forms should act the same but if I close main form all forms are closed. How can I make all forms be equal, so no matter which form I close it only closes that form and not exit application before all forms are closed? Any ideas?
Image of my explorer
Kind Regards
Roy M Klever
It's not too hard to do this, though it starts getting complicated quickly depending on how complete you want it to be. Getting multiple modal dialogs to work independently is a ton of effort.
To start, you need to avoid Application.MainForm entirely. Always use Form := TMyForm.Create(Application) instead of Application.CreateForm(TMyForm, Form). The later sets MainForm and you never want that to happen.
To make things shut down properly you'll need to do something like this in your form's OnClose event handler:
if Screen.FormCount = 1 then
Application.Terminate;
CloseAction := caFree;
Application.Run relies on MainForm being assigned, so in your DPR replace that line with this loop:
repeat
try
Application.HandleMessage;
except
Application.HandleException(Application);
end;
until Application.Terminated;
There are a couple of ways to handle the taskbar entry.
Single taskbar entry: Set Application.MainFormOnTaskbar := False; and the hidden TApplication handle will be used. Clicking on the taskbar entry will bring all of the windows to the front. You'll need to override Application.OnMessage or add a TApplicationEvents component, and watch for WM_CLOSE with the Msg.Handle = Application.Handle`. In that case the user has right-clicked on the taskbar and selected Close, so you should close all the windows.
Multiple taskbar entries: Set Application.MainFormOntaskbar := True. Override your form's CreateParams method and set Params.WndParent := 0;. Each taskbar entry will control that form.
There are probably a few other gotchas, but that's the basics.
As I said, making ShowModal and TOpenDialog/TSaveDialog working independently, so it only affects its parent form and so multiple dialogs can be open at once, is a ton of work, and I can't really recommend it. If you're a masochist, here's the general steps:
Replace TCustomForm.ShowModal with a custom version. Among other things, that routine disables all the other windows in the application, so you need to replace the DisableTaskWindows/EnableTaskWindows calls with EnableWindow(Owner.Handle, False/True) to just disable the parent form. At this point you can open multiple dialogs, but they can only be closed in last-in, first-out order, because the calls end up being recursive. If that's fine, stop here.
There are two ways to work around that:
Rather than making ShowModal blocking, have StartModal and EndModal routines that have the first bit and last bit of ShowModal's code and call an OnShowModalDone event when the dialog is closed. This is kind of a pain to use, but is relatively easy to code and easy to make stable.
Use the Windows fiber routines to swap out the stack and start a new message loop. This approach is easy to use, because ShowModal is blocking, so you call it like normal. This is the approach we used in Beyond Compare. Don't do it. It's complicated to write, there will be stability issues for non-trivial applications because of incompatibilities with third party code (Windows global message hooks, TWebBrowser, .NET in shell extensions loaded by the browse dialog, etc), and if it's a cross-platform project, the Unix ucontext functions aren't safe to use either.
The common dialogs (TOpenDialog, TColorDialog, etc), have similar restrictions. To make them only disable the parent form you need to override TCommonDialog.TaskModalDialog and replace the DisableTaskWindows/EnableTaskWindows calls there too. They can't be made asynchronous like the regular Delphi dialogs above though, since they're blocking functions provided by Windows (GetOpenFileName, ChooseColor, etc). The only way to allow those to close in any order is to have each dialog run in a dedicated thread. Windows can handle most of the synchronization to do that, as long as you're careful about accessing the VCL objects, but it basically involves rewriting large portions of Dialogs.pas.
If you really want that,
1) use a small, maybe hidden, MainForm and launch just the first childform at startup.
2) launch separate applications instead of Windows in the same process. This is what later Office version use.
Here is a similar StackOverflow question:
Multiple app windows activation not working correctly
In my case I don't try to avoid the MainForm like Craig describes. Instead, I hide the main window and all of my real windows are other non-modal forms. I have been happy with how my application works, but Craig's approach may be simpler.
See my answer on the above question to see code samples for my approach and a few links with good background information.
The first form created in a Delphi application is treated as the main form and the application terminates when this form gets closed. The obvious solution is to have a first form that is not one that gets closed by the user but rather one that is not visible to the user and gets closed only when all other forms have been closed.
I have not tried this, but it should work.
This is too late to be an answer but I bumped into the same problem. The solution I opted for is to extract Application.ExeName and pass it to a function like createProcess or even shellExecute. So, now I have independent applications at the OS Level. I also needed different taskbar buttons for the different instances.
I have written an application (using Delphi 2009) that allows a user to select a series of queries which can be run across a number of different systems. In order to allow queries to be run concurrently, each query is run in its own thread, using a TADOQuery object. This all works fine.
The problem that I have is when I try to close the application when a query is still running (and therefore a separate thread is active). When I create each thread, I record the thread's THandle in an array. When I try to close the application, if any threads are still running, I retrieve the thread's handle and pass it to TerminateThread, which should theoretically terminate the thread and allow the application to close. However, this doesn't happen. The main form's onClose event is triggered and it looks like the application is closing, but the process remains active and my Delphi interface appears as though the application is still running (i.e. "Run" button greyed out, debug view active etc.). I don't get control back to Delphi until I manually end the process (either Ctrl-F2 in Delphi or via Task Manager).
I am using TerminateThread because the query can take a long time to run (a few minutes in cases where we're dealing with a million or so records, which in the end user environment is perfectly possible) and while it is running, unless I'm mistaken, the thread won't be able to check the Terminated property and therefore won't be able to end itself if this were set to True until the query had returned, so I can't terminate the thread in the usual way (i.e. by checking the Terminated property). It may be the case that the user wants to exit the application while a large query is running, and in that case, I need the application to immediately end (i.e. all running threads immediately terminate) rather than forcing them to wait until all queries have finished running, so TerminateThread would be ideal but it isn't actually terminating the thread!
Can anyone help out here? Does anyone know why TerminateThread doesn't work properly? Can anyone suggest anything to get the threads running large ADO queries to immediately terminate?
You should be able to cancel an ADO Query by hooking the OnFetchProgress event, and setting the Eventstatus variable to esCancel. This should cause your query to terminate and allow the thread to close gracefully without having to resort to using TerminateThread.
Instead of using threads with TADOQuery, maybe you should consider using the async options of ADO.
ADOQuery1.ExecuteOptions := [eoAsyncExecute, eoAsyncFetch, eoAsyncFetch];
Then when your application close, you can call :
ADOQuery1.cancel;
As you can read in the msdn using TerminateThread is dangerous.
TerminateThread is a dangerous
function that should only be used in
the most extreme cases. You should
call TerminateThread only if you know
exactly what the target thread is
doing, and you control all of the code
that the target thread could possibly
be running at the time of the
termination.
But it also is very effective in killing threads. Are you sure you are right in your conclusions? Maybe the thread is killed, but another thread is still running? Maybe your handles are not thread handles? Could you show us some code? Or even better: A small working example we could try for ourselves?
The Application.ProcessMessages command is well known and I use it in long processes to ensure my program will not tie up the computer.
But I have one fairly quick set of processing, where I am buffering a view into a file. During the buffering procedure, a few system messages may get sent off (e.g. redraw or scrollbar move or other events). I want to prevent these from getting handled by ProcessMessages until my buffering is complete.
Is there any way to either:
Prevent Application.ProcessMessages until my procedure is complete, or
Trap all messages generated during my procedure, and not release them until the end of the procedure.
Allowing the ProcessMessages to continue even if it sends messages you don't want should not be classed as problematic. With a bit of code refactoring, you could move the buffering method into a separate thread and go from there.
If you are attempting to copy the "visual contents" of a control into a file,
look at the WM_PRINT(xxx) message which allows child controls to paint themselves into bitmaps
try the LockWindowUpdate Win32 API method call which will turn off all painting messages to that control
override the WndProc/DefaultWndProc method on your control class or even the parent class if you need to and simply return "true" for each message sent
override specific control methods (such as "scroll bar moved", "OnPaint", "OnPaintBackground" etc) on the control class or even the parent and simply do nothing if your buffering is in progress
Overriding the WndProc or DefaultWndProc and simply returning true for each message essentially "turns off" ProcessMessages but it's not safe to do it this way because the control might need to process one or more messages to function correctly.
Turning off ProcessMessages is not possible (without rewriting the VCL code for message processing) because of the fact that it's part of how the VCL form's message loop has been constructed.
Trap all messages generated during my procedure, and not release them
until the end of the procedure.
There is a dirty hack you can do (only if you can not come up with a better way):
You can watch (trap) any messages by using Win32 Hooks.
Specifically, use SetWindowsHookEx with WH_CALLWNDPROC as the idHook value.
You can then record them in a list/queue and resend them when you want.
I learned way back in Windows 2 that windows messages will happen at times you don't expect them. Any part of a library can cause your app's message processing to happen. Rather than hold back the tide, make your code robust against the situation. This may be as simple as usinga a BeginUpdate/EndUpdate pair, or more complex (using a temporary and doing the final update at the end).
At a pedantic level, the way you "prevent" Application.ProcessMessages is to not call any code that
shows a modal dialog
calls SendMessage
runs its own local message loop
calls Application.ProcessMessages (which is a local message loop)
If you write a loop that does nothing but numerical calculations and file I/O, your UI will be frozen until you exit the loop because no messages are being processed.
If you want your UI to be responsive during some long-running operation of unknown arbitrary code (third party library) but you don't want certain kinds of actions to occur in your app during that time, that's a different problem - that's about preventing reentrancy. You want to prevent some parts of your code from being used while a particular activity is in progress. For example, modal dialogs prevent you from interacting with the app windows underneath the dialog by disabling all the app's top level windows except the modal dialog itself.