【初心者向け】PowerShellでスリープ制御|PCを自在に操作する基本コマンド集

Windows

パソコンの自動制御に便利なスクリプトツール「PowerShell(パワーシェル)」。

Windowsに標準搭載されており、業務の自動化やバッチ処理に役立つ存在です。

PowerShellが活躍する場面

  • 定期的なデータバックアップ
  • システムメンテナンスの自動化
  • 大量ファイルの一括処理
  • 省エネ対策やセキュリティ管理

中でも「スリープ」は、PCの省エネやセキュリティを考えるうえで重要な機能ですが、「コマンドでどうやって制御するの?」「タイマー的な一時停止もスリープって言うの?」と混乱しがちです。

この記事で学べること

  • PowerShellにおけるスリープの種類
  • スクリプトの一時停止方法
  • PCの電源スリープ制御
  • 実用的な活用例
  • トラブルシューティング

この記事では、PowerShellでのスリープの種類とその制御方法を丁寧に解説。用途別にすぐ使える実例も紹介します。

スポンサーリンク
  1. PowerShellとは(基本知識)
    1. PowerShellの概要
  2. PowerShellにおける「スリープ」の種類
    1. (1)スクリプトの一時停止(処理のスリープ)
    2. (2)PC本体のスリープ(電源制御)
    3. スリープの状態比較
  3. スクリプトを一時停止する方法|Start-Sleepの基本
    1. 基本的な使い方
    2. 実用例1:処理の進捗表示
    3. 実用例2:ファイル監視での待機
    4. 実用例3:API呼び出しの間隔調整
    5. 実用例4:プログレスバー付きの待機
    6. パフォーマンスの考慮事項
  4. PCをスリープ状態にする方法|電源制御の実践
    1. 方法1:rundll32コマンドを使用
    2. 方法2:PowerShell Add-Typeを使用
    3. 方法3:WMI(Windows Management Instrumentation)を使用
    4. 方法4:psshutdownツールの使用
    5. タイマー付きスリープスクリプト
    6. 条件付きスリープスクリプト
    7. 安全なスリープスクリプト
  5. スリープ設定の確認と管理
    1. 現在の電源設定の確認
    2. スリープ設定の変更
    3. ウェイクタイマーの管理
  6. スリープ関連でよくあるトラブルと対処法
    1. トラブル1:rundll32で何も起きない
    2. トラブル2:スリープが勝手に解除される
    3. トラブル3:Start-Sleepが効かないように見える
    4. トラブル4:スリープ後にアプリケーションが異常終了
    5. トラブル5:ノートPCでスリープが機能しない
  7. 実用的なスリープ制御スクリプト
    1. スクリプト1:スマートスリープマネージャー
    2. スクリプト2:タスクスケジューラー連携スリープ
    3. スクリプト3:リモートスリープ制御
  8. 高度なスリープ制御テクニック
    1. スリープ状態の監視
    2. カスタムスリープ条件
    3. スリープ前の自動バックアップ
  9. エラーハンドリングとログ機能
    1. 包括的なエラーハンドリング
    2. ログ分析ツール
  10. GUI付きスリープ制御ツール
    1. Windows Forms を使用したGUIツール
  11. 自動化とスケジューリング
    1. スリープポリシーの実装
  12. まとめ|PowerShellでスリープを自在に扱うテクニック

PowerShellとは(基本知識)

PowerShellの概要

PowerShellの特徴

  • Windowsに標準で搭載
  • .NETフレームワークベース
  • オブジェクト指向のシェル
  • 豊富なコマンドレット(コマンド)

PowerShellの起動方法

1. 「Win + R」キーを押す
2. 「powershell」と入力してEnter
3. または「Win + X」→「Windows PowerShell」を選択

管理者権限での起動

1. スタートメニューで「PowerShell」を検索
2. 右クリック→「管理者として実行」
3. UACで「はい」をクリック

PowerShellにおける「スリープ」の種類

「スリープ」と一口に言っても、PowerShellでは2つの異なる意味があります。この違いを理解することが重要です。

(1)スクリプトの一時停止(処理のスリープ)

概要 スクリプトの実行を一時的に止める機能です。プログラムは動いているが、指定した時間だけ処理を待機させます。

使用する場面

  • API呼び出しの間隔調整
  • ファイル処理の待ち時間
  • ユーザーへの表示時間確保
  • システム負荷の調整

使用するコマンド

Start-Sleep -Seconds 10    # 10秒待機
Start-Sleep -Milliseconds 500  # 500ミリ秒待機

(2)PC本体のスリープ(電源制御)

概要 パソコン全体をスリープ状態(低電力モード)に移行させる処理です。画面が消え、CPUの動作が停止します。

使用する場面

  • 省エネ対策
  • セキュリティ確保
  • 自動メンテナンス後の電源管理
  • 長時間放置時の自動スリープ

使用するコマンド

rundll32.exe powrprof.dll,SetSuspendState 0,1,0

スリープの状態比較

状態電力消費復帰時間データ保持用途
通常動作RAM作業中
スクリプトスリープ即座RAM処理待機
スリープ数秒RAM一時休止
休止状態最低数十秒HDD/SSD長期休止
シャットダウンなし数十秒なし完全停止

