2026年5月20日

 windows 10, 如何使用 batch , vbs 將桌面上的視窗移至特定位置?
這些腳本語言缺乏直接控制 Windows GUI 視窗 API(如 MoveWindow)的能力。

powershell 腳本過於複雜,有其它方法嗎?

 windows 10, 如何使用 batch , vbs 將桌面上的視窗移至特定位置?

在 Windows 10 中,單純使用原生 Batch 或 VBScript 是無法直接移動第三方視窗的,因為這些腳本語言缺乏直接控制 Windows GUI 視窗 API(如 MoveWindow)的能力。
要達成這個目的,最有效率的原生做法是使用 PowerShell,或是用 VBScript 呼叫 PowerShell/Excel 內建的 API
以下為您提供三種最實用的解決方案:

方案一:使用 VBScript 呼叫 PowerShell(推薦,免安裝最方便)

這個方法最安全,因為它完全使用 Windows 10 內建的組件。我們利用 VBScript 啟動 PowerShell,並透過 PowerShell 呼叫 Windows API (user32.dll) 來移動視窗。
請建立一個名為 move_window.vbs 的檔案,並寫入以下程式碼:
' 設定目標視窗標題與位置
Dim windowTitle, x, y, width, height
windowTitle = "記事本"  ' 改為您的目標視窗標題(支援部分比對)
x = 100                 ' 畫面 X 座標
y = 100                 ' 畫面 Y 座標
width = 800             ' 視窗寬度
height = 600            ' 視窗高度

