【PowerShell】ループ処理をボタンでオンオフできるようにする(と放置してもスリープさせない方法)(2024/02/22更新)

スポンサーリンク
powershell
スポンサーリンク

放置しているリモート接続中のパソコンが勝手にスリープやスクリーンセーバー(画面ロック)になるのを防ぎたい

私は職場で2つのPCを利用しているが、別々の拠点に置いているため片方のPCにはリモートデスクトップ接続をしている。そうすると、ローカルPC側で作業をしている時、リモート接続中のPCは無操作状態となってしまうため、スクリーンセーバーが起動し画面ロックされてしまうことが多々ある。

画面ロックされるまでの時間が長ければいいのだが、私の職場では一律5分で画面ロックされるように設定されているため、しょっちゅう画面ロックされてはパスワードを入力してロック解除をする・・・という手間が発生している。5分の時間を変更することも出来ない状態になっている。

そこで、放置しているリモート接続中のパソコンが勝手にスリープやスクリーンセーバー(画面ロック)になるのを防ぐプログラムを考えることにした。

決してテレワーク中に無操作でスリープ状態にならないように開発したわけではないのであしからず。
この記事により,何かしらの問題,不利益が起こっても,当サイトでは責任を負いかねます

画面ロックを防ぐプログラムの仕様(搭載機能)

  • あくまで業務上不都合の無いように画面ロックされるのを防ぐため、動作としては「定期的に矢印キーの↓、矢印キーの↑を押下した結果を繰り返す」ことにした。デフォルトの動作間隔は10秒とする。動作間隔はユーザーがテキストボックスに値を入力することで指定できるようにする。
  • 利用する度にスクリプトの実行と終了を行うのは面倒なので、ダイアログにオンオフボタンを表示して、「オン(実行中)」の間は上記定期動作を行い、「オフ(停止中)」の間は何も行わないようにすることにした。
  • 進捗状況を表示する進捗バーを表示することにする。
画面イメージ

コード

絶対改善余地があるけどこんな感じ。

# アセンブリ読み込み
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

### ユーザー定義 ###

# loggerの設定
function logger($msg) {
    Write-Host (Get-Date -Format "u")"`t"$msg
}

# 動作間隔(秒)
$moveInterval = 10  # 10秒ごとに動作。デフォルト値

# カウンタ初期化
$counter = 0

### OnOffフォーム要素 ###

# OnOffフォーム
$OnOffForm = New-Object System.Windows.Forms.Form 
$OnOffForm.Size = New-Object System.Drawing.Size(550,250) 
$OnOffForm.StartPosition = "CenterScreen"
$OnOffForm.Text = "DownUP"
$OnOffForm.TopLevel = $True
$OnOffForm.MaximizeBox = $False

# OnOffラベル1
$label1OnOff = New-Object System.Windows.Forms.Label
$label1OnOff.Location = New-Object System.Drawing.Point(10,10)
$label1OnOff.AutoSize = $true
$label1OnOff.Font = New-Object System.Drawing.Font("Arial", 10) 
$label1OnOff.Text = "DownUPを開始しますか?"

# OnOffラベル2
$label2OnOff = New-Object System.Windows.Forms.Label
$label2OnOff.Location = New-Object System.Drawing.Point(270,50)
$label2OnOff.AutoSize = $true
$label2OnOff.Font = New-Object System.Drawing.Font("Arial", 12) 
$label2OnOff.Text = "停止中"
$label2OnOff.TextAlign = "BottomCenter"

# OnOff-Start/Stopボタン
$StartButtonOnOff = New-Object -TypeName System.Windows.Forms.CheckBox
$StartButtonOnOff.location = New-Object System.Drawing.Point(200,50)
$StartButtonOnOff.Autosize = $true
$StartButtonOnOff.Font = New-Object System.Drawing.Font("Arial", 12) 
$StartButtonOnOff.Text = "Start"
$StartButtonOnOff.TextAlign = "MiddleCenter"
$StartButtonOnOff.Appearance = "Button"
$StartButtonOnOff.FlatStyle = "System"

# OnOff-ツール終了ボタン
$CloseButtonOnOff = New-Object System.Windows.Forms.Button
$CloseButtonOnOff.Location = New-Object System.Drawing.Point(215,170)
$CloseButtonOnOff.AutoSize = $true
$CloseButtonOnOff.Font = New-Object System.Drawing.Font("Arial", 10) 
$CloseButtonOnOff.Text = "ツールを閉じる"
$CloseButtonOnOff.DialogResult = [System.Windows.Forms.DialogResult]::cancel

# 進捗バー
$ProgressBar = New-Object System.Windows.Forms.ProgressBar
$ProgressBar.Location = New-Object System.Drawing.Point(10, 130)
$ProgressBar.Size = New-Object System.Drawing.Size(500, 20)
$ProgressBar.Minimum = 0

# 動作間隔テキストボックス
$IntervalTextBox = New-Object System.Windows.Forms.TextBox
$IntervalTextBox.Location = New-Object System.Drawing.Point(270, 95)
$IntervalTextBox.Size = New-Object System.Drawing.Size(40, 20)
$IntervalTextBox.Font = New-Object System.Drawing.Font("Arial", 12) 
$IntervalTextBox.Text = $moveInterval

