各位高手,大家午安!
2021年拜讀了「Zero皇」大大的這篇 PowerShell 文章:
https://ithelp.ithome.com.tw/articles/10279723
小弟試著依樣畫葫蘆,把每天早上要做的一件事自動化,不但方便而且還很有成就感...
但自動化的過程中,還是有一些因為才疏學淺而做不到的,上網斷斷續續爬文,始終沒答案。
今天想想,還是來這裡請教各位高手,也許能事半功倍!
我困擾的部份主要發生在 AppActivate 上
TestAppAct.ps1 內容如下:
"測試程序即將於2 秒後啟動,請暫停操作系統"
timeout /t 2
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic")
#上面應該是呼叫 MS VB 進來
"將 「LINE」 視窗移至前景"
Microsoft.VisualBasic.Interaction::AppActivate("LINE")
#上面有時成功,有時出現錯誤:
以 "1" 引數呼叫 "appactivate" 時發生例外狀況: "找不到處理序 '{0}'。"
位於 H:\TestAppAct.ps1:7 字元:2
" "
"暫停2秒"
Start-Sleep -Seconds 2
" "
"將 「Chrome」 視窗移至前景"
Microsoft.VisualBasic.Interaction::AppActivate("Chrome") #多半成功
" "
"暫停2秒"
Start-Sleep -Seconds 2
" "
"將 「Word」 視窗移至前景" #失敗,在工作到閃動,不會到前景...
Microsoft.VisualBasic.Interaction::AppActivate("Word")
" "
"程序結束,5秒後本視窗將自動關閉"
Start-Sleep -Seconds 5
不知道是不是有更「穩定」的方式(或指令)來將某個執行中的視窗移到前景?
稍微打一下程式,你在自己按照需求修改就好了
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class User32 {
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
}
"@
#使用WinAPI來讓視窗達到最大化跟前台顯示
#https://learn.microsoft.com/zh-tw/windows/win32/api/winuser/nf-winuser-showwindow
#https://learn.microsoft.com/zh-tw/windows/win32/api/winuser/nf-winuser-setforegroundwindow
##等待Line啟動
do{
if ((Get-Process -Name "Line" -WarningAction SilentlyContinue |measure).Count >0){
Write-Host ("偵測到有LINE出現");
break;
}
}while($true)
##下一步偵測有沒有在前景執行
if (((Get-Process -Name "Line").MainWindowTitle).Length==0){
#沒有在前台執行,再次執行APP,並且等待LINE前台執行成功
Start-Process -Wait "Line的程式路徑"
#如果預設在appdata底下,可用 Start-Process -Wait ($env:LOCALAPPDATA+"\LINE\bin\LineLauncher.exe")
#下方為確認LINE有執行出來
do{
if (((Get-Process -Name "Line").MainWindowTitle).Length>0){
break
}
}while($true)
}
#Word視窗狀況調整
$WORD = Get-Process -Name "WINWORD" -WarningAction SilentlyContinue
if ($WORD -ne $null){
$hwnd = $WORD.MainWindowHandle
##讓視窗放大
[User32]::ShowWindow($hwnd, 3)
##讓視窗在前面
[User32]::SetForegroundWindow($hwnd)
}
備註:Line縮小到工作表(Tray),跟WORD縮小到工作表(TaskBar),兩種程式狀態啟動到前台的方式不一樣。
Dear mobone前輩,謝謝您的回應,我照您的指點試了;但在第一關就卡住了,我加了一些提示當「執行指標」,請您幫忙看一下,是不是有哪裡要修改?(卡在檢查 LINE 是否啟動)
程式如下: (TestAppAct2.ps1)
"測試程序即將於2 秒後啟動,請暫停操作系統"
timeout /t 2
" "
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class User32 {
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
}
"@
" "
"使用「WinAPI」來讓視窗達到最大化跟前台顯示"
" "
#參考: https://learn.microsoft.com/zh-tw/windows/win32/api/winuser/nf-winuser-showwindow
#參考: https://learn.microsoft.com/zh-tw/windows/win32/api/winuser/nf-winuser-setforegroundwindow
"步驟一,檢查 LINE 是否已啟動?"
do{
if ((Get-Process -Name "Line" -WarningAction SilentlyContinue |measure).Count >0){
Write-Host ("偵測到有LINE出現");
break;
}
}while($true)
" "
Pause
" "
"下一步偵測有沒有在前景執行,若否,則啟動 LINE"
if (((Get-Process -Name "Line").MainWindowTitle).Length==0){
#沒有在前台執行,再次執行APP,並且等待LINE前台執行成功
"$env:LOCALAPPDATA"
Start-Process -Wait ($env:LOCALAPPDATA+"\LINE\bin\LineLauncher.exe")
#如果預設在appdata底下,可用 Start-Process -Wait ($env:LOCALAPPDATA+"\LINE\bin\LineLauncher.exe")
#C:\Users\TeRRy\AppData\Local\LINE\bin
#下方為確認LINE有執行出來
do{
if (((Get-Process -Name "Line").MainWindowTitle).Length>0){
break
}
}while($true)
}
#Word視窗狀況調整
$WORD = Get-Process -Name "WINWORD" -WarningAction SilentlyContinue
if ($WORD -ne $null){
$hwnd = $WORD.MainWindowHandle
##讓視窗放大
[User32]::ShowWindow($hwnd, 3)
##讓視窗在前面
[User32]::SetForegroundWindow($hwnd)
}
Pause
" "
"程序結束,5秒後本視窗將自動關閉"
Start-Sleep -Seconds 5
P.S. 程式執行時,有一個現象,在 TestAppAct2.ps1 資料夾中,會自動新增一個檔案,檔名是「0」,大小是 0 byte
Dear mobone 前輩:
我也單獨試了一下 WORD 的部份,如果 WORD 沒執行會報錯(正常)
如果WORD「最小化」或是「還原」,它會變成「最大化」
如果小弟只是希望它「還原」,請問指令應如何修改呢?
你要不要把你的程式碼丟到github上我在幫你確認,在留言這邊看有點亂
如果你沒有git 你就丟到google文件分享給我看
可參考備註的nf-winuser-showwindow的網址,內有寫到
1為還原2為最小3為最大,
[User32]::ShowWindow($hwnd, 3)
把數字3改掉就好
你先開起LINE,然後打這行指令在PS內是否有顯示
Get-Process -Name "line"
正常應該有1個以上才對
https://github.com/moboneOnIT/Questions10215516/blob/main/ithelp.ps1
剛剛下班後稍微修改成func模式你應該會比較好用,你可以參考看看
謝謝 mobone 前輩,
慚愧!慚愧!
[User32]::ShowWindow($hwnd, 3) 我只試了2
因為我猜「1為最小、2為還原、3為最大」 (猜錯...)
那個數字你要參照微軟的資料,
https://learn.microsoft.com/zh-tw/windows/win32/api/winuser/nf-winuser-showwindow#parameters
我也沒全部講解完就是了
有,試了 Get-Process -Name "line"
回報有1個 LINE
那你現在就看你要用哪種叫出來的模式,下面有兩種
ShowAppFromTaskBar "EXE名稱" 模式數字 #從工作表叫出
ShowAppFromTray "EXE名稱" 模式數字 #從右下角小圖區域叫出
我上面GIT裡面的程式碼記得要copy進去你的ps1內
Dear mobone前輩,謝謝您的回應!
我試了一下,有把您的程式加入到我的ps1檔案中了
整合及測試完成再回覆您,謝謝您寶貴的時間!
先將您的回覆設為最佳解法,感謝您,也謝謝其他前輩們的幫忙!
有問題再問沒問題,我看到我會回答的
Dear mobone 前輩:
又來請教您了,請問您上次賜教的這一段,用意是什麼?
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class User32 {
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
}
"@
再麻煩您如果看到,撥冗回覆小弟,謝謝您!
這是調用Windows內建的dll(函式庫),我在備註內有打
由於這算是底層的程式,比較不會有BUG發生,
上面原回答「#使用WinAPI來讓視窗達到最大化跟前台顯示」
的下面有提供網址可以讓你參考