打开文件时指定监视器(.bat)

9
下面的.bat文件只是简单地打开两个文本文件并重叠它们,但我想知道是否可能定义特定的显示源,或者是否有人能提供正确的参数。
@echo off

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

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

我想要得到什么:

@echo off

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

我想要的结果是,screen1.txt 文件在 monitor1 上打开,而 screen2.txt 文件在 monitor2 上打开。


尝试使用Win32_DesktopMonitor WMI类替换PNPDeviceID - npocmaka
你可以使用 wmic path Win32_DesktopMonitor get /format:list 命令列出设备。详见:http://msdn.microsoft.com/zh-cn/library/windows/desktop/aa394591(v=vs.85).aspx - npocmaka
@npocmaka - 包括 PNPDeviceID 在内的所有 Win32_DesktopMonitor 属性 都是只读的。 - rojo
4个回答

3
除非您要启动的应用程序有命令行开关,否则没有简单的方法来指定在哪个显示器上显示窗口。据我所知,既不支持start也不支持notepad这样的开关。我找到的最接近的解决方案是在窗口打开后移动它。

编辑:从PowerShell调用user32.dll SetWindowPos()

这是一个混合批处理+PowerShell脚本,可以启动程序并将其移动到特定的监视器上。将其保存为.bat扩展名。

<# : 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 }

原始回答:编译和链接c#可执行文件
而移动窗口也不是一件容易的事情。参见此帖子获取其他选项。但这里有一个批处理脚本,可以即时组合和链接一个C#应用程序来处理窗口移动。
@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://dev59.com/L3NA5IYBdhLWcg3wUL9Y#15032476
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

如果你要这么做,为什么不编译一个通用的应用程序,并完成它呢?这将是一个维护噩梦。 - Bacon Bits
1
这也是一种即插即用的解决方案,适用于不熟悉 DLL 调用移动窗口的批处理脚本编写者。它允许脚本编写者在其批处理脚本中包含代码,而无需任何第三方可执行文件或下载。如果需要,它可以被重写为 PowerShell 脚本 - rojo

2

Cmdow可以为您完成此操作。

我使用它,但请注意,防病毒软件会将其标记为病毒。作者在上面的 Github 页面链接中解释了这种情况的原因。

用法

获取所有窗口的列表并在列表中查找您的窗口。

cmdow

移动窗口。

您需要指定十六进制的窗口句柄或使用窗口标题。

cmdow "window title" /mov 500 500

0

@ebloch帮我找到解决方案: -将cmd窗口移动到屏幕上所需的位置。 -右键单击。 -选择“属性”。 -选择“布局”。 -取消选中“让系统定位窗口”的框。 -点击“确定”

现在,当我运行.bat文件时,cmd窗口会自动打开在我拖动它的位置。


-5
(Win 7)指定CMD窗口的位置,只需运行批处理或CMD,然后将CMD窗口放置在您想要的位置,包括如果您有多个监视器,则在哪个监视器上,然后右键单击其标题栏,选择“属性”,并单击“确定”。下一次使用相同的批处理程序或图标打开CMD窗口时,它将位于新位置。

这在Windows 10上不起作用,正如作者所述,他正在使用不同的文件名打开记事本两次,因此这不是给定任务的解决方案。 - SaschaM78
不确定在 Windows 10 上是否可以正常运行,但也许可以使用两个批处理文件来运行记事本。另一种方法是复制 Notepad.exe 并将副本重命名为 Notepad2.exe,然后指定每个程序的运行位置。 - ebloch
在Win 10 Pro上,可以使用Nircmd MultiMonitorTool在任何显示器上移动和定位窗口。 - ebloch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接