Get window to refresh (etc) without calling Application.ProcessMessages? - delphi

I've got a legacy app here that has a few 'time-consuming' loops that get fired off as a result of various user interaction. The time-consuming code periodically updates something on the screen with progress information (typically a label) and then, seemingly to persuade the visual refresh to happen there-and-then, the code calls Application.ProcessMessages (argh!).
We all know now what kind of trouble this can introduce to a GUI app (being charitable, it was a more innocent time back then) and we're finding that sure as eggs, from time to time we get users achieving the impossible with the program because they're clicking on controls while the program is 'busy'.
What's the best way of periodically refreshing the visuals of the form without taking-on other events/messages etc?
My thoughts were to either;
- disable all of the controls before doing anything time-consuming, and leaving the '...ProcessMessages' calls in place to 'force' the refresh, or
- find another way to refresh a control periodically
I can do the former but it left me wondering - is there a better solution?
example code from legacy;
i:=0;
while FJobToBeDone do
begin
DoStepOfLoop;
inc(i);
if i mod 100 = 0 then
begin
UpdateLabelsEtc;
Application.ProcessMessages;
end;
end;
I can already hear you all fainting, at the back. :-)

If you call Update() on the controls after you have changed properties you will force them to redraw. Another way is to call Repaint() instead of Refresh(), which implies a call to Update().
You may need to call Update() on parent controls or frames as well, but this could allow you to eliminate the ProcessMessages() call completely.

The solution I use for long updates is by doing the calculations in a separate thread. That way, the main thread stays very responsive. Once the thread is done, it sends a Windows message to the main thread, indicating the main thread can process the results.
This has some other, severe drawbacks though. First of all, while the other thread is active, you'll have to disable a few controls because they might restart the thread again.
A second drawback is that your code needs to become thread-safe. This can be a real challenge sometimes. If you're working with legacy code, it's very likely that your code won't be thread-safe.
Finally, multi-threaded code is harder to debug and should be done by experienced developers.
But the big advantage of multi-threading is that your application stays responsive and the user can just continue to do some other things until the thread is done. Basically, you're translating a synchronous method into an asynchronous function. And the thread can fire several messages indicating certain controls to refresh their own data, which would be updated immediately, on the fly. (And at the moment when you want them to be updated.)
I've used this technique myself quite a few times, because I think it's much better. (But also more complex.)

Do not call Application.Processmessages, This is slow and might generate unlimited recursion.
For example, to update everything in Panel1 without flick, we can use this method:
procedure TForm1.ForceRepaint;
var
Cache: TBitmap;
DC: HDC;
begin
Cache := TBitmap.Create;
Cache.SetSize(Panel1.Width, Panel1.Height);
Cache.Canvas.Lock;
DC := GetDC(Panel1.Handle);
try
Panel1.PaintTo(Cache.Canvas.Handle, 0, 0);
BitBlt(DC, 0, 0, Panel1.Width, Panel1.Height, Cache.Canvas.Handle, 0, 0, SRCCOPY);
finally
ReleaseDC(Panel1.Handle, DC);
Cache.Canvas.Unlock;
Cache.Free;
end;
end;
For better performance, the cache bitmap should be created at first and free when the process has finished

The technique you're looking for is called threading. It is a diffucult technique in programming. The code should be handled with care, because it is harder to debug. Whether you go with threading or not, you should definitely disable the controls that users should not mess with (I mean the controls that can effect the ongoing process). You should consider using actions to enable/disable buttons etc...

Rather than disable the controls, we have a boolean var in the form FBusy, then simply check this when the user presses a button, we introduced it for the exact reasons you mention, users clicking buttons while they wait for long running code to run (it scary how familiar your code sample is).
So you end up with something like
procedure OnClick(Sender:TObejct);
begin
if (FBusy) then
begin
ShowMessage('Wait for it!!');
Exit;
end
else FBusy := True;
try
//long running code
finally
FBusy := False;
end;
end;
Its impotent to remember to rap the long running code up in a try-finally block in case of exits or exception, as you would end up with a form that will not work.
As suggested we do use threads if its for code that will not affect the data, say running a report or data analysis, but some things this is not an options, say if we are updating 20,000 product records, then we don't want anyone trying to sell or other wise altering the records mid flight, so we have to block the application until it is done.