まずは「スクリプトを止める」のか「PCを眠らせる」のか、自分の目的を明確にすることが大切です。

スクリプトを一時停止する方法|Start-Sleepの基本

Start-SleepはPowerShellにおけるタイマー機能です。処理の流れを制御するために非常によく使われます。

基本的な使い方

秒単位での指定

Start-Sleep -Seconds 5    # 5秒待機
Start-Sleep 5             # 省略形

ミリ秒単位での指定

Start-Sleep -Milliseconds 500    # 500ミリ秒(0.5秒)待機

実用例1:処理の進捗表示

Write-Host "処理を開始します..."
Start-Sleep -Seconds 2

Write-Host "データを読み込み中..."
Start-Sleep -Seconds 3

Write-Host "データを処理中..."
Start-Sleep -Seconds 5

Write-Host "処理が完了しました!"

実用例2:ファイル監視での待機

# ファイルが作成されるまで待機
$filePath = "C:\temp\output.txt"

while (-not (Test-Path $filePath)) {
    Write-Host "ファイル待機中..."
    Start-Sleep -Seconds 1
}

Write-Host "ファイルが見つかりました!"

実用例3:API呼び出しの間隔調整

# 複数のAPI呼び出しを行う際の間隔調整
$urls = @(
    "https://api.example1.com/data",
    "https://api.example2.com/data",
    "https://api.example3.com/data"
)

foreach ($url in $urls) {
    Write-Host "APIを呼び出し中: $url"
    # ここでAPI呼び出し処理
    Invoke-RestMethod -Uri $url
    
    # 次の呼び出しまで2秒待機(レート制限対策)
    Start-Sleep -Seconds 2
}

実用例4:プログレスバー付きの待機

# プログレスバー付きの待機
$totalSeconds = 10
for ($i = 1; $i -le $totalSeconds; $i++) {
    $percent = ($i / $totalSeconds) * 100
    Write-Progress -Activity "処理待機中" -Status "$i/$totalSeconds 秒" -PercentComplete $percent
    Start-Sleep -Seconds 1
}
Write-Progress -Activity "処理待機中" -Completed
Write-Host "待機完了!"

パフォーマンスの考慮事項

短時間での高精度な待機

# 100ミリ秒未満の場合はミリ秒単位を使用
Start-Sleep -Milliseconds 50    # 推奨
Start-Sleep -Seconds 0.05        # 精度が劣る場合がある

長時間の待機

# 1時間待機
Start-Sleep -Seconds 3600

# または読みやすい形で
Start-Sleep -Seconds (60 * 60)  # 60秒 × 60分 = 1時間

Start-Sleepは覚えておいて損のない基本コマンドです。次に、PC自体をスリープにする方法を紹介します。

PCをスリープ状態にする方法|電源制御の実践

Windows PCをPowerShellでスリープ状態に移行させる方法は複数あります。それぞれの特徴と使い分けを詳しく解説します。

方法1:rundll32コマンドを使用

基本構文

rundll32.exe powrprof.dll,SetSuspendState 0,1,0

パラメータの意味

  • 第1パラメータ(0):休止状態を無効(0=スリープ、1=休止状態)
  • 第2パラメータ(1):強制実行(0=アプリ確認あり、1=強制実行)
  • 第3パラメータ(0):ウェイク許可(0=許可、1=禁止)

事前準備:休止状態の無効化

# 管理者権限で実行
powercfg -hibernate off

完全なスクリプト例

# 管理者権限チェック
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
    Write-Error "このスクリプトは管理者権限で実行してください"
    exit 1
}

Write-Host "5秒後にPCをスリープ状態にします..."
Start-Sleep -Seconds 5

# PCをスリープ状態に移行
rundll32.exe powrprof.dll,SetSuspendState 0,1,0

方法2:PowerShell Add-Typeを使用

より制御しやすい方法

Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;

public class PowerManager {
    [DllImport("powrprof.dll", SetLastError = true)]
    public static extern bool SetSuspendState(bool hibernate, bool forceCritical, bool disableWakeEvent);
}
"@

# スリープ実行
[PowerManager]::SetSuspendState($false, $true, $false)

方法3:WMI(Windows Management Instrumentation)を使用

# WMIクラスを使用したシャットダウン・スリープ
$wmi = Get-WmiObject -Class Win32_OperatingSystem

# スリープ状態に移行(注意:環境によっては動作しない場合があります)
$wmi.SetPowerState(1)

方法4:psshutdownツールの使用

Sysinternals PSToolsのpsshutdownを使用

# 事前にpsshutdownをダウンロードして配置
# https://docs.microsoft.com/en-us/sysinternals/downloads/psshutdown

# スタンバイ(スリープ)モードに移行
psshutdown -d -t 0

タイマー付きスリープスクリプト

指定時間後に自動スリープ

param(
    [int]$Minutes = 30  # デフォルト30分
)

$seconds = $Minutes * 60
Write-Host "あと $Minutes 分後にPCがスリープします"

