3

I am trying to make a winforms program that will allow me to move all open windows from any process to the center of my main monitor. I have 15+ windows open on average, and before I leave at night it takes roughly a minute to move everything over to my main monitor. I need to move them to my main monitor so I don't have to deal with moving them from off screen if I remote in for a client's issue. I was looking at a link here but this only shows the window name text and length. I believe I need to use the EnumWindows api to do this, but I don't know the best way to do it. I was also looking at the GetWindow function here, but not sure how to use it. Also, I know how to move my winforms to the center of the screen, so that isn't an issue. Any insight on this is appreciated.

Community
  • 1
  • 1
John Janssen
  • 293
  • 2
  • 12
  • 30
  • Maybe this can give you some clues: http://stackoverflow.com/questions/820909/get-applications-window-handles – Youp Bernoulli Oct 01 '14 at 15:25
  • You may already know this, but you can manually move a window that's off screen by pressing `Alt+Spacebar`, then `M`, then any arrow key. The window is now attached to your cursor. It will also save you the trouble of moving all your windows back to the secondary screen every morning. – Rotem Oct 01 '14 at 15:34
  • @Rotem Yes I do know I could do that, but I am trying to do it with a push of a button, so I don't have to worry about doing that for 10+ windows that are on other displays. Instead of doing that for each window, I want to do it in one place for all windows. – John Janssen Oct 01 '14 at 15:39
  • I was just going to suggest OP you make this a console app, which you can schedule to run in the event you forget to do so on the way out. – C Bauer Oct 01 '14 at 15:50
  • @CBauer that is a great idea. – John Janssen Oct 01 '14 at 16:06

1 Answers1

9

You can use EnumWindows if the you want to move all the windows, and not only open forms owned by the application.

[DllImport("USER32.DLL")]
public static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);

Then, in your code, you can iterate throuh Opened (and visible) Windows :

IntPtr lShellWindow = NativeMethods.GetShellWindow();
List<IntPtr> listWindows = new List<IntPtr>();

NativeMethods.EnumWindows(delegate(IntPtr hWnd, int lParam)
{
    if (hWnd == lShellWindow) return true;
    if (!NativeMethods.IsWindowVisible(hWnd)) return true;

    // may be some other checks

    lWindows.Add(hWnd);
    return true;
}, 0);

As you can see, I also used IsWindowVisible and GetShellWindow

[DllImport("USER32.DLL")]
public static extern bool IsWindowVisible(IntPtr hWnd);

[DllImport("USER32.DLL")]
public static extern IntPtr GetShellWindow();

Then, with the list of Handles, you can move the windows with SetWindowPos

[DllImport("user32.dll", SetLastError = true)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);

