Launch an application and send it to second monitor?
Since the window is not yours, you can only move it by invoking the Windows API. You will have to do this:
Launch the process.
Use
FindWindow
to retrieve the handle to the window. If the window doesn’t exist yet, the process hasn’t created it yet; sleep for 500ms and then try again. (But don’t go into an infinite loop; stop if you can’t find the window after a reasonable timeout.)Use
SetWindowPos
to change the position of the window.
If you don’t know the title of the window, you can’t use FindWindow
. In that case,
Launch the process and get the process handle by retrieving
Process.Handle
.Use
EnumWindows
to retrieve all the windows. For each window, useGetWindowThreadProcessId
to check whether it belongs to your process. If no window belongs to your process, wait and keep trying.Use
SetWindowPos
to change the position of the window.
Of course, you can use Screen.AllScreens[n].WorkingArea
to retrieve the position and size of the screen you want, and then you can position the window relative to that.
how to launch a second application in a second screen in WPF?
Even though it is a WPF app you can still reference System.Windows.Forms.
Simply right click on the project in solution explorer, add references, and select System.Windows.Forms.
Then use something like this: (you will also need a reference to System.Drawing)
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.MaximizeToSecondaryMonitor();
}
public void MaximizeToSecondaryMonitor()
{
var secondaryScreen = Screen.AllScreens.Where(s => !s.Primary).FirstOrDefault();
if (secondaryScreen != null)
{
var workingArea = secondaryScreen.WorkingArea;
this.Left = workingArea.Left;
this.Top = workingArea.Top;
this.Width = workingArea.Width;
this.Height = workingArea.Height;
if (this.IsLoaded)
{
this.WindowState = WindowState.Maximized;
}
}
}
}
code taken from here
How to programmatically start an application on a specific monitor on Windows 10?
As suggested by others, this is intended behavior of Windows and for the good reasons.
Also, you can not rely on default window placement atleast for SumatraPDF, since it surely does not use CW_USEDEFAULT, and instead stores these values in :
%USERPROFILE%\AppData\Roaming\SumatraPDF\SumatraPDF-settings.txt
There are multiple options though:
- Use third party tools that monitor top level windows and based on pre-configured rules moves them to specified display. E.g. DisplayFusion, etc.
- Use ligher weight solutions like AutoHotkey/AutoIt.
- Try and do this in code itself. Following is a working solution. I smoke tested on my box.
Disclaimer: I have not written the entire code, for saving time I pulled it up from couple of sources, tweaked it, glued it together, and tested with SumantraPDF. Do also note, that this code is not of highest standards, but solves your problem, and will act as a can-be-done example.
C++ code: (scroll down for C# code)
#include <Windows.h>
#include <vector>
// 0 based index for preferred monitor
static const int PREFERRED_MONITOR = 1;
struct ProcessWindowsInfo
{
DWORD ProcessID;
std::vector<HWND> Windows;
ProcessWindowsInfo(DWORD const AProcessID)
: ProcessID(AProcessID)
{
}
};
struct MonitorInfo
{
HMONITOR hMonitor;
RECT rect;
};
BOOL WINAPI EnumProcessWindowsProc(HWND hwnd, LPARAM lParam)
{
ProcessWindowsInfo *info = reinterpret_cast<ProcessWindowsInfo*>(lParam);
DWORD WindowProcessID;
GetWindowThreadProcessId(hwnd, &WindowProcessID);
if (WindowProcessID == info->ProcessID)
{
if (GetWindow(hwnd, GW_OWNER) == (HWND)0 && IsWindowVisible(hwnd))
{
info->Windows.push_back(hwnd);
}
}
return true;
}
BOOL CALLBACK Monitorenumproc(HMONITOR hMonitor, HDC hdc, LPRECT lprect, LPARAM lParam)
{
std::vector<MonitorInfo> *info = reinterpret_cast<std::vector<MonitorInfo>*>(lParam);
MonitorInfo monitorInfo = { 0 };
monitorInfo.hMonitor = hMonitor;
monitorInfo.rect = *lprect;
info->push_back(monitorInfo);
return TRUE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
// NOTE: for now this code works only when the window is not already visible
// could be easily modified to terminate existing process as required
SHELLEXECUTEINFO info = { 0 };
info.cbSize = sizeof(info);
info.fMask = SEE_MASK_NOCLOSEPROCESS;
info.lpFile = L"C:\\Program Files\\SumatraPDF\\SumatraPDF.exe";
info.nShow = SW_SHOW;
std::vector<MonitorInfo> connectedMonitors;
// Get all available displays
EnumDisplayMonitors(NULL, NULL, Monitorenumproc, reinterpret_cast<LPARAM>(&connectedMonitors));
if (ShellExecuteEx(&info))
{
WaitForInputIdle(info.hProcess, INFINITE);
ProcessWindowsInfo Info(GetProcessId(info.hProcess));
// Go though all windows from that process
EnumWindows((WNDENUMPROC)EnumProcessWindowsProc, reinterpret_cast<LPARAM>(&Info.ProcessID));
if (Info.Windows.size() == 1)
{
// only if we got at most 1 window
// NOTE: applications can have more than 1 top level window. But at least for SumtraPDF this works!
if (connectedMonitors.size() >= PREFERRED_MONITOR)
{
// only move the window if we were able to successfully detect available monitors
SetWindowPos(Info.Windows.at(0), 0, connectedMonitors.at(PREFERRED_MONITOR).rect.left, connectedMonitors.at(PREFERRED_MONITOR).rect.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
}
CloseHandle(info.hProcess);
}
return 0;
}
To emphasize one of my comments in code. This code will only work if the process in question is not already running. You can tweak the code as per your requirements otherwise.
Update: Added C# code below, as I realized OP prefers C#. This code also has the termination logic cooked in.
C# code:
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
private const int SWP_NOSIZE = 0x0001;
private const int SWP_NOZORDER = 0x0004;
private const int PREFERRED_MONITOR = 1;
static void Main(string[] args)
{
// NOTE: you will have to reference System.Windows.Forms and System.Drawing (or
// equivalent WPF assemblies) for Screen and Rectangle
// Terminate existing SumatraPDF process, else we will not get the MainWindowHandle by following method.
List<Process> existingProcesses = Process.GetProcessesByName("SumatraPDF").ToList();
foreach (var existingProcess in existingProcesses)
{
// Ouch! Ruthlessly kill the existing SumatraPDF instances
existingProcess.Kill();
}
// Start the new instance of SumantraPDF
Process process = Process.Start(@"C:\Program Files\SumatraPDF\SumatraPDF.exe");
// wait max 5 seconds for process to be active
process.WaitForInputIdle(5000);
if (Screen.AllScreens.Length >= PREFERRED_MONITOR)
{
SetWindowPos(process.MainWindowHandle,
IntPtr.Zero,
Screen.AllScreens[PREFERRED_MONITOR].WorkingArea.Left,
Screen.AllScreens[PREFERRED_MONITOR].WorkingArea.Top,
0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
}
What determines the monitor my app runs on?
Correctly written Windows apps that want to save their location from run to run will save the results of GetWindowPlacement()
before shutting down, then use SetWindowPlacement()
on startup to restore their position.
Frequently, apps will store the results of GetWindowPlacement()
in the registry as a REG_BINARY
for easy use.
The WINDOWPLACEMENT
route has many advantages over other methods:
- Handles the case where the screen resolution changed since the last run:
SetWindowPlacement()
will automatically ensure that the window is not entirely offscreen - Saves the state (minimized/maximized) but also saves the restored (normal) size and position
- Handles desktop metrics correctly, compensating for the taskbar position, etc. (i.e. uses "workspace coordinates" instead of "screen coordinates" -- techniques that rely on saving screen coordinates may suffer from the "walking windows" problem where a window will always appear a little lower each time if the user has a toolbar at the top of the screen).
Finally, programs that handle window restoration properly will take into account the nCmdShow
parameter passed in from the shell. This parameter is set in the shortcut that launches the application (Normal, Minimized, Maximize):
if(nCmdShow != SW_SHOWNORMAL)
placement.showCmd = nCmdShow; //allow shortcut to override
For non-Win32 applications, it's important to be sure that the method you're using to save/restore window position eventually uses the same underlying call, otherwise (like Java Swing's setBounds()
/getBounds()
problem) you'll end up writing a lot of extra code to re-implement functionality that's already there in the WINDOWPLACEMENT
functions.
Start program on a second monitor?
Save the window position before program shutdown and restore them on startup. Multimonitor displays just increase the size of the desktop; other monitor surfaces just have a different section of the same X/Y plane with its origin at the top-left of the primary monitor.
This can be done automatically for you by any of several components.
BTW, the Screen variable in the Forms unit has a property called MonitorCount and another indexable property, Monitors[Index: Integer]: TMonitor. TMonitor has properties indicating the left, top, width, height etc., so all the information you need is there.
Related Topics
C# Linq Using Null or Empty Strings in a Where Statment
Post Byte Array to Web API Server Using Httpclient
Regular Expression for Anything But an Empty String
Return Json, But It Includes Backward Slashes "\", Which I Don't Want
How to Format a Number in C# With Commas and Decimals
Regex to Remove All Special Characters from String
Newtonsoft.Json Serializeobject Without Escape Backslashes
Most Efficient Way to Compare Two Ienumerables (Or Lists) in Linq
How to Write Data to a Text File Without Overwriting the Current Data
Build Query String for System.Net.Httpclient Get
How to Match Hyphens With Regular Expression
C# Web API Sending Body Data in Http Post Rest Client
How to Format Number as Money Using Regex
Extract First Element from Json
Asp.Net Web Api:Correct Way to Return a 401/Unauthorised Response
Lambda Where Id Does Not Exist in Another List
How to Encrypt a Password Within Appsettings.Json for ASP.NET Core 2