質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

Q&A

解決済

1回答

4100閲覧

Powershellの無限ループを仮想キー押下で脱出したい

kshi

総合スコア1

PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

0グッド

0クリップ

投稿2021/02/08 16:36

編集2021/02/08 16:59

共用PCで利用者のログをとるため、Powershellで疑似的に画面をロックするコードを書いています。
Inputboxが表示されており、そこにパスワードを入力すると、Inputboxが消えます。

  • Inputboxに入力するパスワードは「pass」です。
  • 10秒間マウスが動かなければInputboxが再度ポップアップします。
  • InputboxでEnter空打ちやEscapeでスクリプトを終了します。

 →下記コードではコメントアウト(スクリプトを終わらせるには強制終了させる)

  • 10秒経過しなくても、Escapeを押すと、意図的にInputboxを出すことができます。

 →ここが問題
PowershellのコンソールがアクティブなときはEscapeが効きますが、
他のアプリが前面にあるとEscapeが効きません。
他のアプリが前面にあってもEscapeが効くようにしたいです

以下、処理の骨組みだけにしたコードです。

SleepAndWaitKey関数の仮想キーコード判定がおかしいわけですが、
どう解決したらよいのかわからないでおります。

実行環境はWindows10で、.ps1ファイルを右クリックから「Powershellで実行」しています。

# Microsoft.VisualBasicアセンブリを有効化 [void][System.Reflection.Assembly]::Load( ` "Microsoft.VisualBasic, Version=8.0.0.0, ` Culture=Neutral, PublicKeyToken=b03f5f7f11d50a3a") $rawui = $host.UI.RawUI [void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") $keyMap = [Windows.Forms.Keys] function SleepAndWaitKey($sec) { $LimitTime = (Get-Date).AddSeconds($sec) #指定時刻(10秒後)までループ While ((Get-Date) -lt $LimitTime) { while ($rawui.KeyAvailable) { $keyinput = $rawui.Readkey("NoEcho,IncludeKeyUp,IncludeKeyDown") if ($keyinput.VirtualKeycode -eq $keymap::Escape) { #Escが押されたら[画面ロック]に戻る return $true } } Start-Sleep 0.5 } return $false } while (1) { $input = [Microsoft.VisualBasic.Interaction]:: ` InputBox("パスワード", "画面ロック") if ($input -eq "") { #break #画面ロック終了 } elseif ($input -ne "pass") { continue #パスワード不一致 → 画面ロック } while (1) { #待機前のマウス位置 $Position = [System.Windows.Forms.Cursor]::Position $LastMousePosX = $Position.x $LastMousePosY = $Position.y #指定秒数待機 If (SleepAndWaitKey(10) -eq $true) { break } #待機後のマウス位置 $Position = [System.Windows.Forms.Cursor]::Position #マウス位置が変わらない → [画面ロック] If (($Position.x -eq $LastMousePosX) ` -And ($Position.y -eq $LastMousePosY)) { break } } }

ぜひお知恵を貸してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2021/02/08 22:43

> 他のアプリが前面にあってもEscapeが効くようにしたいです。 キーボードフックしなければいけないので PowerShell では困難です。
Zuishin

2021/02/08 23:09

PowerShell の本質はシェルで、その本分は他の実行コマンドを組み合わせるグルー言語です。大量のボンドをこねて箱を作るより、木の板をボンドで張り合わせて作る方が簡単です。難しいことは C# で行うのが良いと思います。
ku__ra__ge

2021/02/09 04:08

AutoHotkeyで、エスケープが押されたらPowershellのコンソールをアクティブにしてEscapeキー入力を流し込むようにすればいいんじゃないでしょうか。
kshi

2021/02/09 09:33

Zuishin様、ご返信ありがとうございます。キー入力を監視する以外にうまい方法がないか検討します。 ku__ra__ge様、ご返信ありがとうございます。そもそもAutoHotkeyだけで全部やれそうですよね。フリーソフトを気軽にインストールできない環境なので、最初からWindows10に入っているPowershellで何とかできないか試行錯誤しております。
ku__ra__ge

2021/02/10 01:59

