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

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.

Related

Firemonkey - Message Handling

I'm currently trying to develop a project based upon Firemonkey. I'm using Firemonkey for it's UI features as the project consists of many smaller applications, each with a 3D aspect to it. I'm currently only developing/deploying to Windows with the FMX framework, but may go cross-platform at a later date.
I've gotten around most issues I've come across by building a VCL Windows application in the background to perform a very specific action, and then building an FMX frontend. However, this is only suitable when you only want to execute the application to perform that action it's designed to do, and thus can execute the application with parameters. In one of the applications, i've come upon the need to use messages (or something similar). For example, in my FMX application, if i click "button1", i want it to send a message to the background VCL application to perform "action1", rather than execute it with parameters.
A good example could be using the VCL TMediaPlayer in the background application, with the front-end FMX application being used to display the information and provide control of play, pause, etc. Such that it essentially becomes an FMX UI with VCL ability.
I've so far been unable to find anything on how messages (e.g. in VCL, they'd be done with "SendMessage" or "PostMessage" or something similar) are handled with Firemonkey, either through the local help file, or through extensive Googling. Everything i've turned up has been related to email (presumably because of the word "Message" in most of my search terms).
Can anyone point me in the right direction on how messages would be handled with Firemonkey/FMX?
Regards,
Scott Pritchard
My understanding is that Firemonkey is not based on traditional windows, so sending window messages to Firemonkey controls is not usually an option. Although some controls do use windows (most notibly, TCommonCustomForm), so you can use the FmxHandleToHWND() function in the FMX.Platform.Win unit to extract an HWND from a TFmxHandle when needed. I have no clue how to receive and custom process window messages in FMX controls, if that is even possible.
Firemonkey under Windows has access to the Win32 API, so there should be nothing stopping you from sending window messages to other windowed controls, such as your VCL UI. Include the Winapi.Windows unit in your uses clause to access Win32 API functions, just like you would in a VCL application.
UPDATE: because FireMonkey does not expose access to messages that are sent to a Form's window, you have to manually subclass the window in order to receive messages before FireMonkey sees them. You can override the Form's CreateHandle() method, call the inherited method first to create the window, then use FmxHandleToHWND() to get the HWND that you can subclass. Be sure to also override the DestroyHandle() method to remove the subclass before then calling inherited to release the HWND.
Currently, FireMonkey doesn't have a message handler that you can use to send and post messages.
There is a possibility of hooking things up using listeners like FireMonkey works internally, but none of it is documented.
So, instead, here's what I've done:
I created my own custom "message" class. I create instances of the class and add them to a thread-safe list from any thread I need to. On the main thread, I have a timer that checks the list and processes the "messages".

How does Delphi's KeyPreview work?

A colleague and myself were debating over which way was less of a burden on the system resources. (Note: this is not the question I want an answer to. Rather the title and the line below in bold is the question I seek an answer for.)
1. Using KeyPreview to get the keypresses on a form.
or
2. Using defining an OnMessage procedure and handling it there.
At first glance it seems KeyPreview would be less of a system burden since defining an OnMessage procedure results in our program checking every message that comes in. Note messages we don't care about would cause it to jump out by the first if statement. That is at worst we would have an if statement executed for every message.
But we're wondering how Delphi deals with the KeyPreview property... We wonder if Delphi internally defines its own OnMessage and looks at the messages to then trigger the events that are related to keypresses.
If this is the case then would both approaches be about the same?
As others have already said - there is probably no noticeable difference.
I just wanted to point out an exellent article by Peter Below on that topic: A Key's Odyssey archive
This article describes the key message processing as implemented in Delphi 2007 for Win32 VCL forms applications. A few things have changed in this area compared with Delphi 7, but these are mostly additions that I will highlight when we get to them. Most of this code has survived basically unchanged since the times of Delphi 1, a tribute to the robustness of the design. If you are in a hurry or not interested in all the details you can refer to the outline in the summary for a condensed overview.
The KeyPreview functioning:
The KeyPreview property of the current active form is checked for the KeyUp-, KeyDown- and KeyPress- event handlers of the current active control. I.e.: a key press in any control results in the check of the form's KeyPreview property.
If that property is True, the event handler in question invokes the event handler of the form prior to that of itself. If the form's event handler does not change the key value to 0 (or #0, depending on KeyPress or KeyDown/KeyUp), then the active control's event handler takes back over, otherwise the event is considered handled.
Compared to Application.OnMessage:
So setting the key value to 0/#0 in a form's event handler is synonymous to setting the Handled parameter of Application.OnMessage. In this, there is virtually no difference. But since OnMessage is called very early in the dispatching process, there is a theoretical gain in performance because the message is not being dispatched any further. When you leave Handled to False, there is no difference at all, because the KeyPreview property is always checked, whether it is set or not.
The main difference that is left is that you have to set the KeyPreview of áll Forms to True, ánd implement and maintain for each of all forms the appropriate event handlers. Compare this to having just one event handler for Application.OnMessage. That is: assuming you could do with just one routine for all of your forms.
The best answer would be, measure it. Most likely neither one is going to place any noticeable "burden" on the system, and if you don't notice it when you're specifically looking for it, then your users won't either. So just go with whichever one's easier to understand in case you need to come back to that code sometime in the future.
The bottom line here is that you can't generate input quick enough to make the computer even notice. The computer would not be troubled if you produced input messages at rates hundreds or even thousands greater than you typically do.
You won't be able to measure the difference between handling something in OnMessage and using KeyPreview.
So the decision as to which to use comes down to which is most convenient. If you need handling to happen at an application wide level, and you don't have a common base class for all your forms, then you use OnMessage. If you want different behaviour for different forms then you need to use KeyPreview.
Personally I strongly recommend refactoring so that all forms in your projects derive from a common base (a subclass of TForm). This allows you much more flexibility. Done this way you can, for example, use the KeyPreview mechanism to apply intervention points for all forms in your applications.
As for how KeyPreview is implemented, the input messages get redirected in KeyDown, KeyPress etc. in TControl. To learn more read the source code.

Avoiding the need to call ProcessMessages

I've got a component I created that embeds a hardware-accelerated renderer in a TWinControl so you can place it on a form. It works pretty well in most cases, but if I try to resize the control, everything goes blank until the message loop runs and calls the WndProc for the renderer, which is hosted in an external DLL. This means that I need to call Application.ProcessMessages explicitly after I've resized the control in code, which I know is considered bad practice.
Is there any way to put some logic in the control itself to make it invoke the WndProc in the DLL when I change the control's size, so I won't need to call ProcessMessages to get stuff to render right?
You could call TWinControl.Update instead, which should just SendMessage a WM_PAINT event, so you aren't handling arbitrary messages.
The correct solution is to call InvalidateRect when you resize. Windows will then post you a WM_PAINT message. For reference you should pull Petzold off the shelf and read his chapter on painting.

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?

How to Prevent ProcessMessages in 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.

Resources