# カウントダウン表示
for ($i = $seconds; $i -gt 0; $i--) {
    $remainingMinutes = [math]::Floor($i / 60)
    $remainingSeconds = $i % 60
    
    Write-Host "`rスリープまで: ${remainingMinutes}分${remainingSeconds}秒" -NoNewline
    Start-Sleep -Seconds 1
    
    # Escキーでキャンセル機能(オプション)
    if ([Console]::KeyAvailable) {
        $key = [Console]::ReadKey($true)
        if ($key.Key -eq "Escape") {
            Write-Host "`nスリープをキャンセルしました"
            exit
        }
    }
}

Write-Host "`nPCをスリープ状態にします..."
rundll32.exe powrprof.dll,SetSuspendState 0,1,0

条件付きスリープスクリプト

特定の条件でのみスリープ実行

# CPU使用率が低い場合のみスリープ
$cpuThreshold = 10  # CPU使用率10%以下

$cpu = Get-Counter "\Processor(_Total)\% Processor Time"
$cpuUsage = [math]::Round($cpu.CounterSamples[0].CookedValue, 2)

if ($cpuUsage -lt $cpuThreshold) {
    Write-Host "CPU使用率が低いため、スリープします(現在: ${cpuUsage}%)"
    Start-Sleep -Seconds 3
    rundll32.exe powrprof.dll,SetSuspendState 0,1,0
} else {
    Write-Host "CPU使用率が高いため、スリープを見送ります(現在: ${cpuUsage}%)"
}

安全なスリープスクリプト

確認付きスリープ実行

function Confirm-Sleep {
    $response = Read-Host "PCをスリープ状態にしますか? (Y/N)"
    
    switch ($response.ToUpper()) {
        "Y" { 
            Write-Host "スリープを実行します..."
            Start-Sleep -Seconds 2
            rundll32.exe powrprof.dll,SetSuspendState 0,1,0
        }
        "N" { 
            Write-Host "スリープをキャンセルしました"
        }
        default { 
            Write-Host "Y または N を入力してください"
            Confirm-Sleep  # 再帰呼び出し
        }
    }
}

Confirm-Sleep

スリープ設定の確認と管理

現在の電源設定の確認

電源プランの確認

# 現在アクティブな電源プランを表示
powercfg /query SCHEME_CURRENT SUB_SLEEP

# 利用可能な電源プランの一覧
powercfg /list

# スリープ設定の詳細確認
powercfg /query SCHEME_CURRENT SUB_SLEEP STANDBYIDLE

スリープタイマーの確認

# ディスプレイの電源OFF時間
powercfg /query SCHEME_CURRENT SUB_VIDEO VIDEOIDLE

# システムスリープ時間
powercfg /query SCHEME_CURRENT SUB_SLEEP STANDBYIDLE

スリープ設定の変更

コマンドラインでの設定変更

# AC電源接続時のスリープ時間を30分に設定
powercfg /change standby-timeout-ac 30

# バッテリー駆動時のスリープ時間を15分に設定
powercfg /change standby-timeout-dc 15

# ディスプレイの電源OFF時間を10分に設定
powercfg /change monitor-timeout-ac 10

ウェイクタイマーの管理

ウェイクタイマーの確認

# スリープ解除可能なデバイスの確認
powercfg /devicequery wake_armed

# 最後にスリープを解除した理由
powercfg /lastwake

# ウェイクタイマーの一覧
powercfg /waketimers

ウェイクタイマーの無効化

# ウェイクタイマーを無効化
powercfg /change standby-timeout-ac 0

スリープ関連でよくあるトラブルと対処法

トラブル1:rundll32で何も起きない

原因と対処法

# 1. 休止状態が有効になっている
powercfg -hibernate off

# 2. 管理者権限で実行されていない
# → PowerShellを管理者として起動

# 3. 高速スタートアップが干渉している
powercfg /hibernate off

確認用スクリプト

# システムの状態確認
Write-Host "現在の電源設定:"
powercfg /query SCHEME_CURRENT SUB_SLEEP | Select-String "Hibernate\|Standby"

Write-Host "`n管理者権限確認:"
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
Write-Host "管理者権限: $isAdmin"

トラブル2:スリープが勝手に解除される

原因の特定

# 最後にスリープを解除した理由を確認
powercfg /lastwake

# ウェイク可能なデバイスを確認
powercfg /devicequery wake_armed

# アクティブなウェイクタイマーを確認
powercfg /waketimers

対処方法

# マウスやキーボードのウェイク機能を無効化
# デバイスマネージャーで個別設定が必要

# ネットワークアダプターのウェイク機能確認
Get-NetAdapter | Where-Object {$_.Status -eq "Up"} | Get-NetAdapterPowerManagement

# Windows Updateによる自動再起動を無効化
$updateSettings = New-Object -ComObject Microsoft.Update.AutoUpdate
$updateSettings.Settings.NotificationLevel = 1  # 通知のみ

トラブル3:Start-Sleepが効かないように見える

原因と対処法

# 1. 処理が早すぎて気づかない
Write-Host "処理開始: $(Get-Date)"
Start-Sleep -Seconds 5
Write-Host "処理終了: $(Get-Date)"

# 2. 非同期処理と混同している
# → Start-Sleepは同期処理(指定時間必ず待機)

