How to Prevent ProcessMessages in Delphi - delphi

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.

Related

How to intercept a message sent to any TWinControl on my form?

I'm faced with the daunting task of having to intercept and handle the WM_GETOBJECT message whenever it is sent to any TWinControl on a Form.
every panel
every nested panel
every edit box
every combo box
every button
every toolbar
every datetime picker
every image
every listview
every treeivew
every win control
on every form
Obviously i'd prefer not to have to individually subclass every control:
it's difficult to do correctly (may people use SetWindowSubclass when they want to start subclassing, and RemoveWindowSubclass when they're done, and don't realize the crash they just introduced)
it's difficult to do correctly
it requires subclassing every control individually, likely through a child control iteration function (which has the common bug of failing if you apply it when the form handle is created and removing them when the form handle is destroyed)
Is there a way to be involved in the handling of every message sent directly to a child control using SendMessage
similar to how TApplicationEvents.OnMessage can intercept every posted message
similar to how KeyPreview allows a form to see every send Key message
If you don't want to subclass each individual control (which is certainly an option, and one that can be simplified using interposer classes, for instance), then you can instead use a thread-specific WH_CALLWNDPROC or WH_CALLWNDPROCRET hook via the Win32 API SetWindowsHookEx() function. The hooks will tell you which HWND is receiving each message, and you don't need to implement the hooks in a DLL when hooking a thread in the same process as the hooker.
If you need the TWinControl* pointer for a given HWND, you can use the VCL's FindControl() function in the Vcl.Controls unit.

There is any quick way to, while debuging, to stop at a specific Windows Message or API?

So I want to put a Breakpoint in a specific API or Windows message.
I don't find any easy way to do that without writing code in any Delphi version.
Is there a way to do that similar as I can put a breakpoint in memory access?
To stop at any call to an API function, find it in the implementation section of Windows.pas (or wherever the function of interest is declared) and set a breakpoint. That takes care of functions you use with load-time dynamic linking. For run-time dynamic linking (LoadLibrary and GetProcAddress), you'll need a different technique. The variable that gets the result of GetProcAddress will hold the address you want to break at, but I don't know off-hand how to set a breakpoint at that address.
Stopping on a Window message is trickier since messages can be retrieved in many places. You'll have to use conditional breakpoints instead.
To catch most posted messages, you can put a breakpoint in TApplication.HandleMessage on the first line after the call to PeekMessage. Set the condition to be Msg.Message = x. HandleMessage takes care of messages posted to the main thread's message queue for the main Application.Run message loop as well as the VCL's modal message loops. Other modal dialogs (such as Windows.MessageBox) won't use it, though.
Observing sent messages is harder because the OS dispatches them to their target window procedures directly. You'll have to set a breakpoint in the window procedure of every window class you're interested in. You could get most VCL window classes by putting your conditional breakpoint in Classes.StdWndProc.
Keep in mind that conditional breakpoints can be very slow. They work by the debugger putting an unconditional breakpoint there, and when the OS triggers it, the debugger takes over, checks the condition, and then resumes execution if the conditional fails. That can involve a lot of overhead, switching between the debugger and your application; programs receive lots of messages, so if you can find a way to avoid having the debugger interrupt your program to check every one of them, do it.
If this isn't feasible for whatever it is you're trying to debug, then I recommend posting a new question where you describe the problem you're really trying to solve.
You will need to go into Options | Linker and check "Debug DCUs". by default this is not checked so the debugger doesn't step through the entire VCL when you're trying to work.

Is there a way to log every gui event in Delphi?

