I am trying to track a moving desktop application window so I can move a corresponding transparent overlay window in parity. Currently, I hook the EVENT_SYSTEM_MOVESIZEEND event with SetWinEventHook. Inside my callback, I update the location of my overlay with the new location of the target application window.
This works, but it means that my overlay jumps around after the user lets go of the target window's title bar. I would like my overlay to track the target window as it is moving, not just after it has been moved.
The only way I can think to do this is to also hook the EVENT_SYSTEM_MOVESIZESTART event. When the START event fires, spawn a new thread that polls the target windows location and updates my overlay location. Then, when the END event fires, kill the polling thread.
Is this a reasonable approach, or is there a better way to achieve the functionality I want.
Thanks.
As per Hans Passant's suggestion on my question. I was indeed looking to hook EVENT_OBJECT_LOCATIONCHANGE instead of EVENT_SYSTEM_MOVESIZEEND. Once I hooked LOCATIONCHANGE, the tracking worked as expected.
One thing of note, by hooking LOCATIONCHANGE you will also receive mouse events for the window. You can easily filter the movement of the window by checking the hwnd of the WinEventProc callback function.
MSDN:
Handle to the window that generates the event, or NULL if no window is associated with the event. For example, the mouse pointer is not associated with a window.
Related
I have seen several tools adding a custom button and/or drawing on the title bar of all windows of all applications in Windows. How is that done?
Extra points for an example in Delphi.
EDIT:
I found something for dotNET that does this:
http://www.thecodeking.co.uk/2007/09/adding-caption-buttons-to-non-client.html#.VdmioEDenqQ
How I see this job:
First of all we should be able to paint this button on the our own window caption. This procedure will be used later
This part of the program enumerates the active and visible windows
This part of the program using injection attach our dll to enumerated windows
From injected dll we can draw the button on the window caption
Inside this dll we should process the click on the button
We should have mechanism to send result to our main program
I haven't done this, so the following is what I would investigate if I were to try:
For each application / each top-level window:
Create a floating window and position it over the title bar wherever you want it to sit. Set up the parent / child relationship, but this window is part of your own process. (There are occasionally problems parenting a window from one process to one from another process, but try. I'd avoid injecting into other processes if possible.)
You can investigate the window flags to see if the window has a title bar (ie if you should add a button) via GetWindowLong with GWL_STYLE looking for WS_CAPTION. The same call will also let you see the type of caption / frame, which you can combine with GetSystemMetrics with, eg, SM_CYDLGFRAME to figure out the right size for your button on this specific window's title bar.
This window is now your button: paint, handle clicks etc as appropriate.
Make it a non-focusable window so that clicks to it don't take focus away from the window is is on the title bar of. You don't want clicking it to make the title bar change colour, for example. Do this by setting the WS_EX_NOACTIVATE window flag, something like: SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) orWS_EX_NOACTIVATE).
The main problem is to keep it positioned correctly when the window moves, is resized, etc. To do this, install a hook for the system move events. You can also hook minimize and restore via EVENT_SYSTEM_MINIMIZESTART and EVENT_SYSTEM_MINIMIZEEND. This will allow you to keep track of all windows moving around onscreen, such that you can adjust the button-window position if necessary.
That gives you a window which you can paint as a button (and respond to clicks etc), that visually is "attached" to other windows so it stays in the same place as the user drags the title bar, minimizes or maximises the app, etc, and that is in your own process without cross-process problems.
I am trying to emulate simple mouse movement in a window belonging to another process. My app uses global hooks to inject DLL into the target process (WH_CBT and WH_GETMESSAGE) and the injection works like a charm. The intention is to fool the target process into thinking the mouse went over a portion of the screen. When I do a movement with the physical mouse, this triggers a certain app behavior (e.g. a tooltip is being shown). I would prefer if the actual mouse pointer remained in its current position when I perform the "trick".
I have established message monitoring with Spy++. Sending (or posting) plain WM_MOUSEMOVE messages to the target HWND is registered by Spy++ but has no desired effect. When the mouse is physically moved, the app does its thing. I have tried sending some other messages in conjunction to WM_MOUSEMOVE (e.g. WM_SETCURSOR) but things didn't improve. I have even hijacked GetCursorPos in the target process to return the same coordinate as posted in WM_MOUSEMOVE (former is screen, latter is client) but this didn't help either.
When I do a simple SetCursorPos, the app does what it's supposed to do. What other magic am I missing that the SetCursorPos is doing? The messages captured by Spy++ look more or less the same in both scenarios.
Any suggestions on how to send mouse movement are welcome. I do not want to use SendInput, mouse_event or other APIs. I need to target a specific HWND for a very brief period of time.
Usually a tooltip is shown as a result of the WM_NOTIFY message, which is sent with the TTN_SHOW notification code. Have you tried it?
In my wxWidgets (wxPython) app, I am using a 50 ms timer to do some polling and update a window if there are any changes. If changes are detected it calls wxWindow.Refresh to update the window, and the actual updating of the widgets is done in the EVT_PAINT handler. It would be nice if I could disable the painting and/or the timer if the user is not looking at the window anyway. However EVT_PAINT is still being fired even if the window is iconized or hidden behind a different window.
Is there any way to detect if the window is not currently visible on screen, or to prevent EVT_PAINT events from firing unnecessarily?
There is the IsActive method and the EVT_ACTIVATE event to test whether the window has focus, but I want to keep updating the window if it is unfocussed but still visible. The wxWindow.IsShown family of functions doesn't help, they still return True for hidden/iconized windows.
IsShownOnScreen() could help with the iconized case. Edit: But actually does not.
Or you could trap wxEVT_ICONIZE to detect when the window is minimized.
But to be honest I'm rather surprised that refreshing a window hidden behind another one still results in a repaint. If the window in front is not transparent, this really shouldn't happen.
I have this problem. I have an handler to the mainWindow of a certain application, and I want to simulate a keypress on that application...
I'm using sendMessage/postMessage api calls to do this. The reason why I don't use the .Net SendKeys function or the keybd_event of the win32 api, is that they simulate the keypress at a global level. In my case, I may have the application minimized and still want the keypress to be simulated.
The problem with sendMessage and postMessage is that you must pass the handler of the exact childwindow where you want the key to be pressed. For example, in notepad, if I send the key to the mainWindow, nothing happens, I have to send the key to the child window that basically consists of the white canvas where you can write.
With msPaint for example, if a user creates a new document, and opens a textbox in that drawing, and I want to simulate a keypress there, I have to get the childwindow of the childwindow of the mainwindow for it to works.
So I found a way that seemed to work for every situation, basically, I used getWindow with the parameter GW_CHILD, to get the child-window with the highest z-value. Then I do it again for the child window and continue doing it until a certain childWindow has no more childWindows..
And it seemed to work and I was very happy!
However... I found cases where this does not work. Firefox is one of them. Firefox has the mainWindow, and then has a childWindow that's pretty much the same as the mainWindow and then it has another childWindow which is the website area, ie, the area under the address bar and menus. If I am on www.google.com for example, and I want to simulate a keypress in the focused search box, it works, cause getting the child-window of the child-window gives me the correct childWindow. However, if the user clicks on the address bar for example, nothing changes in the way the getWindow works. It will still eventually get the childwindow that's under the address bar, doing nothing, instead of simulating the keypress on the address bar.
The thing is that I haven't found a way of getting the active child window of a certain application. I can only use the GetWindow method to get the child window of a certain window and do it until I find a child window with no childs. However, as you've seen in the firefox case, the active window is actually the parent of the child window that I get in the end.
I've tried other api calls like getTopWindow but I had no luck..
Anyone can put some light on this issue?
Thanks!
If the application violates the windowing rules of windows, you'll need an exception.
In Mozilla, it's like this (IIRC):
There's this 'god' window of the class MozillaUIWindowClass and with the "- Mozilla Firefox" string in its window text.
If you know the position of the address bar you can use the following function:
And provide it with the HWND of the 'god' window and the position of the address bar.
HWND ChildWindowFromPoint(HWND, POINT);
There is probably a better solution, I came up with this since I needed to automate mouse, which is position based.
For more information you might need to consult the sources of particular software, or spend whole day in Spy+. :>
You can use GetGUIThreadInfo to get info about the UI of a particular process.
If you have the main window you can call GetWindowThreadProcessId to obtain the process thread id. Then you can call GetGUIThreadInfo to get info about the active/focused windows, etc.
I also have to point that some applications only have one window and all its controls are windowsless (like Windows Live Messenger).
In MFC a double-mouse click event triggers the following sequence of messages
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBCLK
WM_LBUTTONUP
So responding to the WM_LBUTTONDBCLK message allows you to detect a double-click. But if I just want to detect a single-click how to I distinguish it?
But just looking at the WM_LBUTTONUP message isn't enough as it could be a single-click or it could be the first click of a double-click.
How can I successfully identify just a single-click?
(Please allow me to call these events Mouse Up and Mouse Down. My MFC is a little rusty. And there's this stuff called .NET who's been messing up my terminology lately ;-)
Short story: You don't simply want to know about Mouse Click. You need more.
Long story:
Although this is counter-intuitive, it appears that simply wanting a mouse-click is fairly uncommon. Most often, you'll want to perform some processing on Mouse Down and do some further processing on Mouse Up. The trick is that simply tracking Mouse Up messages is not enough: Mouse Down may not have happened in your window. Do you consider it a valid click then? Especially considering that the Mouse Down processing (such as selecting an item) did not occur.
Going further up the reasoning, you should not rely on receiving a Mouse Up after you processed Mouse Down: User may have moved the mouse and released the button somewhere else (think drag'n'drop), in which case, you don't receive the MouseUp event... unless you capture the mouse on MouseDown to make sure you get mouse event up to Mouse Up even if the mouse left your window.
All in all, you end up tracking Mouse Down, capture the mouse and when you receive Mouse Up, just check if you own the capture. If not, the mouse was either double-clicked (no 2nd mouse down) or Mouse Down happened somewhere else hence you most likely don't care about this Mouse Up.
In conclusion: There's no MouseClick message simply because you wouldn't go very far with it: You need to handle more messages and implement more mechanics anyway.
Oh! And if your dealing with an existing control which already handles all this items and selection stuff, such as a listview, chances are it provides with a similar custom notification such as Item Activate or Item Selection Changed.
I just tried this in Delphi, the behavior is the same: even when a double click is happening, a single click event is issued right after the first one of the two.
I solved it using a timer, which works like this:
deactivate timer on WM_LBUTTONDBLCLK (and set bDbl to true)
activate timer on WM_LBUTTONUP if bDbl==false
deactivate on WM_LBUTTONUP if bDbl==true (and reset bDbl)
I set the interval of the timer to the time returned by GetDoubleClickTime.
MSDN says:
The GetDoubleClickTime function
retrieves the current double-click
time for the mouse. A double-click is
a series of two clicks of the mouse
button, the second occurring within a
specified time after the first. The
double-click time is the maximum
number of milliseconds that may occur
between the first and second click of
a double-click.
If the timer happens to fire then you have the real click. In my case the double click interval is 500ms, so any "real click" will be delayed this long.
You could check WM_LBUTTONDOWN has not been called more than once before WM_LBUTTONUP. In practice Windows does this for you, in that if you get a WM_LBUTTONDBCLK you tend not to get a WM_LBUTTONUP.
You can use PreTranslateMessage() to count the messages as they appear. If you've received only the mouse messages corresponding to a single-click, and the system-configured time for double-clicking has expired, you can safely assume it's a single-click.
As far as I know there is no way to know that this is the case as it is happening, which makes sense -- until the time is expired, there's no way to know that a second click is or isn't coming.
that's a little tricky.
I would detect the WM_LBUTTONDOWN & WM_LBUTTONUP combo, store that event somewhere and set a timeout for a second or so. If there isn't a WM_LBUTTONDBCLK during that timeout then you have a single click.
This might imply you need to have another thread running but I think you could accomplish it with one thread.
I think the solution is to start a timer after the first click & then check the elapsed time after at the next immediate click, this will tell you if it is a single click or double click.
You typically look at #MLButtonUp and you would not have single click and double click behavior on the same mouse button.