Related

Capturing all windows messages to an application

Is there a global WinProc for all windows in an application or a way to capture all paint messages to the application for a brief period of time? Some of the third party libraries that we use add their own wndproc's to controls (e.g. DevExpress ribbon, Docking Manager).
We have some code that should be run in a background thread but that's not possible at the moment. This code can take a long time to run and obviously the application becomes unresponsive during this time. We can't use things like processmessages because that would result in the code being reentered. To get around this I thought it should be possible to create our own version of ProcessMessages that looks for specific messages. The idea is that I would create a small panel with a progress bar and a cancel button and then only allow messages for the cancel button's windows handle.
The simplified code to process a single message looks something like this:
begin
if PeekMessage(aMsg, 0, 0, 0, PM_NOREMOVE) then
begin
Unicode := (aMsg.hwnd = 0) or IsWindowUnicode(aMsg.hwnd);
if Unicode then
MsgExists := PeekMessageW(aMsg, 0, 0, 0, PM_REMOVE)
else
MsgExists := PeekMessageA(aMsg, 0, 0, 0, PM_REMOVE);
if MsgExists then
begin
Result := True;
if aMsg.hwnd = aButtonWindowsHandle then
begin
TranslateMessage(aMsg);
if Unicode then
DispatchMessageW(aMsg)
else
DispatchMessageA(aMsg);
end
else
begin
// WM_PAINT is a special message. If you don't handle a WM_PAINT
// then windows will just stick it back in the queue again.
if (aMsg.message = WM_PAINT) and (aMsg.hwnd <> 0) then
begin
// Queue this paint message so we do an update when the
// processing is finished.
FQueuedPaintMessages.Add(aMsg);
// Paint nothing here otherwise Windows will send it again and
// insist that we paint it.
if BeginPaint(aMsg.hwnd, paintStruct) <> 0 then
EndPaint(aMsg.hwnd, paintStruct);
end;
end;
end;
end;
end;
This is called as follows:
while ProcessMessage(msg) do {loop};
This appeared to work correctly but during testing I picked up a couple of cases where one of the Windows API calls (PeekMeesage, TranslateMessage, DispatchMessage, or the two painting calls) is triggering a call back to our docking control's WndProc via KiUserCallbackDispatcher in ntdll.dll. The docking control is ultimately triggering a paint which is a problem. I am not sure what Windows API call it is because the stack trace is missing some lines at that point. It only happens on one of our test machines so I can't put in a breakpoint.
I am aware that this is far from an ideal solution and that we would be better off rewriting the code so that it can be run in a background thread but that would be a huge amount of work and is not feasible at this time.
It looks like SetWindowsHookEx with WH_MOUSE_LL might be a solution but that still needs a message loop.
Not so much an answer to your question than maybe a potential solution to your problem...
Instead of having a panel with a "cancel" button, instead display a label saying "Press and hold ESC to cancel process". Then, within your process you can get the status of the ESC key by calling GetAsyncKeyState at regular interval. I did not test it, but I'd expect it to work at least "well enough" for your needs.
The selected answer gave me a solution to my problem but #IInspectable answered the question about catching all of the paint messages. So for other people looking at that question. The answer is this:
"None of this can be made to work reliably with reasonable effort. It's just a clumsy attempt at fighting the system, and the system is going to win that one."
In other words. Don't try and do it.

What is the best way to output verbose to a memo without locking down the application

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.

Delphi 7 Occasional deadlock changing TLabel.Font.Style from IdHTTPListener event

