Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specify monitor when opening file. (.bat)

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.

like image 510
Johnny Thrust Avatar asked Nov 24 '22 00:11

Johnny Thrust


2 Answers

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
like image 66
rojo Avatar answered Nov 26 '22 14:11

rojo


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
like image 24
tinmac Avatar answered Nov 26 '22 14:11

tinmac