Move a window on keypress + mouse (like linux ALT

2019-03-02 15:59发布

问题:

Simple, i want to move a windows pressing ALT+MOUSE, like linux os (ALT+drag).

It's possible to pass a win32 api (move api) to the windows interested clicking on it?

I have a windows services that hook key pressed (ALT button in specific). When ALT key is pressed and a mouse down event is verified, i want to move window clicking anywhere, not only on the title bar!

Currently i move my form windows in this way:

using System.Runtime.InteropServices;

[DllImport( "user32.dll", CharSet = CharSet.Auto, SetLastError = false )]
static extern IntPtr SendMessage( IntPtr hWnd, uint Msg, int wParam, int lParam );
[DllImportAttribute( "user32.dll", CharSet = CharSet.Auto, SetLastError = false )]
public static extern bool ReleaseCapture();

private void Form1_MouseDown( object sender, MouseEventArgs e )
{
  ReleaseCapture();
  SendMessage( this.Handle, 0xa1, 0x2, 0 );
}

How can I get windows handle of the specific windows by clicking and after call SendMessage() on it?

It's possible?

回答1:

You can do this by trapping the WM_NCHITTEST message that Windows sends to see what area of the window got clicked. You can fool it by returning HTCAPTION and it will dutifully perform the mouse actions you'd normally get when clicking the caption of a window. Including moving the window. Paste this code into your form:

    protected override void WndProc(ref Message m) {
        base.WndProc(ref m);
        // Trap WM_NCHITTEST when the ALT key is down
        if (m.Msg == 0x84 && (Control.ModifierKeys == Keys.Alt)) {
            // Translate HTCLIENT to HTCAPTION
            if (m.Result == (IntPtr)1) m.Result = (IntPtr)2;
        }
    }


回答2:

I worked this out my self, came up with something interesting of my own calculations, worked perfectly for me, for any window (any active foreground window). Kinda long, but really easy to understand if you follow along the comments, hope it helps :) The way it works, is that you press a certain registered key-combo, like Ctrl+Alt+M and the mouse will stick in the center of an active window, you move the mouse, the windows follows it, press the SAME combo again, to release, no need for mouse clicks or anything.

public void MoveWindow_AfterMouse()
    {
      // 1- get a handle to the foreground window (or any window that you want to move).
      // 2- set the mouse pos to the window's center.
      // 3- let the window move with the mouse in a loop, such that:
      //    win(x) = mouse(x) - win(width)/2   
      //    win(y) = mouse(y) - win(height)/2
      // This is because the origin (point of rendering) of the window, is at its top-left corner and NOT its center!

      // 1- 
      IntPtr hWnd = WinAPIs.GetForegroundWindow();

      // 2- Then:
      // first we need to get the x, y to the center of the window.
      // to do this, we have to know the width/height of the window.
      // to do this, we could use GetWindowRect which will give us the coords of the bottom right and upper left corners of the window,
      // with some math, we could deduce the width/height of the window.
      // after we do that, we simply set the x, y coords of the mouse to that center.
      RECT wndRect = new RECT();
      WinAPIs.GetWindowRect(hWnd, out wndRect);
      int wndWidth = wndRect.right - wndRect.left;
      int wndHeight = wndRect.bottom - wndRect.top; // cuz the more you go down, the more y value increases.
      Point wndCenter = new Point(wndWidth / 2, wndHeight / 2); // this is the center of the window relative to itself.
      WinAPIs.ClientToScreen(hWnd, out wndCenter); // this will make its center relative to the screen coords.
      WinAPIs.SetCursorPos(wndCenter.X, wndCenter.Y);

      // 3- Moving :)))
      while (true)
      {
        Point cursorPos = new Point();
        WinAPIs.GetCursorPos(out cursorPos);
        int xOffset = cursorPos.X - wndWidth / 2;
        int yOffset = cursorPos.Y - wndHeight / 2;
        WinAPIs.MoveWindow(hWnd, xOffset, yOffset, wndWidth, wndHeight, true);
        Thread.Sleep(25);
      }
    }

And now:

int moveCommandToggle = 0;
protected override void WndProc(ref Message m)
{
   if (m.Msg == 0x0312)
   {
     int keyID = m.WParam.ToInt32();
     if(keyID == some_key_combo_you_registered_for_the_moving)
     {
         if (moveCommandToggle++ % 2 == 0)
         {
            mover = new Thread(() => MoveWindow_AfterMouse());
            mover.Start();
         }
         else mover.Abort();
      }
    }
 }

If you're wondering about RECT:

  public struct RECT
  {
    public int left;    // xCoor of upper left corner.
    public int top;     // yCoor of upper left corner.
    public int right;   // xCoor of lower right corner.
    public int bottom;  // yCoor of lower right corner.
  };

WinAPIs was just a static class that I did my DllImports in.