Delphi OLE Automation freezing GUI - delphi

We are using some OLE automation in Delphi 7 to open a word document, then once loaded, save it, and load it into a database.
This is working fine, but part of the requirement is to have a progress bar whilst the OLE bit is taking place, and also a timeout if the OLE part takes too long.
Problem we are having is that the entire GUI freezes whilst the OLE is taking place. The progress bar does nothing, then shoots up right at the end.
Any ideas on how we could approach this?

I think this is going to be difficult to do cleanly. So far as I know, Word automation doesn't give you the opportunity to cancel long running events. It also doesn't notify you of progress.
Probably the best that you can do is first of all move the automation into a separate thread. Then throw up a marquee progress bar whilst the long running automation is in progress. At least that will let the user know that something is happening.
As far as cancelling goes, you can let the user cancel from your progress dialog and then have your program continue. You could kill the automation thread, but that would leave Word in a bad state. I'd just let it continue to completion, but then ignore the results. From the user's perspective this will meet your goals reasonably well, even if it's a little dirty behind the scenes.

Related

Indy Telnet SendString robustness best practice

I have a simple Firemonkey application with a series of buttons and sliders (track bar) that when a user interacts with these controls it send a command using IdTelnet. i.e.
IdTelnet1.SendString(' '+str+#13);
The problem is that very occasionally the users experience total lockup of the app and windows reports the app as "Not responding" when clicking on something. I have not reproduced this myself however I suspect its related to clicking on buttons in quick succession.
My current theory is that I am getting re-entry into SendString and IdTelnet is not handling this well.
Before jumping into writing a queued sending system I wanted to ask for any best practice advice. Is this a known issue and what is the best way to do quick fire sends that are user initiated?

Stopping "Not responding"

I have a backup process copying a lot of files (CopyFileEx) and every few minutes, windows seems to need a break, probably paging-out or clearing buffers or something. During this time, the main form blinks and displays "Not responding" in the main Caption.
After a few seconds it blinks again, the message has gone and all is well with the World and the process continues.
Is there some way I can stop the blink and message?
"Not Responding" has a very specific meaning: The application's UI thread is not continuing to pump messages. The reason Windows calls this "Not Responding" is because the message pump is what sends user input to the application for your code to respond to. Once you understand this, the way to keep the application responsive is obvious: don't run long, blocking operations in the UI thread. Actually implementing that can be tricky, for two reasons.
First, feedback: If you're doing a long operation in a separate thread, you want it to report back to the user on its progress, which can only be done in the UI thread. So you need to use TThread.Synchronize or TThread.Queue or something similar (shameless self-promotion) to post back UI updates to your main thread.
Second, if you have a "copy everything" button in the UI, and it takes a minute to run, and it's running on a different thread but you're keeping the main thread responsive, what's stopping the user from clicking the "copy everything" button again before the task is complete? This may or may not be a valid operation according to your program design, but it it isn't, it can cause problems. Make sure that, if this can be a problem, that your design compensates for it, for example by disabling the button after you click it and re-enabling it after it completes.
Finally, if you're going to do anything complex at all with multithreading, I'd highly recommend you take a good look at OmniThreadLibrary... except that the tag says you're using Delphi 5, which can't run OTL. You really ought to take a look at updating.

Anything else I can use to keep program responsive besides processmessage?

I have an application that can run for quite a long time scanning a database.
During this process I keep my program responsive by using processmessage.
This processmessage is triggered when my progress bar is updated and inc'ed.
This works fine is most cases, but when the databases get larger it takes longer for the progress bar to jump up 1%, the program becomes unresponsive until that time.
Is there another way to keep my program alive besides processmessages?
Multi threading is the answer. A standard Delphi application is basically a single threaded application that can do one thing at a time. Hence the gui lockup, it can't remain responsive if it's doing something else.
If you want to have a responsive gui and do heavy lifting at the same time, you need to have the heavy lifting in a separate thread or threads. This way your main thread can make sure you have a responsive program and the worker threads do the heavy lifting.
This works nice for heavy database work but also for for instance the downloading of files or situations where an answer of for instance a remote server can take a long time.
But this answer will probably give you more questions then answers because to explain HOW to use multi threading would be too big of an explanation for this question.
One other thing though: have a long and hard look at your database code. How are you retrieving records from the database, are there good indexes on the database etc. etc. etc. You can get insane speed improvements by optimizing this code before you have to start thinking about multi threading.
I've found the following resource: http://thaddy.co.uk/threads/ which you can download with pictures at: http://cc.embarcadero.com/item/14809 to be very usefull threading tutorial.
If you want to make your GUI program appear responsive, you must service the message queue in a timely fashion. There is no alternative.
When it comes to running database queries, the way to do that without freezing your UI, is to move the query to a different thread.

How might I find out the source of long delays on resizing the main form?

I have a D2006 app that contains a page control and various grids, etc on the tabs. When I resize the main form (which ripples through and resizes just about everything on the form that is aligned to something), I experience long delays, like several seconds. The app freezes, the idle handler is not called and running threads appear to suspend also.
I have tries pausing execution in the IDE while this is happening in an attempt to break execution while it is in the troublesome code, but the IDE is not taking messages.
Obviously I'm not expecting anyone to point me at some errant piece of code, but I'm after debugging approaches that might help me. I have extensive execution timing code throughout the app, and the long delays don't show up in any of the data. For example, the execution time of the main form OnResize handler is minimal.
If you want to find out what's actually taking up your time, try a profiler. Sampling Profiler could answer your question pretty easily, especially if you're able to find the beginning and the end of the section of code that's causing trouble and insert OutputDebugString statements around it to narrow down the profiling.
OK. Problem solved. I noticed that the problem only occurred when I had command-line switches enabled to log some debug info. The debug info included some HTTP responses that were written to a debug log (a TMemo) on one of the tabs. When the HTTP response included a large block with no CR/LFs the TMemo wrapped it. Whenever I resized the main form, the TMemo resized and the control had to render the text again with the new word wrapping.
To demonstrate:
start a new Delphi project
drop a TMemo onto the form
align it to Client
compile and run
paste a large amount of text into the TMemo
resize the main form
I won't award myself the answer, as I hadn't really provided enough info for anybody else to solve it.
BTW #Mason - would SamplingProfiler have picked this one up - given that the execution is inside the VCL, and not in my code?
A brute-force approach that may give results.... Put a debug message to OutputDebugString() from every re-size event, sending the name of the control as the string to be displayed. This may show you which ones are being called "a lot".
You may have a situation where controls are bumping each other, setting off cascading re-size events. Like 3 siblings in the back seat of a compact car, once they start jostling for position, it can take a while for them to "settle down".
Don't make me turn this car arround....
The debug log (viewable in the IDE, or with an external ODS viewer), may show you which ones are causing the most trouble, if they appear multiple times for one "user-initiated re-size event".
Run your application in AQTime's performance profiler (included with XE, but you can get a time-limited version from their website).
Do some fanatic resizing for a while, and then stop the application.
After that, you'll see exactly which function was called many times, and where most time was spent.

Making Sure A ShowMessage Stays On Top

I have a few applications and I call a ShowMessage('Complete!'); at the end of a long operation.
Most of the time, this works great, but every once in awhile, the Message Dialog will show up behind the main form.
Is there any way for me to ensure the ShowMessage will always be on top?
Thanks!
Call the Windows MessageBox() API instead and pass in the handle to the active form. Actually, my code uses Application.MainFormHandle all the time which I am therefore sure is a reasonable and simple approach.
This will have the benefit of being the system native dialog rather than the home-grown Delphi version. It supports clipboard operations also.
If you want to get very fancy then you can use the Vista task dialog, but that's much more complex and you clearly don't need it for such a simple dialog.

Resources