# 3. ミリ秒とマイクロ秒の混同
Start-Sleep -Milliseconds 1000  # 1秒
# Start-Sleep -Microseconds は存在しない

トラブル4:スリープ後にアプリケーションが異常終了

予防策

# スリープ前にアプリケーションを安全に終了
$processesToStop = @("notepad", "calc", "mspaint")

foreach ($processName in $processesToStop) {
    $processes = Get-Process -Name $processName -ErrorAction SilentlyContinue
    if ($processes) {
        Write-Host "$processName を終了中..."
        $processes | Stop-Process -Force
        Start-Sleep -Seconds 1
    }
}

Write-Host "アプリケーション終了完了。スリープを実行します..."
Start-Sleep -Seconds 2
rundll32.exe powrprof.dll,SetSuspendState 0,1,0

トラブル5:ノートPCでスリープが機能しない

確認と対処

# 電源管理ドライバーの状態確認
Get-WmiObject Win32_SystemDriver | Where-Object {$_.Name -like "*power*"}

# BIOS/UEFI設定の確認が必要な場合
Write-Host "以下のBIOS設定を確認してください:"
Write-Host "- ACPI設定"
Write-Host "- Power Management設定"
Write-Host "- USB Power Delivery設定"

# ハードウェア互換性の確認
powercfg /devicequery wake_programmable

実用的なスリープ制御スクリプト

スクリプト1:スマートスリープマネージャー

<#
.SYNOPSIS
スマートスリープマネージャー - 条件に応じて自動でスリープを実行

.DESCRIPTION
CPU使用率、実行中のプロセス、時間帯などを考慮してスリープを実行

.PARAMETER Force
強制的にスリープを実行

.PARAMETER CheckInterval
チェック間隔(分)
#>

param(
    [switch]$Force,
    [int]$CheckInterval = 5
)

function Test-SystemIdle {
    # CPU使用率チェック
    $cpu = Get-Counter "\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 3
    $avgCpu = ($cpu.CounterSamples | Measure-Object CookedValue -Average).Average
    
    # メモリ使用率チェック
    $memory = Get-WmiObject Win32_OperatingSystem
    $memoryUsage = (($memory.TotalVisibleMemorySize - $memory.FreePhysicalMemory) / $memory.TotalVisibleMemorySize) * 100
    
    # ネットワーク活動チェック
    $network = Get-Counter "\Network Interface(*)\Bytes Total/sec" -SampleInterval 1 -MaxSamples 1
    $networkActivity = ($network.CounterSamples | Where-Object {$_.InstanceName -ne "Loopback Pseudo-Interface 1"} | Measure-Object CookedValue -Sum).Sum
    
    Write-Host "システム状態:"
    Write-Host "  CPU使用率: $([math]::Round($avgCpu, 1))%"
    Write-Host "  メモリ使用率: $([math]::Round($memoryUsage, 1))%"
    Write-Host "  ネットワーク活動: $([math]::Round($networkActivity / 1024, 1)) KB/s"
    
    # アイドル判定の閾値
    return ($avgCpu -lt 10 -and $memoryUsage -lt 80 -and $networkActivity -lt 10240)
}

function Test-CriticalProcesses {
    $criticalProcesses = @("sqlservr", "w3wp", "devenv", "Teams", "Zoom")
    
    foreach ($processName in $criticalProcesses) {
        if (Get-Process -Name $processName -ErrorAction SilentlyContinue) {
            Write-Host "重要なプロセスが実行中: $processName"
            return $false
        }
    }
    return $true
}

function Invoke-SmartSleep {
    if ($Force) {
        Write-Host "強制スリープを実行します..."
    } else {
        # 時間帯チェック(22:00-06:00の間のみ自動スリープ)
        $currentHour = (Get-Date).Hour
        if ($currentHour -lt 22 -and $currentHour -gt 6) {
            Write-Host "現在の時間帯では自動スリープを実行しません(22:00-06:00以外)"
            return
        }
        
        # システム状態チェック
        if (-not (Test-SystemIdle)) {
            Write-Host "システムがアイドル状態ではないため、スリープを見送ります"
            return
        }
        
        # 重要プロセスチェック
        if (-not (Test-CriticalProcesses)) {
            Write-Host "重要なプロセスが実行中のため、スリープを見送ります"
            return
        }
        
        Write-Host "条件を満たしたため、スリープを実行します..."
    }
    
    # カウントダウン
    for ($i = 10; $i -gt 0; $i--) {
        Write-Host "`rスリープまで $i 秒..." -NoNewline
        Start-Sleep -Seconds 1
    }
    
    Write-Host "`nスリープを実行中..."
    rundll32.exe powrprof.dll,SetSuspendState 0,1,0
}

# メイン処理
if ($Force) {
    Invoke-SmartSleep
} else {
    Write-Host "スマートスリープマネージャーを開始します(チェック間隔: $CheckInterval 分)"
    
    while ($true) {
        Write-Host "`n$(Get-Date): システム状態をチェック中..."
        Invoke-SmartSleep
        
        Write-Host "次のチェックまで $CheckInterval 分待機..."
        Start-Sleep -Seconds ($CheckInterval * 60)
    }
}