この記事が参考になると思います。 https://qiita.com/nicco_mirai/items/513ae714d3d9c688cdab 上記URLはPowerShellからVB.NETを通じてWindowsAPIを呼び出してキーボード入力を別の入力に化けさせるというジョークプログラムですが、このコードからキーボード入力を検知する箇所を抜き出せば他のアプリが前面にあってもEscapeを検知して処理を実行することができると思います。 うまく行った場合、自己回答でEscapeを検知して処理を実行するコードを書き込んでもらえると後でこの質問を参照する人が助かると思います。
kshi

2021/02/10 09:35

ku__ra__ge様、ご返信ありがとうございます。 ご教示いただいたページを含め、Win32APIを研究中です。FindWindowでコンソールを探してShowWindowで非表示にする別懸案には成功しており、最後の懸案がキー入力のフックです。
guest

回答1

0

自己解決

一応、自己解決できたかな、というコードです。

Win32APIのSetWindowsHookEx、UnhookWindowsHookEx、CallNextHookExは使い方がよくわかりませんでした。GetAsyncKeyStateを試したらあっさり解決しました。

先頭で以下の宣言を追加します。

PowerShell

1$code = @' 2 [DllImport("user32.dll")] 3 public static extern IntPtr GetAsyncKeyState(long vKey); 4'@ 5Add-Type $code -Name Utils -Namespace Win32 6

以下のようにキーの押下状態を取得して判定します。(18)(46)は仮想キーコード値です。

PowerShell

1 $alt = [Win32.Utils]::GetAsyncKeyState(18) 2 $del = [Win32.Utils]::GetAsyncKeyState(46) 3 if ($alt -ne 0 -and $del -ne 0) { 4 Write-Host "キー入力で中断" 5 return $true 6 } 7

全体のコードは以下の通りです。

  • 実行すると、最初にInputBoxが出ます。
  • 「pass」と入力すると、InputBoxが閉じます。
  • 10秒経過すると再度InputBoxが出ます。
  • 10秒経過しないうちに、AltとDelを同時に長押し(1秒程度)すると意図的にInputBoxを出すことができます。
  • 他のアプリ上にいても動作します。

PowerShell

1# Microsoft.VisualBasicアセンブリを有効化 2[void][System.Reflection.Assembly]::Load( ` 3 "Microsoft.VisualBasic, Version=8.0.0.0, ` 4 Culture=Neutral, PublicKeyToken=b03f5f7f11d50a3a") 5 6$code = @' 7 [DllImport("user32.dll")] 8 public static extern IntPtr GetAsyncKeyState(long vKey); 9'@ 10Add-Type $code -Name Utils -Namespace Win32 11 12[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 13 14function SleepAndWaitKey($sec) { 15 16 Write-Host "待機開始" 17 $LimitTime = (Get-Date).AddSeconds($sec) 18 19 #指定時刻(10秒後)までループ 20 While ((Get-Date) -lt $LimitTime) { 21 $alt = [Win32.Utils]::GetAsyncKeyState(18) 22 $del = [Win32.Utils]::GetAsyncKeyState(46) 23 if ($alt -ne 0 -and $del -ne 0) { 24 Write-Host "キー入力で中断" 25 return $true 26 } 27 Start-Sleep 1 28 } 29 Write-Host "待機終了" 30 return $false 31} 32 33while (1) { 34 35 $input = [Microsoft.VisualBasic.Interaction]:: ` 36 InputBox("パスワード: 「pass」と入力してください", "画面ロック") 37 38 if ($input -ne "pass") { 39 continue #パスワード不一致 → 画面ロック 40 } 41 42 while (1) { 43 #待機前のマウス位置 44 $Position = [System.Windows.Forms.Cursor]::Position 45 $LastMousePosX= $Position.x 46 $LastMousePosY= $Position.y 47 48 #指定秒数待機 49 If (SleepAndWaitKey(10) -eq $true) { 50 break 51 } 52 53 #待機後のマウス位置 54 $Position = [System.Windows.Forms.Cursor]::Position 55 56 #マウス位置が変わらない → [画面ロック] 57 If (($Position.x -eq $LastMousePosX) ` 58 -And ($Position.y -eq $LastMousePosY)) { 59 break 60 } 61 } 62 63}

私の用途ではこれで十分です。

投稿2021/02/11 15:53

kshi

総合スコア1

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問