# 動作間隔ラベル
$IntervalLabel = New-Object System.Windows.Forms.Label
$IntervalLabel.Location = New-Object System.Drawing.Point(10,100)
$IntervalLabel.AutoSize = $true
$IntervalLabel.Font = New-Object System.Drawing.Font("Arial", 10) 
$IntervalLabel.Text = "定期実行間隔を秒数で指定してください"

# OnOffフォームへのラベル等構成要素の配置
$OnOffForm.Controls.AddRange(@($label1OnOff,$label2OnOff,$StartButtonOnOff,$CloseButtonOnOff,$ProgressBar,$IntervalTextBox,$IntervalLabel))

# イベントハンドラ
$StartButtonOnOff.add_CheckedChanged({
    if ($StartButtonOnOff.Checked) {
        
        # 動作間隔テキストボックスの値が半角数字であるか確認
        if ($IntervalTextBox.Text -match '^[0-9]+$') {
            $moveInterval = [int]$IntervalTextBox.Text

            # for-whileループ処理の停止フラグ
            $stopFlag = $false

            # 進捗バーの最大値を動作間隔に設定
            $ProgressBar.Maximum = $moveInterval  

            $StartButtonOnOff.Text = 'Stop'
            $label2OnOff.Text = "実行中"
            $label2OnOff.BackColor = "yellow"
            $label2OnOff.Font = New-Object System.Drawing.Font("Arial", 12, [System.Drawing.FontStyle]::Bold)
            $label1OnOff.Text = "DownUPを停止しますか?"
        
            # ループ処理
            while ($StartButtonOnOff.Checked -and !$stopFlag) {

                # GUIイベントの更新を強制
                [System.Windows.Forms.Application]::DoEvents()

                # ツールを閉じるボタンが押されたかどうかを確認
                if (-not $OnOffForm.Visible) {
                    $stopFlag = $true
                    break
                }

                # Stopボタンが押されたかどうかを確認
                if (-not $StartButtonOnOff.Checked) {
                    $stopFlag = $true
                    break
                }

                #ループカウンタ
                $counter++
                logger "Count: $counter"

                # 矢印↓ボタンを押下
                [System.Windows.Forms.SendKeys]::SendWait("{DOWN}")

                # 余分な移動が発生しないように、キーダウンとキーアップの間に少し待機
                Start-Sleep -Seconds 1

                # 矢印↑ボタンを押下
                [System.Windows.Forms.SendKeys]::SendWait("{UP}")
            
                for ($i = 0; $i -lt $moveInterval; $i++) {

                    # GUIイベントの更新を強制
                    [System.Windows.Forms.Application]::DoEvents()

                    # ツールを閉じるボタンが押されたかどうかを確認
                    if (-not $OnOffForm.Visible) {
                        $stopFlag = $true
                        break
                    }

                    # Stopボタンが押されたかどうかを確認
                    if (-not $StartButtonOnOff.Checked) {
                        $stopFlag = $true
                        break
                    }

                    if($i -eq 0){
                        $ProgressBar.Value = $ProgressBar.Maximum
                    }
            
                    Start-Sleep -Seconds 1  # 1秒Sleep

                    # 進捗バーの更新(動作間隔から経過時間を引いた値)
                    $ProgressBar.Value = $moveInterval - $i - 1

                }

            }

        } else {
                # 動作間隔テキストボックスに半角数字以外が入力されている場合はエラーメッセージ表示
                [System.Windows.Forms.MessageBox]::Show("動作間隔には半角数字を入力してください。", "エラー", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
                # スタートボタンをオフに戻す
                $StartButtonOnOff.Checked = $false
        }
    } else {
        $StartButtonOnOff.Text = 'Start'
        $label2OnOff.Text = "停止中"
        $label2OnOff.ResetBackColor()
        $label2OnOff.Font = New-Object System.Drawing.Font("Arial", 12)
        $label1OnOff.Text = "DownUPを開始しますか?"
    }
})

# イベントハンドラ
$CloseButtonOnOff.add_Click({
    $OnOffForm.Close()
    $OnOffForm.Dispose()
})

# OnOffフォームを表示
[Windows.Forms.Application]::Run($OnOffForm)

コード解説

# 動作間隔(秒)
$moveInterval = 10  # 10秒ごとに動作。デフォルト値

ここは定期実行させる動作間隔を指定するパラメータである。
デフォルト(初期値)は10秒としている。

# カウンタ初期化
$counter = 0

何回目のループ処理かをカウントするためのパラメータである。
今回の目的に照らすと、機能として必要というわけではないが、別の形で利用する際にあれば便利かと思い追加した(定期的なWebスクレイピング、疎通確認など・・・)

# イベントハンドラ
$StartButtonOnOff.add_CheckedChanged({
    if ($StartButtonOnOff.Checked) {
        
        # 動作間隔テキストボックスの値が半角数字であるか確認
        if ($IntervalTextBox.Text -match '^[0-9]+$') {
            $moveInterval = [int]$IntervalTextBox.Text

            # for-whileループ処理の停止フラグ
            $stopFlag = $false

            # 進捗バーの最大値を動作間隔に設定
            $ProgressBar.Maximum = $moveInterval  

            $StartButtonOnOff.Text = 'Stop'
            $label2OnOff.Text = "実行中"
            $label2OnOff.BackColor = "yellow"
            $label2OnOff.Font = New-Object System.Drawing.Font("Arial", 12, [System.Drawing.FontStyle]::Bold)
            $label1OnOff.Text = "DownUPを停止しますか?"
        
            # ループ処理
            while ($StartButtonOnOff.Checked -and !$stopFlag) {

                # GUIイベントの更新を強制
                [System.Windows.Forms.Application]::DoEvents()

                # ツールを閉じるボタンが押されたかどうかを確認
                if (-not $OnOffForm.Visible) {
                    $stopFlag = $true
                    break
                }

                # Stopボタンが押されたかどうかを確認
                if (-not $StartButtonOnOff.Checked) {
                    $stopFlag = $true
                    break
                }

                #ループカウンタ
                $counter++
                logger "Count: $counter"

                # 矢印↓ボタンを押下
                [System.Windows.Forms.SendKeys]::SendWait("{DOWN}")

                # 余分な移動が発生しないように、キーダウンとキーアップの間に少し待機
                Start-Sleep -Seconds 1

                # 矢印↑ボタンを押下
                [System.Windows.Forms.SendKeys]::SendWait("{UP}")
            
                for ($i = 0; $i -lt $moveInterval; $i++) {

                    # GUIイベントの更新を強制
                    [System.Windows.Forms.Application]::DoEvents()

                    # ツールを閉じるボタンが押されたかどうかを確認
                    if (-not $OnOffForm.Visible) {
                        $stopFlag = $true
                        break
                    }

                    # Stopボタンが押されたかどうかを確認
                    if (-not $StartButtonOnOff.Checked) {
                        $stopFlag = $true
                        break
                    }

                    if($i -eq 0){
                        $ProgressBar.Value = $ProgressBar.Maximum
                    }
            
                    Start-Sleep -Seconds 1  # 1秒Sleep

                    # 進捗バーの更新(動作間隔から経過時間を引いた値)
                    $ProgressBar.Value = $moveInterval - $i - 1

                }

            }

        } else {
                # 動作間隔テキストボックスに半角数字以外が入力されている場合はエラーメッセージ表示
                [System.Windows.Forms.MessageBox]::Show("動作間隔には半角数字を入力してください。", "エラー", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
                # スタートボタンをオフに戻す
                $StartButtonOnOff.Checked = $false
        }
    } else {
        $StartButtonOnOff.Text = 'Start'
        $label2OnOff.Text = "停止中"
        $label2OnOff.ResetBackColor()
        $label2OnOff.Font = New-Object System.Drawing.Font("Arial", 12)
        $label1OnOff.Text = "DownUPを開始しますか?"
    }
})

# イベントハンドラ
$CloseButtonOnOff.add_Click({
    $OnOffForm.Close()
    $OnOffForm.Dispose()
})

# OnOffフォームを表示
[Windows.Forms.Application]::Run($OnOffForm)

Startボタンが押下されたかどうかをイベントハンドラとして、定期ループ処理のオンオフを制御している。青色で囲われた部分がオン(実行中)の処理、赤色で囲われた部分がオフ(停止中)の処理である。

[System.Windows.Forms.SendKeys]::SendWait("{DOWN}")は、Windows フォームアプリケーションの SendKeys クラスを使用して、キーボードのキーをシミュレートしている。具体的には、下向きの矢印キー({DOWN})が押されたときと同じ動作をする。SendWaitメソッドは、キーストロークをウィンドウに送り、アクティブなウィンドウが受け取るようにする。この場合、下向きの矢印キーが送信される。

このスクリプトの全体の流れでは、このコマンドが無限ループ内(while)で使用され、Downキーが押され、1秒待機後にUpキーが押されている。これにより、DownとUpのキーが定期的に繰り返される。定期動作間隔は、$moveIntervalに指定した秒数だけ間隔を空けるようにしている。

また、動作途中でもfor-whileループから抜け出せるように、変数$stopFlag がtrueの場合は処理から抜けるようにしている。変数$stopFlag は、処理中にStopボタン($StartButtonOnOff)やツールを閉じるボタン($OnOffForm)が押下されたときにtrueとなるようにしている。

[System.Windows.Forms.Application]::DoEvents()はコメントにも記載している通り、GUIの更新を強制するメソッドである。この処理がないとfor文処理中にStopボタンやツールを閉じるボタンを押してもfor文の処理($moveInterval分の秒数)が経過しないと操作に反応しない問題が発生する。

あと、半角数字以外が動作間隔を指定するテキストボックスに入力された時は、エラーメッセージを表示するように if ($IntervalTextBox.Text -match '^[0-9]+$')で条件分岐させている。

注意点と補足

  • PowerShellが実行可能な環境である必要があります。

改版履歴

2024/02/22 初版公開。一部更新。

コメント

タイトルとURLをコピーしました