Wondered if anyone could help with a really tricky sporadic (not consistently reproducible) issue - possibly to do with threads. I'm in Delphi 7 (it's old code...), running an IdHttpListener (Indy). Code below is copied from a large cumbersome app - but hopefully enough to explain. An incoming HTTP request runs the following event - where web_lock is a TCriticalSection I define at the top. I do it in a critical section as the web requests causes changes that I need to be atomic.
procedure TFWebServer.WebServerCommandGet(Thread: TIdPeerThread;
RequestInfo: TIdHTTPRequestInfo; ResponseInfo: TIdHTTPResponseInfo);
var S,PageString : string;
begin
web_lock.Acquire;
PageString:=' ';
if (copy(RequestInfo.Document,1,15)='/_Request_Part_') then begin
s:=copy(RequestInfo.Document,16,length(RequestInfo.Document));
FMainGui.doFunction(s);
PageString:='OK';
end; // Lots more else cases here...
ResponseInfo.ContentType:='text/plain';
ResponseInfo.ResponseNo:=200;
ResponseInfo.ContentStream:=TMemoryStream.Create;
ResponseInfo.ContentStream.Write(PageString[1],length(PageString)*
sizeof(PageString[1]));
ResponseInfo.ContentLength:=length(PageString)*sizeof(PageString[1]);
web_lock.Release;
end;
and then, my FMainGui.doFunction(s) does something like this:-
procedure doFunction(s : String);
var i,j : integer;
begin
i:=strtoint(s);
for j:=1 to Pages do begin // Pages is dynamic - but correctly set
if (j=i) then // Pagelabs[j] is always a visible TLabel
Pagelabs[j].Font.Style:=[fsBold]
else Pagelabs[j].Font.Style:=[];
end;
end;
Bit simplified - Pagelabs is a set of dynamically created TLabels that I display on a page, and the one you've selected using the web request gets made bold.
THE PROBLEM: Occasionally, and unpredictably, I get some kind of deadlock dealing with the web request - it just freezes, with circular swirly mouse pointer, and doesn't recover. If I'm debugging it in Delphi, the call stack is empty, and I can only step through the assembly code - afraid I'm not up to working what that means! I tracked it down to the Pagelabs[j].Font.Style:=[fsBold] line above, by writing one line text files between every single line of code... so while the error was sporadic, when it did occur, it was always that line it locked up on.
I appreciate this is a snippet of a large app, but is there anything obvious I'm doing wrong? Eg - should it be safe to change GUI properties from a thread triggered by the HTTP Listener? Or is there something different I should be doing?
Any ideas much appreciated,
Thanks,
Wes
You cannot manipulate UI controls from worker threads, only from the main GUI thread!
You have two options:
You may block your HTTP threads and temporary switch to main thread by TThread.Synchronize() procedure. Like in this example:
http://docwiki.embarcadero.com/CodeExamples/Seattle/en/Synchronize_(Delphi)
You may prefer delayed out-of-sync execution via
Windows messages ( use PostMessage and avoid SendMessage )http://www.cryer.co.uk/brian/delphi/howto_send_custom_window_message.htm
AsyncCall library http://andy.jgknet.de/blog/bugfix-units/asynccalls-29-asynchronous-function-calls/
thread-queues approach with something like OmniThreadsLibrary
First option might slow you down, for any long processing in the GUI thread would also freeze your HTTP handlers.
Second options would require making a "snapshot" copy of any required data and passing it along with the deferred call request ( as the VCL update code might be executed at any random time AFTER (or during) the HTTP handler (or several HTTP handlers). It would be normal when several HTTP handlers would request GUI updates and you would have to check which of them passed the most recent data and skip other requests for example.

Delphi idle handler only fires when I move the mouse

I have an OnIdle handler in my D2006 app. With this code:
procedure TMainForm.ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
begin
Inc (IdleCalls) ;
Sleep (10) ;
Done := False ;
end ;
the app runs smoothly, the idle handler is called 100 times per second, and the CPU usage is next to zero.
I then added a TActionList and connected up some controls to actions, coded an Execute and Update handler.
procedure TMainForm.ActionNewButtonExecute(Sender: TObject);
begin
DoNewProject ;
end ;
procedure TMainForm.ActionNewButtonUpdate(Sender: TObject);
begin
ActionNewButton.Enabled := AccessLevelIsSupervisor ;
end;
Problem. The OnUpdate event doesn't fire. On a hunch I set Done := true in the OnIdle handler and the OnIdle handler is then only called when I move the mouse. And the Update action still doesn't fire.
Why might the Update handler not be firing, and should I set Done to true or false? Or both?
Use the source, Luke. :)
Look at the Forms unit, specifically TApplication.Idle. It contains, in part, the following:
Done := True;
try
if Assigned(FOnIdle) then FOnIdle(Self, Done);
if Done then
if FActionUpdateDelay <= 0 then
DoActionIdle
// Excluded to avoid copyright violation
// See also the else portion, which contains (in part)
else
if IdleTimerHandle = 0 then
begin
IdleTimerHandle := SetTimer(0, 0, FActionUpdateDelay, IdleTimerDelegate);
if IdleTimerHandle = 0 then
DoActionIdle
end;
finally
// Omitted
end;
As you can see, DoActionIdle is only called when either Done = True and FActionUpdateDelay <= 0 or IdleTimerHandle = 0. DoActionIdle (also part of TApplication) is what calls UpdateAction. So if neither of the above conditions are met, TAction.OnUpdate is never called.
There's a separate method, TApplication.DoMouseIdle, that you may want to peruse as well.
As mentioned in the comments, Sleep in the idle handler will do no good, also the bacground processing will stall if there is no activity on the application.
You can however lower the CPU usage w/o much disturbing effects: After processing all OnIdle events, the application will call WaitMessage (which will sleep while the message queue is empty), if the Done parameter is True - you can just unconditionally set it in your handler.
As for background processing, use either a thread and call back to the main thread via Synchronize or, if you really-really have to, use a timer and don't ever forget to handle reentrancy (both solutions will by the way wake the application even while WaitMessage).
Get rid of that OnIdle event handler, you accepted it is there just in case.
If you later need to perform background tasks, learn how to use threads. To get a specific frequency, you're allowed to use sleep or any other technique within a thread.
My advice is in this way because, as you see, that way of do things is interfering with other parts of your application. If it is a bug in the TApplication, I don't know, maybe it is. If you want to investigate more, make a copy of your project, check everything and if you think this have to work another way, fill a QC entry about that.
I was looking the XE source code and it seems Ok, they set an event to update the actions if the Idle event is not done.. I don't see a bug there. I have no pre-2010 ready installations to check ancient versions.