The Delphi debugger is great for debugging linear code, where one function calls other functions in a predictable, linear manner, and we can step through the program line by line.
I find the debugger less useful when dealing with event driven gui code, where a single line of code can cause new events to be triggered, which may in turn trigger other events.
In this situation, the 'step through the code' approach doesn't let me see everything that is going on.
The way I usually solve this is to 1) guess which events might be part of the problem, then 2) add breakpoints or logging to each of those events.
The problem is that this approach is haphazard and time consuming.
Is there a switch I can flick in the debugger to say 'log all gui events'? Or is there some code I can add to trap events, something like
procedure GuiEventCalled(ev:Event)
begin
log(ev);
ev.call();
end
The end result I'm looking for is something like this (for example):
FieldA.KeyDown
FieldA.KeyPress
FieldA.OnChange
FieldA.OnExit
FieldB.OnEnter
This would take all the guesswork out of Delphi gui debugging.
I am using Delphi 2010
[EDIT]
A few answers suggested ways to intercept or log Windows messages. Others then pointed out that not all Delphi Events are Windows messages at all. I think it is these type of "Non Windows Message" Events that I was asking about; Events that are created by Delphi code. [/EDIT]
[EDIT2]
After reading all the information here, I had an idea to use RTTI to dynamically intercept TNotifyEvents and log them to the Event Log in the Debugging window. This includes OnEnter, OnExit, OnChange, OnClick, OnMouseEnter, OnMouseLeave events. After a bit of hacking I got it to work pretty well, at least for my use (it doesn't log Key events, but that could be added).
I've posted the code here
To use
Download the EventInterceptor Unit and add it to your project
Add the EventInterceptor Unit to the Uses clause
Add this line somewhere in your code for each form you want to track.
AddEventInterceptors(MyForm);
Open the debugger window and any events that are called will be logged to the Event Log
[/EDIT2]
Use the "delphieventlogger" Unit I wrote download here. It's only one method call and is very easy to use. It logs all TNotifyEvents (e.g. OnChange, OnEnter, OnExit) to the Delphi Event Log in the debugger window.
No, there's no generalized way to do this, because Delphi doesn't have any sort of "event type" that can be hooked in some way. An event handler is just a method reference, and it gets called like this:
if assigned(FEventHandler) then
FEventHandler(self);
Just a normal method reference call. If you want to log all event handlers, you'll have to insert some call into each of them yourself.
I know it is a little bit expensive, but you can use Automated QA's (now SmartBear) TestRecorder as an extension to TestComplete (if you want this only on your system, TestComplete alone will do). This piece of software will track your GUI actions and store it in a script like language. There is even a unit that can be linked into your exe to make these recordings directly at the user's system. This is especially helpful when some users are not able to explain what they have done to produce an error.
Use WinSight to see the message flow in real time.
If you really want the program to produce a log, then override WinProc and/or intercept the messages in Application.
The TApplication.OnMessage event can be used to catch messages that are posted to the main message queue. That is primarily for OS-issued messages, not internal VCL/RTL messages, which are usually dispatched to WndProc() methods directly. Not all VCL events are message-driven to begin with. There is no single solution to what you are looking for. You would have to use a combination of TApplication.OnMessage, TApplication.HookMainWindow(), WndProc() overrides, SetWindowsHook(), and selective breakpoints/hooks in code.
Borland's WinSight tool is not distributed anymore, but there are plenty of third-party tools readily available that do the same thing as WinSight, such as Microsoft's Spy++, WinSpector, etc, for tracking the logging window messages in real-time.
As an alternative, to debug the triggered events use the debugger Step Into (F7) instead of Step Over (F8) commands.
The debugger will stop on any available code line reached during the call.
You can try one of the AOP frameworks for Delphi. MeAOP provides a default logger that you can use. It won't tell you what is going on inside an event handler but it will tell you when an event handler is called and when it returns.

Delphi - Message loop for Form created in DirectShow filter goes dead

I have a DirectShow filter created with Delphi Pro 6 and the DSPACK direct show library. I'm running under windows XP. I've tried creating the form dynamically when the container class for the DirectFilter has its constructor called, passing NIL into the constructor as the AOwner parameter (TMyForm.Create(nil) and then calling the Form's Show() method. The form does show but then appears to stop receiving windows messages because it never repaints and does not respond to input. As a test I then tried creating my own WndProc() and overriding the Form's WndProc(). My WndProc() did get called once but never again.
I'm guessing it's because I'm a DLL and the context that I am running in is not "friendly" to the window message handler for the form; perhaps something to do with the thread that calls it or whatever. If someone could give me a tip on how to solve this or what the proper way to create a persistent window is from the context of a DirectShow filter I'd appreciate it. Note, as I said the window needs to be persistent so I can't create it as a Filter property page.
Thanks,
Robert
I can't help you with the DirectShow filter specifics, but I feel that some general information about windows and message handling might help.
Windows have thread affinity, which means that all messages for a window will be handled in the context of the thread that created it. That means that this thread needs to have the standard message processing loop, the low level equivalent of Application.ProcessMessages(). Both messages from the same and from other threads will be queued in the message queue of the creating thread, and the message loop will get them, (optionally) translate them, and dispatch them to the window handler of the target window.
What you are describing could be caused by either
not having a message processing queue in the thread that creates the window, or
creating the window in the wrong thread
(Note that these are essentially the same, but stated like this it becomes apparent that there may be different problems that cause this, which need to be fixed in different ways - either the window needs to be created in a different thread, or a processing loop needs to be created in the thread.)
You need to find out which of the two causes your window not to process messages. You don't necessarily need to override WndProc(), message handling methods for distinct messages will work (or not work) the same. That your WndProc() was called once doesn't really tell you much, because under some circumstances messages sent from the same thread will be handled without the message loop, by calling the window proc directly.
Since your filter resides in a DLL I don't think that creating your own message loop would be the right thing. This works for a modal dialog, which will be created, the message loop will run until the dialog is closed, and then the message loop will terminate and the DLL function will return. It will not work for a DLL exported function that will be called and needs to return all while the message loop is still running. I assume the framework that creates and calls those filters will handle the message loop as well. However, that is a gut feeling, not knowing about DirectShow filters this may well be wrong.
What might help you to debug this is a tool like Spy++ from Visual Studio, with which you can show information about windows, log messages sent to them or to all windows in the same process or thread, show window hierarchies and do a lot of other interesting things. If you don't have that, there are a lot of clones (some freeware) on the net, which Google should turn up. Trying to show the messages sent to all windows of the same thread or process should tell you whether a message loop is running. You should also be able to get more information by running SysInternals Process Explorer or similar tools.

Is there another way to load MSHTML documents without use Application.ProcessMessages?

Is there another way to load MSHTML documents without use Application.ProcessMessages?
To load a document into a IHTMLDocument I need to do this:
while Doc.readyState <> 'complete' do
Application.ProcessMessages;
I want not to process all the message queue during the loading, because I would be changing my application flow, in other words, some messages that should be processed after the loading to be completed can be processed earlier, even before the loading end.
There is a special message code that the IHTMLDocument expect to advance in the loading process? Or there is another way to load?
The call to Application.ProcessMessages is most likely just needed to allow the MSHTML activeX control time to finish loading the document. It sounds like they're using cooperative multitasking here to simulate loading the doc in the background - the ActiveX posts messages to itself to process the next chunk or whatever.
Normally, this wouldn't affect your app's flow because the doc load would happen as part of your normal message loop. But because you're wanting to load the doc synchronously (not do anything else until the doc is fully loaded), you're sensitive to the way it's doing background loading via messages.
One solution: see if you can remove the requirement to load the doc synchronously. Let the load happen when it happens, but move the check for readState = complete into a timer, perhaps on a 1 second interval. When the timer discovers the doc load is complete, then fire off your downstream food chain activities.
Another solution: Display a modal dialog while waiting for the doc to load. This has the benefit of disabling the rest of your UI so you don't run the risk of reentrancy. Calling ProcessMessages means the user can still interact with your window, click on buttons, menus etc. Usually this will lead to problems. Displaying a modal dialog ("progress dialog?") avoids reentrancy by disabling everything behind the modal dialog.
Third possibility: Replace Application.ProcessMessages with PeekMessage and logic to examine the message to decide whether to let it go through or put it back on the message queue for later. This is a bit dirty but might work in very special cases.
I recommend approach #2, the modal dialog.
The component TEmbeddedWB contains some helper functions such as LoadFromFile and LoadFromStream which will load the document into the MSHTML control directly. Move your complete logic into the onDocumentComplete event.
There is a TEmbeddedWB.OnDocumentComplete event, fired when a document completes loading. Is there any special reason why you don't want to use that?

Resources