スクリプト2:タスクスケジューラー連携スリープ

<#
.SYNOPSIS
タスクスケジューラーと連携したスリープ制御

.DESCRIPTION
指定時刻にスリープを実行するタスクを登録・管理
#>

function Register-SleepTask {
    param(
        [string]$TaskName = "AutoSleep",
        [string]$Time = "23:00"
    )
    
    $action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-Command `"rundll32.exe powrprof.dll,SetSuspendState 0,1,0`""
    $trigger = New-ScheduledTaskTrigger -Daily -At $Time
    $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
    $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
    
    Register-ScheduledTask -TaskName $TaskName -Action $action -Trigger $trigger -Settings $settings -Principal $principal -Description "自動スリープタスク"
    
    Write-Host "スリープタスクを登録しました: $TaskName (実行時刻: $Time)"
}

function Remove-SleepTask {
    param([string]$TaskName = "AutoSleep")
    
    Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue
    Write-Host "スリープタスクを削除しました: $TaskName"
}

function Show-SleepTasks {
    Get-ScheduledTask | Where-Object {$_.TaskName -like "*Sleep*"} | Select-Object TaskName, State, @{Name="NextRun";Expression={(Get-ScheduledTaskInfo $_.TaskName).NextRunTime}}
}

# 使用例
# Register-SleepTask -TaskName "EveningSleep" -Time "22:30"
# Show-SleepTasks
# Remove-SleepTask -TaskName "EveningSleep"

スクリプト3:リモートスリープ制御

<#
.SYNOPSIS
リモートPCのスリープ制御

.DESCRIPTION
ネットワーク経由で他のPCをスリープ状態にする
#>

function Invoke-RemoteSleep {
    param(
        [string[]]$ComputerName,
        [PSCredential]$Credential
    )
    
    $scriptBlock = {
        rundll32.exe powrprof.dll,SetSuspendState 0,1,0
    }
    
    foreach ($computer in $ComputerName) {
        try {
            Write-Host "リモートスリープを実行中: $computer"
            
            if ($Credential) {
                Invoke-Command -ComputerName $computer -ScriptBlock $scriptBlock -Credential $Credential
            } else {
                Invoke-Command -ComputerName $computer -ScriptBlock $scriptBlock
            }
            
            Write-Host "完了: $computer"
        } catch {
            Write-Error "エラー: $computer - $($_.Exception.Message)"
        }
    }
}

# 使用例
# $cred = Get-Credential
# Invoke-RemoteSleep -ComputerName "PC01", "PC02" -Credential $cred

高度なスリープ制御テクニック

スリープ状態の監視

# スリープ履歴の確認
function Get-SleepHistory {
    param([int]$Days = 7)
    
    $startTime = (Get-Date).AddDays(-$Days)
    
    Get-WinEvent -FilterHashtable @{
        LogName = 'System'
        Id = 42, 1  # スリープ関連のイベントID
        StartTime = $startTime
    } | Select-Object TimeCreated, Id, LevelDisplayName, Message | Sort-Object TimeCreated -Descending
}

カスタムスリープ条件

# ファイル監視によるスリープ制御
function Start-FileTriggerSleep {
    param(
        [string]$WatchPath = "C:\temp\sleep.trigger",
        [int]$CheckInterval = 30
    )
    
    Write-Host "ファイル監視スリープを開始します: $WatchPath"
    
    while ($true) {
        if (Test-Path $WatchPath) {
            Write-Host "トリガーファイルを検出しました。スリープを実行します..."
            Remove-Item $WatchPath -Force
            Start-Sleep -Seconds 3
            rundll32.exe powrprof.dll,SetSuspendState 0,1,0
            break
        }
        
        Write-Host "ファイル監視中... ($(Get-Date))"
        Start-Sleep -Seconds $CheckInterval
    }
}

スリープ前の自動バックアップ

function Invoke-SleepWithBackup {
    param(
        [string]$BackupSource = "C:\Users\$env:USERNAME\Documents",
        [string]$BackupDestination = "D:\Backup",
        [switch]$SkipBackup
    )
    
    if (-not $SkipBackup) {
        Write-Host "スリープ前にバックアップを実行します..."
        
        # バックアップ先ディレクトリの作成
        if (-not (Test-Path $BackupDestination)) {
            New-Item -ItemType Directory -Path $BackupDestination -Force
        }
        
        # 日付付きバックアップフォルダ
        $backupFolder = Join-Path $BackupDestination "Backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
        
        try {
            # ロバコピーを使用した高速バックアップ
            $robocopyArgs = @(
                $BackupSource,
                $backupFolder,
                "/MIR",      # ミラー同期
                "/R:1",      # リトライ回数
                "/W:1",      # 待機時間
                "/NP",       # プログレス非表示
                "/MT:8"      # マルチスレッド
            )
            
            Write-Host "バックアップ開始: $BackupSource → $backupFolder"
            Start-Process -FilePath "robocopy" -ArgumentList $robocopyArgs -Wait -NoNewWindow
            Write-Host "バックアップ完了"
            
        } catch {
            Write-Warning "バックアップでエラーが発生しました: $($_.Exception.Message)"
            $continue = Read-Host "バックアップに失敗しましたが、スリープを続行しますか? (Y/N)"
            if ($continue -ne "Y") {
                Write-Host "スリープをキャンセルしました"
                return
            }
        }
    }
    
    Write-Host "PCをスリープ状態にします..."
    Start-Sleep -Seconds 3
    rundll32.exe powrprof.dll,SetSuspendState 0,1,0
}

