The following shows how to use System.Diagnostics.Process
to start a program (.exe) that has a window and once that program has started, move its window to a specified point (x, y) on the specified monitor (ex: 1, 2, etc...).
It's been tested with UT512/UT513 Interface Program (UT512 UT513 software installation file).
Create a class (name: Helper)
File Helper.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
namespace MoveWindowTest
{
public class Helper
{
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
public enum SpecialWindowHandles : int
{
/// <summary>
/// Places the window at the top of the Z order.
/// </summary>
HWND_TOP = 0,
/// <summary>
/// Places the window at the bottom of the Z order. If
/// the hWnd parameter identifies a topmost window, the
/// window loses its topmost status and is placed at
/// the bottom of all other windows.
/// </summary>
HWND_BOTTOM = 1,
/// <summary>
/// Places the window above all non-topmost windows. The
/// window maintains its topmost position even when
/// it is deactivated.
/// </summary>
HWND_TOPMOST = -1,
/// <summary>
/// Places the window above all non-topmost windows (that
/// is, behind all topmost windows). This flag has no
/// effect if the window is already a non-topmost window.
/// </summary>
HWND_NOTOPMOST = -2
}
[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>
SWP_ASYNCWINDOWPOS = 0x4000,
/// <summary>
/// Prevents generation of the WM_SYNCPAINT message.
/// </summary>
SWP_DEFERERASE = 0x2000,
/// <summary>
/// Draws a frame (defined in the window's class
/// description) around the window.
/// </summary>
SWP_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>
SWP_FRAMECHANGED = 0x0020,
/// <summary>
/// Hides the window.
/// </summary>
SWP_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>
SWP_NOACTIVATE = 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>
SWP_NOCOPYBITS = 0x0100,
/// <summary>
/// Retains the current position (ignores X and Y parameters).
/// </summary>
SWP_NOMOVE = 0x0002,
/// <summary>
/// Does not change the owner window's position in the Z order.
/// </summary>
SWP_NOOWNERZORDER = 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>
SWP_NOREDRAW = 0x0008,
/// <summary>
/// Same as the SWP_NOOWNERZORDER flag.
/// </summary>
SWP_NOREPOSITION = 0x0200,
/// <summary>
/// Prevents the window from receiving the WM_WINDOWPOSCHANGING message.
/// </summary>
SWP_NOSENDCHANGING = 0x0400,
/// <summary>
/// Retains the current size (ignores the cx and cy parameters).
/// </summary>
SWP_NOSIZE = 0x0001,
/// <summary>
/// Retains the current Z order (ignores
/// the hWndInsertAfter parameter).
/// </summary>
SWP_NOZORDER = 0x0004,
/// <summary>
/// Displays the window.
/// </summary>
SWP_SHOWWINDOW = 0x0040,
}
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[DllImport("User32.dll")]
public static extern Int32 SetWindowPos(IntPtr hwnd,
SpecialWindowHandles hwndInsertAfter,
int x,
int y,
int cx,
int cy,
SetWindowPosFlags flags);
public static System.Drawing.Point GetCurrentWindowPosition(IntPtr hwnd)
{
Point p = new Point(-1, -1);
RECT rect = new RECT();
if (GetWindowRect(hwnd, ref rect))
{
p = new Point(rect.Left, rect.Top);
}
return p;
}
public static void MoveWindowToMonitor(IntPtr hwnd, int monitorNumber, int x, int y)
{
RECT rect = new RECT();
if (GetWindowRect(hwnd, ref rect))
{
SetWindowPos(hwnd,
SpecialWindowHandles.HWND_TOP,
System.Windows.Forms.Screen.AllScreens[monitorNumber].WorkingArea.Left + x,
System.Windows.Forms.Screen.AllScreens[monitorNumber].WorkingArea.Top + y,
rect.Right,
rect.Bottom,
SetWindowPosFlags.SWP_SHOWWINDOW | SetWindowPosFlags.SWP_NOSIZE);
}
}
public static void StartProcess(string fullyQualifiedExeFilename,
string windowTitle,
int monitorNumber,
int x,
int y)
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = fullyQualifiedExeFilename;
startInfo.CreateNoWindow = true; // Don't create a window
//startInfo.UseShellExecute = true; // If true, uses 'ShellExecute'; if false, uses 'CreateProcess'
startInfo.UseShellExecute = false; // If true, uses 'ShellExecute'; if false, uses 'CreateProcess'
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
using (Process p = new Process { StartInfo = startInfo, EnableRaisingEvents = true })
{
p.Start(); //start
// Don't wait for exit
}
// Give the window time to be created.
// Periodically check if the window has been created yet.
// The sleep time can be adjusted as desired,
// as well as maxCount.
int count = 0;
int maxCount = 50;
IntPtr hwnd = IntPtr.Zero;
do
{
System.Threading.Thread.Sleep(75);
//System.Threading.Thread.Sleep(125);
// Find window
hwnd = FindWindow(null, windowTitle);
//Debug.WriteLine("hwnd: " + hwnd.ToString("X8") + " count: " + count.ToString());
if (hwnd != IntPtr.Zero)
{
break;
}
count++; // Increment
} while (count < maxCount);
// Move window
MoveWindowToMonitor(hwnd, monitorNumber, x, y);
}
}
}
Usage:
string filename = System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
@"DMM\UT512_UT513\UT512_UT513.exe");
if (!System.IO.File.Exists(filename))
{
filename = System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
@"DMM\UT512_UT513\UT512_UT513.exe");
}
if (!System.IO.File.Exists(filename))
{
throw new Exception("Filename '" + filename + "' not found.");
}
// Start 'UT512_UT513 V2.00.exe' and move it to monitor 1; x = 100, y = 200
Helper.StartProcess(filename, "UT512/UT513 Interface Program V2.00", 1, 100, 200);
Note: The window text (title) can be found by using Spy++.
Resources: