Help us understand the problem. What is going on with this article?

常駐型PowerShellプログラムをタスクトレイに入れる

More than 1 year has passed since last update.

概要

下記のような構造を持つ常駐型のPowerShellプログラム(例えばこちらのスクリプトなど)が、(最小化しても)常にタスクバー上に表示されていると目障りなのでタスクトレイ(正式には"通知領域")に入れます。

while ($True) {
    ... #何かの処理
    ...
    Start-Sleep <n>
}

実装

以下のソースを対象のスクリプト(拡張子 .ps1)の先頭部分に追加(埋め込み)し、実行(右クリックして「PowerShellで実行」)することでタスクトレイの中に入れることができます。最後のコメント行は削除しないでください(2桁目の"!"が重要)。
 なお、対象スクリプトには引数がなく、したがってparam文を持たないものとします(常駐型なら少しの工夫によりそのような形式で書ける筈)。

 ※こちらの記事を参考にしました。

#@Powershell -NoP -W Hidden -C "$PSCP='%~f0';$PSSR='%~dp0'.TrimEnd('\');&([ScriptBlock]::Create((gc '%~f0'|?{$_.ReadCount -gt 1}|Out-String)))" %* & exit/b
# by earthdiver1  V1.05
if ($PSCommandPath) {
    $PSCP = $PSCommandPath
    $PSSR = $PSScriptRoot
    $code = '[DllImport("user32.dll")]public static extern bool ShowWindowAsync(IntPtr hWnd,int nCmdShow);'
    $type = Add-Type -MemberDefinition $code -Name Win32ShowWindowAsync -PassThru
    [void]$type::ShowWindowAsync((Get-Process -PID $PID).MainWindowHandle,0) }
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
$menuItem = New-Object System.Windows.Forms.MenuItem "Exit"
$menuItem.add_Click({$notifyIcon.Visible=$False;while(-not $status.IsCompleted){Start-Sleep 1};$appContext.ExitThread()})
$contextMenu = New-Object System.Windows.Forms.ContextMenu
$contextMenu.MenuItems.AddRange($menuItem)
$notifyIcon = New-Object System.Windows.Forms.NotifyIcon
$notifyIcon.ContextMenu = $contextMenu
$notifyIcon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($PSCP)
$notifyIcon.Text = (Get-ChildItem $PSCP).BaseName
$notifyIcon.Visible = $True
$_syncHash = [hashtable]::Synchronized(@{})
$_syncHash.NI   = $notifyIcon
$_syncHash.PSCP = $PSCP
$_syncHash.PSSR = $PSSR
$runspace = [RunspaceFactory]::CreateRunspace()
$runspace.ApartmentState = "STA"
$runspace.ThreadOptions  = "ReuseThread"
$runspace.Open()
$runspace.SessionStateProxy.SetVariable("_syncHash",$_syncHash)
$scriptBlock = Get-Content $PSCP | ?{ $on -or $_[1] -eq "!" }| %{ $on=1; $_ } | Out-String
$action=[ScriptBlock]::Create(@'
#   param($Param1, $Param2)
    Start-Transcript -LiteralPath ($_syncHash.PSCP -Replace '\..*?$',".log") -Append
    Function Start-Sleep { [CmdletBinding(DefaultParameterSetName="S")]
        param([parameter(Position=0,ParameterSetName="M")][Int]$Milliseconds,
              [parameter(Position=0,ParameterSetName="S")][Int]$Seconds,[Switch]$NoExit)
        if ($PsCmdlet.ParameterSetName -eq "S") {
            $int = 5
            for ($i = 0; $i -lt $Seconds; $i += $int) {
                if (-not($NoExit -or $_syncHash.NI.Visible)) { exit }
                Microsoft.PowerShell.Utility\Start-Sleep -Seconds $int }
        } else {
            $int = 100
            for ($i = 0; $i -lt $Milliseconds; $i += $int) {
                if (-not($NoExit -or $_syncHash.NI.Visible)) { exit }
                Microsoft.PowerShell.Utility\Start-Sleep -Milliseconds $int }}}
    $script:PSCommandPath = $_syncHash.PSCP
    $script:PSScriptRoot  = $_syncHash.PSSR
'@ + $scriptBlock)
$PS = [PowerShell]::Create().AddScript($action) #.AddArgument($Param1).AddArgument($Param2)
$PS.Runspace = $runspace
$status = $PS.BeginInvoke()
$appContext = New-Object System.Windows.Forms.ApplicationContext
[void][System.Windows.Forms.Application]::Run($appContext)
exit
#! ---------- ScriptBlock (Line No. 28) begins here ---------- DO NOT REMOVE THIS LINE
  • バッチファイルとして実行(スタートアップに登録しやすい。ダブルクリックも可能)したい場合は、先頭行のコメントをはずして以下のコマンドを有効にします。
@Powershell -NoP -W Hidden -C "$PSCP='%~f0';$PSSR='%~dp0'.TrimEnd('\');&([ScriptBlock]::Create((gc '%~f0'|?{$_.ReadCount -gt 1}|Out-String)))" %* & exit/b
  • 起動した後、プログラムを終了する必要が生じた場合には、タスクトレイ上のアイコンを右クリックして Exit を選択します。待機状態時に終了させるために Start-Sleep コマンドレットをカスタマイズしています。既定では待機時間が秒単位の場合は5秒毎、ミリ秒単位の場合は100ミリ秒毎に終了判定を行いますが、必要に応じて36,41行目の $int 変数の値を調整してください。 途中終了させたくないStart-Sleepコマンドレットがある場合は、当該 Start-Sleep に -NoExit パラメータを追加してください。
  • 一瞬、黒いコンソール画面が表示されてしまうのがどうしても気になる方は、ショートカットを作成してプロパティの「実行時の大きさ」を「最小化」としてください。

(2018.5.1修正) \$PSScriptRoot を script スコープで参照可能としました。

クリエイティブ・コモンズ 表示 - 継承 4.0 国際

earthdiver1
元科学技術系エンジニア。最近はWindows上で環境を整える必要のないPowerShellで遊んでます。基本的には自分用の備忘録ですが、誰かの参考になれば嬉しいです。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away