8

The following .bat file below simply opens two text files overlaying them, but I'm wondering if it's possible to define a specific display source, or if anyone can assist in providing the correct parameters.

@echo off

START /max /wait NOTEPAD.EXE C:\test\screen1.txt

START /max /wait NOTEPAD.EXE C:\test\screen2.txt

What I'm trying to get:

@echo off

START /max /wait NOTEPAD.EXE C:\test\screen1.txt "monitor1"  
START /max /wait NOTEPAD.EXE C:\test\screen2.txt "monitor2"

So the results I am trying to receive is that screen1.txt opens on monitor1, and screen2.txt on monitor2.

PeeHaa
  • 71,436
  • 58
  • 190
  • 262
Johnny Thrust
  • 81
  • 1
  • 1
  • 2
  • try ti change `PNPDeviceID` with the `Win32_DesktopMonitor` wmi class: – npocmaka Apr 20 '13 at 09:04
  • http://msdn.microsoft.com/en-us/library/windows/desktop/aa394591(v=vs.85).aspx - you can list devices with `wmic path Win32_DesktopMonitor get /format:list` – npocmaka Apr 20 '13 at 09:05
  • @npocmaka - All the [properties of Win32_DesktopMonitor](http://msdn.microsoft.com/en-us/library/windows/desktop/aa394122%28v=vs.85%29.aspx), including `PNPDeviceID`, are read-only. – rojo Apr 20 '13 at 12:36

4 Answers4

3

Unless the application you're launching has a command-line switch for it, there's no easy way to specify on which monitor to display a window. As far as I'm aware, neither start nor notepad supports such a switch. The closest solution I've found is to move a window after it's already open.

Edit: user32.dll SetWindowPos() invoked from PowerShell

Here's a hybrid batch + PowerShell script to launch a program and move it to a specific monitor. Save it with a .bat extension.

<# : batch portion
@echo off & setlocal disabledelayedexpansion

set args=%*
call set args=%%args:%1 %2=%%
set "exe=%~2"
set "monitor=%~1"
set "scriptname=%~nx0"
powershell -noprofile "iex (${%~f0} | out-string)"
exit /b %ERRORLEVEL%

: end batch / begin powershell #>

function usage() {
    write-host -nonewline "Usage: "
    write-host -f white "$env:scriptname monitor# filename [arguments]`n"
    write-host -nonewline "* "
    write-host -f white -nonewline "monitor# "
    write-host "is a 1-indexed integer.  Monitor 1 = 1, monitor 2 = 2, etc."
    write-host -nonewline "* "
    write-host -f white -nonewline "filename "
    write-host "is an executable or a document or media file.`n"
    write-host -nonewline "$env:scriptname mimics "
    write-host -f white -nonewline "start"
    write-host ", searching for filename both in %PATH% and"
    write-host "in Windows' app paths (web browsers, media players, etc).`n"
    write-host "Examples:"
    write-host "To display YouTube in Firefox on your second monitor, do"
    write-host -f white "     $env:scriptname 2 firefox `"www.youtube.com`"`n"
    write-host "To play an mp3 file using the default player on monitor 1:"
    write-host -f white "     $env:scriptname 1 mp3file.mp3"
    exit 1
}

add-type user32_dll @'
    [DllImport("user32.dll")]
    public static extern void SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter,
        int x, int y, int cx, int cy, uint uFlags);
'@ -namespace System

add-type -as System.Windows.Forms
if ($env:monitor -gt [windows.forms.systeminformation]::MonitorCount) {
    [int]$monitor = [windows.forms.systeminformation]::MonitorCount
} else {
    [int]$monitor = $env:monitor
}
try {
    if ($env:args) {
        $p = start $env:exe $env:args -passthru
    } else {
        $p = start $env:exe -passthru
    }
}
catch { usage }

$shell = new-object -COM Wscript.Shell
while (-not $shell.AppActivate($p.Id) -and ++$i -lt 100) { sleep -m 50 }

try {
    $x = [Windows.Forms.Screen]::AllScreens[--$monitor].Bounds.X
    $hwnd = (Get-Process -id $p.Id)[0].MainWindowHandle
    [user32_dll]::SetWindowPos($hwnd, [intptr]::Zero, $x, 0, 0, 0, 0x41);
}
finally { exit 0 }

Original answer: compile and link c# executable

And moving a window is no easy task, either. See this post for some other options. But here's a batch script that will compose and link a C# app on the fly to handle window moves.

@echo off
setlocal

:: // generate c.cs
call :heredoc movewind >"%temp%\c.cs" && goto compile_and_link
// syntax: movewind.exe [pid | "window title"] x y
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