Detect if an application is not in use

How can I detect if an application is not used for more than x minutes in DELPHI
If you write Windows app take a look at GetLastInputInfo function.
Here is some code that looks for mouse and keybord activity with the applicatin
procedure TUserActivity.AppMessage(var Msg: TMsg; var Handled: Boolean);
begin
Handled := False;
case Msg.message Of
WM_KEYDOWN,
WM_LBUTTONDOWN,
WM_MBUTTONDOWN,
WM_RBUTTONDOWN:
Activity := TRUE;
WM_MOUSEMOVE:
begin
if (LastXYPos <> Msg.lParam) then
Activity := True;
LastXYPos := Msg.lParam;
end;
end;
end;
Use the Application.OnIdle event:
Write an OnIdle event handler to perform special processing when an application is idle. An application is idle when it is not processing code. For example, an application is idle when it is waiting for input from the user. 
OnIdle is called only once, as the application transitions into an idle state. It is not called continuously unless Done is set to false. Applications that set Done to false consume an inordinate amount of CPU time, which affects overall system performance.
Use either a timer or GetLastInputInfo as #aku suggests in this event to determine if you can start your maintenance without interrupting the user
Use the applications OnDeactivate and onActive events..
That way you can abort the longrunning job if the user activates your program again.
ex:
Application.OnDeactivate = yourDeactivProcedure;
procedure mainform.YourDecativateProcedure (sender : tObject);
begin
// do your job..
end;
To handle the activate event to abort you either have to do it a bad way with a sleep and after the sleep check if i global vairable has been set.
Or you can have a theared object that does the loongrunning job.
Which I would say is much better. You can set the loongrunningjobs priority to low and it wont affect your program as much,
Depends on how you're defining "used" -- if you were monitoring yourself, you could look at the last time you responded to user interaction by logging it when it happened (mouse move/key pressed/menu event fired/etc.). Monitoring another application is tricky as it'll be harder to define that it is "in use".
That really depends on the application and what it does. While users may not interact with it in the sense of new input, they certainly might be viewing the client area that is visible.
Also - you don't say if you want to detect this internal to the app or external to the app.
Simple methods
see if it has current focus.
check if the window is visible
lots of others too, but they rely on the app itself.
You must define what you mean by "used" as well. It could mean different things and that would make significant changes to how you determine whether it met your criteria or not.

Resources