I was wondering if anybody could give insight on how to implement the window selector in Winspector Spy. Basically, I would want to provide a panel that I could mouse down on, drag over to another processes window (or sub window) and get something like the HWND out of it. Ideally, I would do this in C#, but if it's only possible by wrapping the C APIs, then I can just do it in C++.
I messed around with the DragDrop event and calling DoDragDrop on mouse down in C#, but wasn't really sure if that could give me what I want. Will it be easier to just get the global X/Y pos of the mouse and find the topmost window at that location? Is there an API which does that automagically for me given x, y parameters?
EDIT: Just discovered WindowFromPoint for the latter question
You don't normally get mouse messages when the mouse isn't over your window. But you need to in order to do drag and drop operations. So, Windows provides a mechanism called mouse capture. To prevent mouse capture from being abused, you can only capture the mouse on a button down message. Once you have capture, you get mouse move messages no matter where the mouse is on screen until you release capture or when Windows sees the corresponding button up message.
The C++ code for this looks something like this
case WM_LBUTTONDOWN:
{
SetCapture(hwnd);
}
break;
case WM_MOUSEMOVE:
if (GetCapture() == hwnd)
{
POINT pt = {GET_MOUSE_X(lParam), GET_MOUSE_Y(lParam));
ClientToScreen(hwnd, &pt);
HWND hwndAtPoint = WindowFromPoint(pt);
// Show info for hwndAtPoint....
}
break;
case WM_LBUTTONUP:
if (GetCapture() == hwnd)
{
ReleaseCapture();
}
break;
case WM_CANCELMODE:
// this is a request from Windows that leave my modal state
if (GetCapture() == hwnd)
ReleaseCapture(hwnd);
break;
case WM_CAPTURECHANGED:
// notification that I lost capture (I released it or it was taken from me)
break;
The GetAncestor function can be helpful to go from the window at the point, to the top level window that own it. GetWindow can be used to walk around the window tree.
In .NET, the Control class has a Capture property that does the same thing see
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.capture.aspx
You'll need to consider how you are going to draw the rectangle around the window first, that affects the rest of your code. The easiest way to do this is by using a Form that has its TransparencyKey set to its BackColor and FormBorderStyle set to None. Draw a rectangle in the Paint event, the same size as the form's ClientRectangle, that gets you a visible rectangle with everything else transparent. Set the form's Location and Size property to match the window you found.
Now finding the window from the mouse position. You can't use WindowFromPoint(), it doesn't consider disabled windows. You'll need to use EnumWindows(). In the callback, call GetWindowRect() and check if the mouse is located inside the rectangle. Be sure to ignore your rectangle drawing window.
When you get a match, now call GetWindow() repeatedly with the GW_HWNDPREV to find windows that overlap the window you found. Keep checking the rectangle and keep ignoring your rectangle window.
This eventually gets you the top-level window that the mouse cursor is on. Now use ChildWindowFromPoint() to check if the mouse is on a child window, if any. Create your rectangle drawing form, if necessary, and give it the same size and location as the found window.
Call this code from the MouseMove event of, say, a PictureBox that displays a bulls-eye graphic. Set its Capture property to true in its MouseDown event.
Close the Close() method of your rectangle drawing form in the MouseUp event.
Since you have tagged this with C#, I can put in a link or two for this job that you are trying to accomplish and hopefully will give you the necessary insight into how to achieve this:
'A Simple Windows Forms properties Spy'
'Spying Windows Messages from the Inside'
'.Net Object Spy and InvokeRemote'
'Runtime Object Spy' <- This is my favourite tool!
All of the above articles are on CodeProject.
Hope this helps,
Best regards,
Tom.
Simple. You just set the mouse capture on mouse down, so you get all mouse messages even if they are outside your own window. Then on mouse up, use WindowFromPoint.
I'm not familiar with .NET, but with the Win32 API, you use SetCapture to set the mouse capture.
Imports:
using System.Runtime.InteropServices;
My suggestion, when the mouse is on your form, handle the mouse move/mouse up events (To capture the mouse outside of your form using a windows hook, look here: http://support.microsoft.com/kb/318804 ), and when the mouse button is released, get the mouse position on the screen, and get the window behind the cursor, using the link you provided like this:
[DllImport("user32.dll")]
public static extern IntPtr WindowFromPoint(Point lpPoint);
[DllImport("user32.dll")]
public static extern bool GetCursorPos(out Point lpPoint);
public static IntPtr GetWindowUnderCursor()
{
Point ptCursor = new Point();
if (!(PInvoke.GetCursorPos(out ptCursor)))
return IntPtr.Zero;
return WindowFromPoint(ptCursor);
}
Now you have your window handle, from there the possibilities are endless.
NOTE: The link above, (the windows hook) will only work if the mouse down occurs on your form, and the hook ends when the mouse is lifted
You could look at the C++ source for Winspy++, another window inspector program similar to Winspector Spy. I don't know of any open-source C# program like this, though.
Related
On System.Windows.UIElement there is a CaptureMouse() and a paired ReleaseMouseCapture() method. In this WPF DragDrop sample they call CaptureMouse on MouseDown and release it on MouseUp. The documentation in MSDN is about as useless as it comes - "CaptureMouse -> Captures the mouse."
In my head before trying it I assumed that it somehow locked the mouse inside the UIElement bounds, but that's clearly not the case when I try it. From experimenting, it seems to have something to do with responding to events when the mouse is outside of the UIElement, but not wanting to be a cargo cult programmer I don't want to just use it because the example does, I'd like an authoritative description of what it means.
From Capture and Uncapture the Mouse on MSDN:
When an object captures the mouse, all mouse related events are treated as if the object with mouse capture perform the event, even if the mouse pointer is over another object.
Only the capturing control receives the mouse events until released.
Capturing the mouse is useful for dragging because all the dragging code can exist in the one control, rather than being spread over multiple controls.
When it has captured the mouse, a control will receive mouse events even if the mouse pointer is no longer within its bounding area.
Typically, it's used for:
Drag and drop
Buttons (to handle Mouse Up when you put the mouse down on the button and move the mouse before you release the button)
The Silverlight 2 documentation for it has a more verbose description, I don't know why it isn't a part of the 3.5 documentation page too:
When an object has captured the mouse, that object receives mouse input whether or not the mouse pointer is within its bounding area. The mouse is typically only captured during simulated drag operations.
...
It works the same with WPF, and so the reason it is used with DragDrop, is that is how the
it knows to report back to the control being dragged from when the mouse may be outside of that control. If you comment out the MyCanvas.Capture() and the Capture(Null) (which clears it) then you can no longer drop.
I have a winform window in which I am drawing manually/programmatically, I would like to provide a HWND handle to a process I cannot modify that expressly awaits a win32 HWND (this process is then going to draw its own things in my window, using the HWND handle).
The important point of detail is that I want the HWND to point at a certain location in my window.
Is there a way to create a new artificial HWND which would point a particular zone of my window?
You can only get a HWND to a window and every control like buttons checkboxes etc are also considered windows.
So if there is no control located at the "zone" where you want to get a HWND to, that is not possible.
But if there is a groupbox on that place for instance, it is possible.
You want to draw to a certain area.
So why not just get a handle to the window and with the use of GetClientRect and GetWindowRect and draw to the x/y coordinates you want.
I have a Windows 8 application, and I need to detect when the mouse is at the bottom of the screen because my application has a CommandBar, and I would like to open it when the mouse is at the bottom. I already have swipe gestures that will open it when the user swipes, but now I have an added requirement for when the user does not have a touch device and instead must bring the mouse to the bottom of the screen to show my CommandBar. I am used to WPF's style of MouseMoved events, but unfortunately these are not available in Metro applications, so how can I get the mouse position or at least detect that the user has brought the mouse to the bottom of the screen? I have tried searching about this, but I couldn't find anything...perhaps I am missing something?
As Chue X said the standard way to open the app bar by mouse is right click.
Windows Runtime apps use Pointer messages for all pointer input: Mouse, touch and pen will all generate PointerPressed, PointerMoved, etc. You can examine the Pointer event args to see which type of input it is in the PointerPoint.PointerDevice.PointerDeviceType.
There are slightly different versions of the Pointer events on the CoreWindow (with full window scope) and on the xaml UIElement (scoped to the element). You can use either in a xaml app. For your use either would work. They give essentially the same information.
Responding to mouse interactions (XAML) http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh994936.aspx
Here's the code I used to achieve this behavior from any OnPointerMoved event handler:
... OnPointerMoved(PointerRoutedEventArgs e)
{
PointerPoint point = e.GetCurrentPoint(page);
if (point.Position.Y > page.RenderSize.Height - 5)
{
page.MainMenu.IsOpen = true;
}
}
I have a point in screen coordinates and I need to find which window it "falls" onto. I.e. find the window (hWND) that is foremost of all windows containing the point. Pinvoke and WinApi is fine...
PS. I actually need to detect, which window is my mouse cursor currently "over", to put it another way.
Use WindowFromPoint.
I have an application wich open a modal form with the ShowDialog method.
Once this form is displayed I want to capture the mouse movement even if the cursor is outside the form.
How can I capture the mouse movement? I saw something with the Capture property but I cannot manage to make it work.
[edit]
I want to be notified if the mouse move outside the form.
The Capture property is the correct way, but there are some limitations.
Only the foreground window can capture the mouse
Mouse capturing can be disabled by other parts of the system
The Win32 API function SetCapture gets reset everytime a "mouse up" event occours. I assume that also applies for .NET.
See the remarks section of Capture property.
When the mouse is captured, you'll receive the usual events but with a wider mouse coordinate range (for example a negative X position, if the mouse is left of the capturing control)
Mouse capturing is fragile, because of it's global nature. Check if there are other ways to handle certain events. Perhaps the MouseLeave or MouseEnter events are enough in your case.
You can just use the static property Control.MousePosition.
You can read the position of the cursor, using the Cursor.Position property, see Cursor.Position