[Flags()]
public enum SetWindowPosFlags : uint
{
    /// <summary>If the calling thread and the thread that owns the window are attached to different input queues,
    /// the system posts the request to the thread that owns the window. This prevents the calling thread from
    /// blocking its execution while other threads process the request.</summary>
    /// <remarks>SWP_ASYNCWINDOWPOS</remarks>
    AsynchronousWindowPosition = 0x4000,
    /// <summary>Prevents generation of the WM_SYNCPAINT message.</summary>
    /// <remarks>SWP_DEFERERASE</remarks>
    DeferErase = 0x2000,
    /// <summary>Draws a frame (defined in the window's class description) around the window.</summary>
    /// <remarks>SWP_DRAWFRAME</remarks>
    DrawFrame = 0x0020,
    /// <summary>Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to
    /// the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE
    /// is sent only when the window's size is being changed.</summary>
    /// <remarks>SWP_FRAMECHANGED</remarks>
    FrameChanged = 0x0020,
    /// <summary>Hides the window.</summary>
    /// <remarks>SWP_HIDEWINDOW</remarks>
    HideWindow = 0x0080,
    /// <summary>Does not activate the window. If this flag is not set, the window is activated and moved to the
    /// top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter
    /// parameter).</summary>
    /// <remarks>SWP_NOACTIVATE</remarks>
    DoNotActivate = 0x0010,
    /// <summary>Discards the entire contents of the client area. If this flag is not specified, the valid
    /// contents of the client area are saved and copied back into the client area after the window is sized or
    /// repositioned.</summary>
    /// <remarks>SWP_NOCOPYBITS</remarks>
    DoNotCopyBits = 0x0100,
    /// <summary>Retains the current position (ignores X and Y parameters).</summary>
    /// <remarks>SWP_NOMOVE</remarks>
    IgnoreMove = 0x0002,
    /// <summary>Does not change the owner window's position in the Z order.</summary>
    /// <remarks>SWP_NOOWNERZORDER</remarks>
    DoNotChangeOwnerZOrder = 0x0200,
    /// <summary>Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to
    /// the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent
    /// window uncovered as a result of the window being moved. When this flag is set, the application must
    /// explicitly invalidate or redraw any parts of the window and parent window that need redrawing.</summary>
    /// <remarks>SWP_NOREDRAW</remarks>
    DoNotRedraw = 0x0008,
    /// <summary>Same as the SWP_NOOWNERZORDER flag.</summary>
    /// <remarks>SWP_NOREPOSITION</remarks>
    DoNotReposition = 0x0200,
    /// <summary>Prevents the window from receiving the WM_WINDOWPOSCHANGING message.</summary>
    /// <remarks>SWP_NOSENDCHANGING</remarks>
    DoNotSendChangingEvent = 0x0400,
    /// <summary>Retains the current size (ignores the cx and cy parameters).</summary>
    /// <remarks>SWP_NOSIZE</remarks>
    IgnoreResize = 0x0001,
    /// <summary>Retains the current Z order (ignores the hWndInsertAfter parameter).</summary>
    /// <remarks>SWP_NOZORDER</remarks>
    IgnoreZOrder = 0x0004,
    /// <summary>Displays the window.</summary>
    /// <remarks>SWP_SHOWWINDOW</remarks>
    ShowWindow = 0x0040,
}

and in your code :

internal static void MoveWindow(IntPtr intPtr, int X, int Y)
{
    NativeMethods.SetWindowPos(intPtr, IntPtr.Zero, X, Y, 0 , 0, NativeMethods.SetWindowPosFlags.DoNotChangeOwnerZOrder  | NativeMethods.SetWindowPosFlags.IgnoreResize);
}

You can improve this if you want all windows centered, but that's a good start ;)

Edit : To improve, you can get size of each windows in the EnumWindows

List<AppWindow> listWindows = new List<AppWindow>();

NativeMethods.EnumWindows(delegate(IntPtr hWnd, int lParam)
{
    if (hWnd == lShellWindow) return true;
    if (!NativeMethods.IsWindowVisible(hWnd)) return true;

    // may be some other checks

    NativeMethods.RECT rct;

    if (NativeMethods.GetWindowRect(hWnd, out rct))
    {
        listWindows.Add(new AppWindow()
        {
            Handle = hWnd,
            Width = rct.Right - rct.Left,
            Height = rct.Bottom - rct.Top,
        });
    }
    return true;
}, 0);

Use GetWindowRect and RECT :

[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int Left;        // x position of upper-left corner
    public int Top;         // y position of upper-left corner
    public int Right;       // x position of lower-right corner
    public int Bottom;      // y position of lower-right corner
}

and a class named AppWindow :

internal class AppWindow
{
    private IntPtr handle ;
    private Int32 height;
    private Int32 width;

    public IntPtr Handle
    {
        get { return this.handle; }
        set { this.handle = value; }
    }

    public Int32 Height
    {
        get { return this.height; }
        set { this.height= value; }
    }

    public Int32 Width
    {
        get { return this.width; }
        set { this.width= value; }
    }
}

Now, you can calculate where to move the windows

Edit : How to get center of the primary screen ? Easy with WinForms :

int width = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Width;
int height = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Height;

Now, the center of the screen is width/2and height/2

Edit : (to conclude)

foreach (AppWindow app in listWindows)
{
    this.MoveWindow(app.Handle, (this.width / 2) - (app.Width/2), (this.height / 2) - (app.Height/2));
}

and I think it's good ... (I haven't try exactly this solution, but it's very close of one of my projects)

Xaruth
  • 4,034
  • 3
  • 19
  • 26