Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drag and Drop like Winspector Spy

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

like image 728
NG. Avatar asked Feb 21 '10 19:02

NG.


3 Answers

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

like image 172
John Knoeller Avatar answered Nov 18 '22 04:11

John Knoeller


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.

like image 30
Hans Passant Avatar answered Nov 18 '22 05:11

Hans Passant


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.

like image 1
t0mm13b Avatar answered Nov 18 '22 06:11

t0mm13b