エラーハンドリングとログ機能

包括的なエラーハンドリング

function Invoke-SafeSleep {
    param(
        [int]$DelaySeconds = 5,
        [string]$LogPath = "$env:TEMP\SleepLog.txt"
    )
    
    try {
        # ログファイルの初期化
        if (-not (Test-Path $LogPath)) {
            New-Item -ItemType File -Path $LogPath -Force
        }
        
        $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        Add-Content -Path $LogPath -Value "[$timestamp] スリープ処理開始"
        
        # 管理者権限チェック
        $isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
        
        if (-not $isAdmin) {
            $errorMsg = "管理者権限が必要です"
            Add-Content -Path $LogPath -Value "[$timestamp] エラー: $errorMsg"
            throw $errorMsg
        }
        
        # システム状態チェック
        $runningProcesses = Get-Process | Where-Object {$_.ProcessName -in @("Teams", "Zoom", "Skype")}
        if ($runningProcesses) {
            $processNames = $runningProcesses.ProcessName -join ", "
            Add-Content -Path $LogPath -Value "[$timestamp] 警告: 通信アプリが実行中 ($processNames)"
            
            $continue = Read-Host "通信アプリが実行中です。続行しますか? (Y/N)"
            if ($continue -ne "Y") {
                Add-Content -Path $LogPath -Value "[$timestamp] ユーザーによりキャンセル"
                return
            }
        }
        
        # 未保存ファイルの確認
        $unsavedFiles = Get-Process | Where-Object {$_.MainWindowTitle -like "*- 名前未設定*" -or $_.MainWindowTitle -like "**"}
        if ($unsavedFiles) {
            Add-Content -Path $LogPath -Value "[$timestamp] 警告: 未保存ファイルの可能性"
            Write-Warning "未保存のファイルがある可能性があります。保存してください。"
            Start-Sleep -Seconds 10
        }
        
        # カウントダウン
        Write-Host "スリープ実行まで:"
        for ($i = $DelaySeconds; $i -gt 0; $i--) {
            Write-Host "`r  $i 秒..." -NoNewline
            Start-Sleep -Seconds 1
        }
        
        Add-Content -Path $LogPath -Value "[$timestamp] スリープ実行"
        Write-Host "`nスリープを実行しています..."
        
        # スリープ実行
        rundll32.exe powrprof.dll,SetSuspendState 0,1,0
        
    } catch {
        $errorTimestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        $errorMessage = $_.Exception.Message
        Add-Content -Path $LogPath -Value "[$errorTimestamp] エラー: $errorMessage"
        
        Write-Error "スリープの実行に失敗しました: $errorMessage"
        
        # エラー時の代替処理
        $altAction = Read-Host "代替処理を選択してください: [1]再試行 [2]シャットダウン [3]キャンセル"
        switch ($altAction) {
            "1" { 
                Write-Host "再試行します..."
                Invoke-SafeSleep -DelaySeconds $DelaySeconds -LogPath $LogPath
            }
            "2" { 
                Write-Host "シャットダウンを実行します..."
                Stop-Computer -Force
            }
            "3" { 
                Write-Host "処理をキャンセルしました"
            }
            default { 
                Write-Host "無効な選択です。処理をキャンセルします"
            }
        }
    }
}

ログ分析ツール

function Analyze-SleepLog {
    param(
        [string]$LogPath = "$env:TEMP\SleepLog.txt",
        [int]$Days = 30
    )
    
    if (-not (Test-Path $LogPath)) {
        Write-Warning "ログファイルが見つかりません: $LogPath"
        return
    }
    
    $logContent = Get-Content $LogPath
    $startDate = (Get-Date).AddDays(-$Days)
    
    # ログエントリの解析
    $sleepEntries = @()
    $errorEntries = @()
    
    foreach ($line in $logContent) {
        if ($line -match '^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (.+)) {
            $timestamp = [DateTime]::Parse($matches[1])
            $message = $matches[2]
            
            if ($timestamp -ge $startDate) {
                if ($message -like "*スリープ実行*") {
                    $sleepEntries += [PSCustomObject]@{
                        Timestamp = $timestamp
                        Message = $message
                    }
                } elseif ($message -like "*エラー*") {
                    $errorEntries += [PSCustomObject]@{
                        Timestamp = $timestamp
                        Message = $message
                    }
                }
            }
        }
    }
    
    # 統計情報の表示
    Write-Host "=== スリープログ分析結果 (過去 $Days 日) ==="
    Write-Host "スリープ実行回数: $($sleepEntries.Count)"
    Write-Host "エラー発生回数: $($errorEntries.Count)"
    
    if ($sleepEntries.Count -gt 0) {
        $avgSleepTime = $sleepEntries | Group-Object {$_.Timestamp.Hour} | Sort-Object Count -Descending | Select-Object -First 1
        Write-Host "最も多いスリープ時間帯: $($avgSleepTime.Name)時台 ($($avgSleepTime.Count)回)"
    }
    
    if ($errorEntries.Count -gt 0) {
        Write-Host "`n=== 最近のエラー ==="
        $errorEntries | Select-Object -Last 5 | ForEach-Object {
            Write-Host "[$($_.Timestamp)] $($_.Message)"
        }
    }
}