class movewind {
    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);

    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    static void Main(string[] args) {
        int pid;
        string title;
        bool res = Int32.TryParse(args[0], out pid);
        if (res) {title = Process.GetProcessById(pid).MainWindowTitle;} else {title = args[0];}
        IntPtr handle = FindWindow(null, title);
        try {
            SetWindowPos(handle, IntPtr.Zero, Convert.ToInt32(args[1]), Convert.ToInt32(args[2]), 0, 0, 0x41);
        }
        catch (Exception e) {
            Console.WriteLine("Exception caught while attempting to move window with handle " + handle);
            Console.WriteLine(e);
        }
    }
}
:compile_and_link

set "movewind=%temp%\movewind.exe"

for /f "delims=" %%I in ('dir /b /s "%windir%\microsoft.net\*csc.exe"') do (
    if not exist "%movewind%" "%%I" /nologo /out:"%movewind%" "%temp%\c.cs" 2>NUL
)
del "%temp%\c.cs"
if not exist "%movewind%" (
    echo Error: Please install .NET 2.0 or newer.
    goto :EOF
)

:: // get left monitor width
for /f "tokens=2 delims==" %%I in ('wmic desktopmonitor get screenwidth /format:list') do set "x=%%I"

:: // make sure test environment is in place
if not exist "c:\test" mkdir "c:\test"
if not exist "c:\test\screen1.txt" >"c:\test\screen1.txt" echo This should be on the left.
if not exist "c:\test\screen2.txt" >"c:\test\screen2.txt" echo This should be on the right.

:: // syntax: movewind.exe [pid | "window title"] x y
start /max notepad.exe "c:\test\screen1.txt"
call :movewind "screen1.txt - Notepad" 0 0
start /max notepad.exe "c:\test\screen2.txt"
call :movewind "screen2.txt - Notepad" %x% 0

del "%movewind%"

:: // end main runtime
goto :EOF

:: // SCRIPT FUNCTIONS

:movewind <title> <x> <y>
tasklist /v | find /i "%~1" && (
    "%movewind%" "%~1" %~2 %~3
    goto :EOF
) || (
    ping -n 1 -w 500 169.254.1.1 >NUL
    goto movewind
)

:heredoc <uniqueIDX>
:: // https://stackoverflow.com/a/15032476/1683264
setlocal enabledelayedexpansion
set go=
for /f "delims=" %%A in ('findstr /n "^" "%~f0"') do (
    set "line=%%A" && set "line=!line:*:=!"
    if defined go (if #!line:~1!==#!go::=! (goto :EOF) else echo(!line!)
    if "!line:~0,13!"=="call :heredoc" (
        for /f "tokens=3 delims=>^ " %%i in ("!line!") do (
            if #%%i==#%1 (
                for /f "tokens=2 delims=&" %%I in ("!line!") do (
                    for /f "tokens=2" %%x in ("%%I") do set "go=%%x"
                )
            )
        )
    )
)
goto :EOF
rojo
  • 24,000
  • 5
  • 55
  • 101
  • If you're going to go this far, why not compile a generic application once and be done with it? This is a maintenance nightmare. – Bacon Bits Jun 29 '15 at 14:29
  • 1
    It's also a turnkey solution for batch scripters unfamiliar with DLL calls to move windows. It allows scripters to include the code in their batch scripts without requiring any 3rd party executables or downloads. It might be rewritable as a [PowerShell script](http://blogs.technet.com/b/heyscriptingguy/archive/2013/06/25/use-powershell-to-interact-with-the-windows-api-part-1.aspx), though, if the need ever arises. – rojo Jun 29 '15 at 14:34
2

cmdow will do this for you.

I use it but please be aware that AV flags it as a virus. There is an explanation why this is the case given by the author on the github page link above.

Usage

Get a list of all windows & find your window in the list.

cmdow

Move the window.

You need specify the window handle in hex or use the window title.

cmdow "window title" /mov 500 500
tinmac
  • 2,357
  • 3
  • 25
  • 41
0

@ebloch helped me find a solution: -Move the cmd window to the desired location on the screen(s). -Right-click. -Select "Properties". -Select "Layout". -Uncheck box "Let system position window". -Click "OK"

The cmd window now opens in the location I dragged it to automatically when I run the .bat file.

-5

(Win 7) To specify location to open a CMD window just run the batch or CMD and then position the CMD window where you want it, including on which monitor if you have more than one, and then right click on its title bar, select Properties, and click OK. The next time the CMD window opens using the same batch or icon it will be in the new location.

ebloch
  • 15
  • 2
  • This doesn't work on Windows 10 and as the author states, he is opening notepad twice with different file names so this won't be a solution for the given task. – SaschaM78 Jan 27 '16 at 17:56
  • Not sure about Win 10 but maybe run Notepad using two batches would work. Alternative may be copying Notepad.exe and renaming copy Notepad2.exe and then specifying where to run each. – ebloch Jan 27 '16 at 18:13
  • On Win 10 Pro can use Nircmd MultiMonitorTool to move and position windows on any monitor. – ebloch Apr 28 '20 at 16:48