' 組合 PowerShell 指令
Dim psCmd
psCmd = "powershell -NoProfile -Command """ & _
"[[void]][System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); " & _
"$code = @' " & vbCrLf & _
"[DllImport(\""user32.dll\"")] " & vbCrLf & _
"public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); " & vbCrLf & _
"[DllImport(\""user32.dll\"")] " & vbCrLf & _
"public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); " & vbCrLf & _
"'@; " & _
"$type = Add-Type -MemberDefinition $code -Name Win32 -PassThru; " & _
"$ws = New-Object -ComObject Shell.Application; " & _
"$win = $ws.Windows() | Where-Object { $_.LocationName -like '*" & windowTitle & "*' -or $_.Name -like '*" & windowTitle & "*' }; " & _
"if (!$win) { $processes = Get-Process | Where-Object { $_.MainWindowTitle -like '*" & windowTitle & "*' }; " & _
"foreach ($p in $processes) { $type::MoveWindow($p.MainWindowHandle, " & x & ", " & y & ", " & width & ", " & height & ", $true) } }" & _
""""

' 執行指令(隱藏視窗背景執行)
CreateObject("Wscript.Shell").Run psCmd, 0, True

方案二:使用純 PowerShell 腳本(程式碼最乾淨)

如果您不一定要用 .vbs.bat 副檔名,直接寫成 .ps1 檔案是 Windows 10 系統下最標準的做法。
請建立一個 MoveWindow.ps1 檔案:
# 定義 Windows API
$API = @'
[DllImport("user32.dll")]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
'@
Add-Type -MemberDefinition $API -Name WindowModifier -Namespace Win32

# 設定目標與參數
$ProcessName = "notepad" # 執行緒名稱(例如記事本為 notepad)
$X = 200
$Y = 200
$Width = 1024
$Height = 768

# 取得視窗控制代碼並移動
$Target = Get-Process -Name $ProcessName -ErrorAction SilentlyContinue
if ($Target) {
    [Win32.WindowModifier]::MoveWindow($Target.MainWindowHandle, $X, $Y, $Width, $Height, $true)
}
  • 如何執行:您可以寫一個簡易的 .bat 檔案來啟動它:
    powershell -ExecutionPolicy Bypass -File "C:\路徑\MoveWindow.ps1"
    

方案三:使用 VBScript 模擬鍵盤快速鍵(僅能靠邊或最大化)

如果您的目的只是想把視窗「靠左」、「靠右」或「最大化」,不需要精準的座標(如 X=123),可以用 VBScript 模擬 Windows 10 的快捷鍵(Win + 方向鍵)。
請建立 snap_window.vbs
Set WshShell = CreateObject("WScript.Shell")

' 1. 先將目標視窗帶到最前端(以記事本為例)
WshShell.AppActivate "記事本"
WScript.Sleep 500 ' 等待視窗反應

' 2. 模擬按下 Win + 左方向鍵 (將視窗靠左側拼貼)
' 註:VBScript 無法直接模擬 Win 鍵,但可以透過組合鍵或複製特定行為。
' 這裡改用通用組合鍵:Alt + Space,再按 M (移動),再用方向鍵移動
WshShell.SendKeys "% "
WScript.Sleep 100
WshShell.SendKeys "M"
WScript.Sleep 100
WshShell.SendKeys "{LEFT 50}" ' 往左移動 50 次
WshShell.SendKeys "{ENTER}"   ' 確認位置
(注意:此方法容易受到當前使用者滑鼠點擊干擾,精準度較低。)

如果您想讓腳本更符合您的需求,請告訴我:
  • 您想要移動的目標程式是什麼?(例如:Chrome 瀏覽器、LINE、或是特定的資料夾?)
  • 您的具體移動規則是什麼?(例如:固定移到右下角、或者是偵測雙螢幕移動?)

 cmd for commad, for /r "dir" %i (*) 中,若 dir 要使用另外一個 loop 變數 "%~j" 或者 "!F!" 為何不能使用?

在 Windows 批次檔(Batch)中,FOR /R 的設計有一個核心限制:它的搜尋路徑必須在指令「解析(Parse)階段」就確定,而不能在「執行(Execute)階段」動態改變。
以下是無法使用的具體原因與解決方法:

為什麼不能用?

  • %~j(巢狀迴圈變數)失敗原因FOR /R 語法規定,路徑參數必須是一個靜態字串或環境變數。當它嵌套在另一個迴圈內時,FOR /R 會在外部迴圈開始執行「前」就先被解析,此時 %~j 還沒有被賦值,導致語法錯誤。
  • !F!(延遲環境變數)失敗原因FOR /R 不支援延遲變數擴展(Delayed Expansion)作為其路徑。即使開啟了 setlocal enabledelayedexpansionFOR /R 仍會在處理 !F! 之前就完成路徑解析,導致它把 !F! 當成一個名為 !F! 的字面字串路徑。

解決方法

如果你需要動態改變 FOR /R 的搜尋路徑,請使用以下兩種標準替代方案:

方案一:使用 PUSHD 切換目錄(最推薦)

不要把變數傳給 /R,而是先用 PUSHD 切換到該動態路徑,然後直接執行 FOR /R(不指定路徑,預設為當前目錄)。
@echo off
setlocal enabledelayedexpansion

:: 假設 %~j 或 !F! 的路徑存在變數 dirPath 中
set "dirPath=C:\Your\Dynamic\Path"

pushd "!dirPath!"
for /r %%i in (*) do (
    echo %%i
)
popd

方案二:利用 CALL 呼叫子程序

FOR /R 移到一個獨立的子程序(Label)中,並透過參數將動態路徑傳遞過去。此時參數會變成靜態的 %1FOR /R 就能正常解析。
@echo off

:: 外部迴圈範例,假設從某處取得了變數 %%j
for /d %%j in (C:\Test\*) do (
    call :SubLoop "%%~j"
)
goto :eof

:SubLoop
:: 此時 %1 是從外部傳進來的靜態路徑
for /r "%~1" %%i in (*) do (
    echo %%i
)
goto :eof

如果想進一步調整你的腳本,可以告訴我:
  • 你的外部迴圈 %~j如何取得路徑的?(例如:讀取文字檔、遍歷資料夾?)
  • 這個腳本的最終目的是什麼?(例如:複製檔案、修改副檔名?)
我可以幫你寫出最精簡且能直接執行的完整程式碼。