GUI付きスリープ制御ツール

Windows Forms を使用したGUIツール

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

function Show-SleepControlGUI {
    # フォームの作成
    $form = New-Object System.Windows.Forms.Form
    $form.Text = "PowerShell スリープ制御ツール"
    $form.Size = New-Object System.Drawing.Size(400, 300)
    $form.StartPosition = "CenterScreen"
    $form.FormBorderStyle = "FixedDialog"
    $form.MaximizeBox = $false
    
    # タイマー設定
    $timerLabel = New-Object System.Windows.Forms.Label
    $timerLabel.Text = "スリープまでの時間 (分):"
    $timerLabel.Location = New-Object System.Drawing.Point(20, 20)
    $timerLabel.Size = New-Object System.Drawing.Size(150, 20)
    $form.Controls.Add($timerLabel)
    
    $timerTextBox = New-Object System.Windows.Forms.TextBox
    $timerTextBox.Text = "5"
    $timerTextBox.Location = New-Object System.Drawing.Point(180, 18)
    $timerTextBox.Size = New-Object System.Drawing.Size(60, 20)
    $form.Controls.Add($timerTextBox)
    
    # チェックボックス
    $forceCheckBox = New-Object System.Windows.Forms.CheckBox
    $forceCheckBox.Text = "強制スリープ(アプリ確認なし)"
    $forceCheckBox.Location = New-Object System.Drawing.Point(20, 50)
    $forceCheckBox.Size = New-Object System.Drawing.Size(200, 20)
    $form.Controls.Add($forceCheckBox)
    
    $backupCheckBox = New-Object System.Windows.Forms.CheckBox
    $backupCheckBox.Text = "スリープ前にバックアップ実行"
    $backupCheckBox.Location = New-Object System.Drawing.Point(20, 75)
    $backupCheckBox.Size = New-Object System.Drawing.Size(200, 20)
    $form.Controls.Add($backupCheckBox)
    
    # ステータスラベル
    $statusLabel = New-Object System.Windows.Forms.Label
    $statusLabel.Text = "準備完了"
    $statusLabel.Location = New-Object System.Drawing.Point(20, 110)
    $statusLabel.Size = New-Object System.Drawing.Size(350, 40)
    $statusLabel.BorderStyle = "Fixed3D"
    $form.Controls.Add($statusLabel)
    
    # プログレスバー
    $progressBar = New-Object System.Windows.Forms.ProgressBar
    $progressBar.Location = New-Object System.Drawing.Point(20, 160)
    $progressBar.Size = New-Object System.Drawing.Size(350, 20)
    $progressBar.Visible = $false
    $form.Controls.Add($progressBar)
    
    # ボタン
    $startButton = New-Object System.Windows.Forms.Button
    $startButton.Text = "スリープ開始"
    $startButton.Location = New-Object System.Drawing.Point(20, 200)
    $startButton.Size = New-Object System.Drawing.Size(100, 30)
    $form.Controls.Add($startButton)
    
    $cancelButton = New-Object System.Windows.Forms.Button
    $cancelButton.Text = "キャンセル"
    $cancelButton.Location = New-Object System.Drawing.Point(140, 200)
    $cancelButton.Size = New-Object System.Drawing.Size(100, 30)
    $cancelButton.Enabled = $false
    $form.Controls.Add($cancelButton)
    
    $immediateButton = New-Object System.Windows.Forms.Button
    $immediateButton.Text = "即座にスリープ"
    $immediateButton.Location = New-Object System.Drawing.Point(260, 200)
    $immediateButton.Size = New-Object System.Drawing.Size(110, 30)
    $form.Controls.Add($immediateButton)
    
    # タイマー
    $timer = New-Object System.Windows.Forms.Timer
    $script:remainingSeconds = 0
    $script:isCanceled = $false
    
    # イベントハンドラー
    $startButton.Add_Click({
        try {
            $minutes = [int]$timerTextBox.Text
            $script:remainingSeconds = $minutes * 60
            $script:isCanceled = $false
            
            $progressBar.Maximum = $script:remainingSeconds
            $progressBar.Value = 0
            $progressBar.Visible = $true
            
            $startButton.Enabled = $false
            $cancelButton.Enabled = $true
            $immediateButton.Enabled = $false
            
            $statusLabel.Text = "カウントダウン開始: $minutes 分後にスリープします"
            $timer.Start()
            
        } catch {
            [System.Windows.Forms.MessageBox]::Show("無効な時間が入力されました", "エラー", "OK", "Error")
        }
    })
    
    $cancelButton.Add_Click({
        $script:isCanceled = $true
        $timer.Stop()
        $progressBar.Visible = $false
        
        $startButton.Enabled = $true
        $cancelButton.Enabled = $false
        $immediateButton.Enabled = $true
        
        $statusLabel.Text = "キャンセルされました"
    })
    
    $immediateButton.Add_Click({
        $result = [System.Windows.Forms.MessageBox]::Show("すぐにスリープしますか?", "確認", "YesNo", "Question")
        if ($result -eq "Yes") {
            $form.Hide()
            rundll32.exe powrprof.dll,SetSuspendState 0,1,0
        }
    })
    
    $timer.Add_Tick({
        if ($script:isCanceled) {
            return
        }
        
        $script:remainingSeconds--
        $progressBar.Value = $progressBar.Maximum - $script:remainingSeconds
        
        $minutes = [math]::Floor($script:remainingSeconds / 60)
        $seconds = $script:remainingSeconds % 60
        $statusLabel.Text = "残り時間: ${minutes}分${seconds}秒"
        
        if ($script:remainingSeconds -le 0) {
            $timer.Stop()
            $statusLabel.Text = "スリープを実行中..."
            $form.Hide()
            
            # バックアップオプションのチェック
            if ($backupCheckBox.Checked) {
                # バックアップ処理(簡略版)
                $statusLabel.Text = "バックアップ実行中..."
                Start-Sleep -Seconds 2
            }
            
            rundll32.exe powrprof.dll,SetSuspendState 0,1,0
        }
    })
    
    $timer.Interval = 1000  # 1秒間隔
    
    # フォーム表示
    $form.ShowDialog()
}

