はじめに
Ollamaの serve や自作の常駐スクリプトなど、「ログオン時に自動で立ち上げておきたいが、黒いコンソールウィンドウは出したくない」ものは多い。Windowsのタスクスケジューラはログオン時に実行できるが、コンソールアプリを直接登録するとウィンドウが表示される。そこで、VBScriptの WshShell.Run をワンクッション挟んで非表示で起動する。
本記事は、この構成を筆者のWindows実機で検証した結果をまとめたもの。検証のお題はOllamaの ollama serve。
環境
- Windows 11(ビルド 10.0.26200)
- PowerShell 5.1
- Ollama 0.40.4(公式インストーラ版。
%LOCALAPPDATA%\Programs\Ollama\ollama.exe)
やったこと
1. 非表示で起動するVBSを作る
launch_hidden.vbs:
Set sh = CreateObject("WScript.Shell")
ollama = sh.ExpandEnvironmentStrings("%LOCALAPPDATA%\Programs\Ollama\ollama.exe")
sh.Run """" & ollama & """ serve", 0, False
ポイントは Run(コマンド, 0, False) の第2引数 0(ウィンドウスタイル: 非表示)と第3引数 False(プロセスの終了を待たない)。
Run はコマンド文字列中の環境変数を展開しないため、%LOCALAPPDATA% などのパスは ExpandEnvironmentStrings で先に展開しておく。PowerShellスクリプトを経由したい場合も同じ要領になる。
sh.Run "powershell.exe -NoProfile -ExecutionPolicy Bypass -File """ & scriptPath & """", 0, False
2. 本当に「非表示」か確認する
目視だけでは心もとないので、起動されたプロセス自身に申告させる。
$me = Get-Process -Id $PID
"handle=$($me.MainWindowHandle) title='$($me.MainWindowTitle)'"
VBS経由で起動したところ、実際の出力はこうだった。
handle=0 title=''
MainWindowHandle が 0、つまりウィンドウを持たない状態で動作していることが確認できた。
3. タスクスケジューラに登録する
まずは影響の少ない「1回だけ」のタスクで動作確認する。この時点では管理者権限は不要。
$vbs = "$env:USERPROFILE\launch_hidden.vbs"
$tr = "wscript.exe `"$vbs`""
schtasks /create /tn "HiddenLauncher" /tr "$tr" /sc once /st 23:59 /f
schtasks /run /tn "HiddenLauncher"
schtasks /delete /tn "HiddenLauncher" /f
実測結果:
- 作成:
SUCCESS: The scheduled task "HiddenLauncher" has successfully been created. - 実行:
SUCCESS。起動されたプロセスはMainWindowHandle=0のまま(非表示) - 削除:
SUCCESS
本番は「ログオン時」に変える。
schtasks /create /tn "HiddenLauncher" /tr "$tr" /sc onlogon /f
ただしこれは、管理者権限でないと作成できない(実測)。
ERROR: Access is denied.
本番登録だけは管理者として実行したPowerShellから行い、動作確認は非管理者で /sc once を使う、と切り分けるとやりやすい。
4. 二重起動を防ぐ
常駐プロセスは「すでに起動していないか」を確認してから立ち上げたい。Ollamaのサーバーならポートを確認する。
$listening = Get-NetTCPConnection -LocalPort 11434 -State Listen -ErrorAction SilentlyContinue
if ($listening) { exit }
検証中、Windowsでは ollama serve の二重起動がエラーにならないことに気づいた。既存のOllamaサーバーがLISTEN中の状態で、VBSからもう1つ serve を起動すると、後から起動した方が別アドレスでLISTENし、両方のプロセスがLISTEN状態のまま共存してしまった。エラーにはならないため、ポートを確認しないと「いつの間にか2つ動いている」状態になり得る。
ハマった点
-
/sc onlogonは管理者権限が必須。非管理者で実行するとERROR: Access is denied.になる。/sc onceは非管理者でも作成できるので、テストと本番登録で権限を分けるとよい。 -
VBSの
Runは環境変数を展開しない。%LOCALAPPDATA%などをそのまま書いても展開されないため、ExpandEnvironmentStringsで事前に展開する。 -
Windowsは同一ポートの二重LISTENを許す場合がある。バインドエラーで気づけないので、起動前に
Get-NetTCPConnectionでLISTENの有無を確認する。 - 非表示かどうかの判定は
MainWindowHandleを見るのが手軽。タスクスケジューラ経由で起動したプロセスでも0のままだった。
まとめ
- VBSの
Run(コマンド, 0, False)をワンクッション挟むことで、タスクスケジューラからコンソールを出さずに常駐プロセスを起動できる。 - 動作テストは非管理者で
/sc once、本番は管理者権限で/sc onlogon。 - 起動前のポート確認(二重起動対策)を忘れない。
なお、Ollama公式アプリ自体がログオン時に常駐してサーバーを起動する構成のため、Ollamaについては公式アプリに任せるのが基本で、この仕組みは「自作の常駐ツールをコンソールなしで立ち上げたいとき」に効く。
関連する本
この常駐起動の仕組みを、ローカルLLMの自動実行まで含めて一冊にまとめました。第1章・第2章は無料で読めます。