views:

620

answers:

4

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:

  1. Prevent Application.ProcessMessages until my procedure is complete, or

  2. Trap all messages generated during my procedure, and not release them until the end of the procedure.

+5  A: 

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.

Mike J
The LockWindowUpdate seems to be what I was looking for. And your other ideas are good too. Thanks.
lkessler
The LockWindowUpdate() API should never be used, according to Raymond Chen: http://blogs.msdn.com/oldnewthing/archive/2007/02/23/1747713.aspx At least it should not be used for what you want to use it for: http://blogs.msdn.com/oldnewthing/archive/2007/02/22/1742084.aspx
mghie
Hmmm. Thanks mghie. Your warning will be heeded. But Mike still gave me a lot of information about the processing to lead me in the right direction.
lkessler
@mghie: LockWindowsUpdate should never be used. True - by I await something that works as well. Using SetWindowUpdate seems to behave differently. I really do try to say no.....Brian.
Brian Frost
@Brian: "Works as well" - how so, if `LockWindowsUpdate()` doesn't really work? What could be worse? Or more to the point - what are you trying to do that `LockWindowsUpdate()` helps you doing while the alternatives don't? What problems do you have with `SetWindowUpdate()` in particular?
mghie
@mghie: Well, it's strange. I start using a couple of Lock and Unlock procedures (below) which I believe are the recommended method, but I sometimes get controls not repainted, non-alignment or other artifacts which annoyingly are always absent when I then try LockWindowUpdate. Maybe I'm still doing something wrong....?Lock: SendMessage(Hnd, WM_SETREDRAW, 0, 0);Unlock: SendMessage(Hnd, WM_SETREDRAW, 1, 0);Flags := RDW_FRAME + RDW_INVALIDATE + RDW_ALLCHILDREN + RDW_NOINTERNALPAINT;If UpdateNow then Inc(Flags, RDW_UPDATENOW);RedrawWindow(Hnd, nil, 0, flags);
Brian Frost
@Brian: I don't know, I never had problems with `WM_SETREDRAW`. It's usually followed by either `Invalidate()` only, or `Invalidate()` and `Update()` (or `Repaint()`, which does both). Alignment issues can be taken care of by calling `Realign()`. A combination of these has so far always worked for me...
mghie
+2  A: 

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.

Nick D
I think this would work, too. A little more work than Mike's ideas, but would give me complete control. Thanks.
lkessler
+1  A: 

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).

mj2008
That's sort of what I'm looking for: a BeginUpdate/EndUpdate pair that will prevent all Windows events until the work is done. But I believe these only work to prevent repainting.
lkessler
A: 

At a pedantic level, the way you "prevent" Application.ProcessMessages is to not call any code that

  1. shows a modal dialog
  2. calls SendMessage
  3. runs its own local message loop
  4. 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.

dthorpe