# GUIツールの起動
# Show-SleepControlGUI

自動化とスケジューリング

スリープポリシーの実装

class SleepPolicy {
    [string]$Name
    [hashtable]$Conditions
    [scriptblock]$Action
    [bool]$Enabled
    
    SleepPolicy([string]$name, [hashtable]$conditions, [scriptblock]$action) {
        $this.Name = $name
        $this.Conditions = $conditions
        $this.Action = $action
        $this.Enabled = $true
    }
    
    [bool]CheckConditions() {
        foreach ($key in $this.Conditions.Keys) {
            $value = $this.Conditions[$key]
            
            switch ($key) {
                "Time" {
                    $currentTime = Get-Date -Format "HH:mm"
                    if ($currentTime -ne $value) { return $false }
                }
                "DayOfWeek" {
                    if ((Get-Date).DayOfWeek -notin $value) { return $false }
                }
                "CpuThreshold" {
                    $cpu = Get-Counter "\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 1
                    if ($cpu.CounterSamples[0].CookedValue -gt $value) { return $false }
                }
                "NoActiveUsers" {
                    $activeSessions = quser 2>$null
                    if ($activeSessions -and $value) { return $false }
                }
            }
        }
        return $true
    }
    
    [void]Execute() {
        if ($this.Enabled -and $this.CheckConditions()) {
            Write-Host "ポリシー '$($this.Name)' の条件が満たされました。実行します..."
            & $this.Action
        }
    }
}

# ポリシーの定義例
$policies = @(
    [SleepPolicy]::new("NightSleep", @{
        Time = "23:00"
        DayOfWeek = @("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")
        CpuThreshold = 15
    }, {
        Write-Host "夜間自動スリープを実行"
        rundll32.exe powrprof.dll,SetSuspendState 0,1,0
    }),
    
    [SleepPolicy]::new("LunchSleep", @{
        Time = "12:00"
        NoActiveUsers = $true
        CpuThreshold = 10
    }, {
        Write-Host "昼休み自動スリープを実行"
        rundll32.exe powrprof.dll,SetSuspendState 0,1,0
    })
)

function Start-SleepPolicyMonitor {
    param([SleepPolicy[]]$Policies)
    
    Write-Host "スリープポリシー監視を開始します..."
    
    while ($true) {
        foreach ($policy in $Policies) {
            $policy.Execute()
        }
        
        Start-Sleep -Seconds 60  # 1分間隔でチェック
    }
}

# 使用例
# Start-SleepPolicyMonitor -Policies $policies

まとめ|PowerShellでスリープを自在に扱うテクニック

PowerShellでは、**「スクリプトの一時停止」「PCの電源スリープ」**の両方を柔軟に制御できます。この記事で学んだ内容をまとめると:

基本的なスリープ制御

  1. Start-Sleep:スクリプトの一時停止
    • 秒単位:Start-Sleep -Seconds 10
    • ミリ秒単位:Start-Sleep -Milliseconds 500
  2. rundll32:PCのスリープ制御
    • 基本形:rundll32.exe powrprof.dll,SetSuspendState 0,1,0
    • 管理者権限が必要

高度な活用方法

  1. 条件付きスリープ:CPU使用率や時間帯による制御
  2. スマートスリープ:システム状態を総合判断
  3. タスクスケジューラー連携:定時自動実行
  4. GUI制御:使いやすいインターフェース
  5. ログ機能:実行履歴の管理

トラブルシューティングのポイント

  1. 管理者権限の確認
  2. 休止状態の無効化
  3. ウェイクタイマーの管理
  4. 実行中アプリの確認

コメント

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