真夏の怪異、ログオン中のユーザーが1人少ない! それは「Shift-JIS」の呪い?:その知識、ホントに正しい? Windowsにまつわる都市伝説(189)
先日、Windows Server用のWebベースの管理ツール「Windows Admin Center」を操作していて、おかしな表示に気が付きました。「概要」ページにある「ログインしているユーザー」が「-1」だというのです。サーバに1人以上のユーザーがログオンしている場合、今度は「0」だというのです。一体どういうことでしょうか。なぜこんな凡ミスにこれまで誰も気が付かなかったのかと疑問に思っていたら、実は日本語環境特有の「Shift-JIS」の呪いだったのです。
「ログインしているユーザー」が「-1」は仕様? 「0」はバグ?
「Windows Admin Center」の「概要」ページにある「ログインしているユーザー」の数が実際とは異なる問題に気が付いたのは、2021年5月末にリリースされた最新バージョン「2103.2」に更新してすぐのことでした。
管理対象のサーバにログオン中のユーザーがいないときには「-1」と報告し(画面1)、1人のユーザーがログオン中のときには「0」を報告します。さらに、ローカルログオンとリモートデスクトップ接続で同時に複数のユーザーでログオンしてみても、「0」を報告するのです(画面2)。
まだ最新版に更新していないバージョン「2103」で確認しても同様です。UIの言語設定の問題かと思い、「設定」の「言語/地域」を「日本語/日本語(日本)」から「English/English(United States)」に変更してみましたが、状況は変わりません。
Azureポータルに統合された「Windows Admin Center(プレビュー)」でも確認してみました。管理対象のサーバにログオン中のユーザーがいないときに「-1」と報告するのは同じでしたが(画面3)、1人以上のユーザーのログオン数は正しく報告しているように見えます(画面4)。
ログオン中のユーザーがいないときに「-1」を報告するのは、どうやら仕様のようです。1人以上のログオンユーザーがいる場合に「0」を報告する問題の原因は、管理対象のサーバの言語環境、それも日本語環境固有の問題のようです。
オンプレミスのWindows Admin Centerの接続先は日本語版「Windows Server 2019評価版」でシステムロケールはそれぞれ「日本語(日本)」、AzureポータルのWindows Admin Center(プレビュー)の接続先は英語版「Windows Server 2019 Datacenter:Azure Edition」でシステムロケールは「English(United States)」という違いがあります。
誰もいないときの「-1」は言語に関係ない初歩的なバグ
Windows Admin Centerには現在、表示中のツール(ページ)で使用されているPowerShellスクリプトを表示する機能があります(※Azureポータルに統合されたWindows Admin Center《プレビュー》にこの機能はありません)。
「概要」ページの「ログオンしているユーザー」の情報は、「Get-NumberOfLoggedOnUsers」ファンクションとして実装されていました(画面5)。コードを追いかけてみると、システムにログオンしているユーザーの情報を表示する「quser.exe」コマンドを実行し、その標準出力の行数からヘッダ行の「1」を引いたものをログオン中のユーザー数としていました。
quser.exeはログオン中のユーザーがいない場合、標準エラーに「* に対するユーザーは存在しません。」(英語環境の場合「No User exists for *」)を出力します。
「Get-NumberOfLoggedOnUsers」ファンクションでは、標準エラーに出力があればカウントの初期値「0」($count = 0)を返すことを期待しているようですが、以下のコードでは標準エラーを判定する前にプロセスを閉じてしまっているため、標準エラーは判定前に空の状態になってしまうのではないでしょうか。標準出力もまた空っぽ(0行)のため、ユーザー数が「-1」(0-1)になるというわけです。
function Get-NumberOfLoggedOnUsers { $count = 0 $error.Clear(); 〜中略〜 $process.WaitForExit() $process.Dispose() if (!$process.StandardError) { # quser does not return a valid ps object and includes the header. # subtract 1 to get actual count. $count = $result.count - 1 } @{Count = $count} }
プロセスの終了と標準エラーの判定の順番を変更することで、コードが期待した結果を出すことは、誰もログオンしていないHyper-V仮想マシンに対するPowerShell Direct(「Invoke-Command -VMName 仮想マシン名」)でスクリプトを実行させて確認しました(画面6)。
これは明らかなバグなので、Windows Admin Center側で修正される必要があります。それにしても、正式リリースのバージョンなのに、テストしていないのでしょうか。なお、ログオン中のユーザーがいると考慮した場合、順番を入れ替えるだけではなく、もう少し修正が必要です(正しく動作するコードは本稿の最後に掲載)。
1人以上が「0」は日本語固有のバグ
コマンドプロンプトやPowerShellウィンドウでquser.exeを実行してみると、日本語固有の問題が影響していることが容易に想像できました。前出の画面6とは異なり、ヘッダ行が2行で表示されるからです。コマンドプロンプトで「CHCP 437」と入力し、英語のコードページに切り替えて実行してみると、日本語環境で2行に分かれている「アイドル時間」の部分は、英語環境では1行の「IDLE TIME」です(画面7)。
日本語環境ではヘッダ行が2行に分かれているわけですから、1人以上のログオンユーザーがいる場合、「実際のユーザー数+1」の数を報告しそうですが、実際には全て「0」と報告します。なぜでしょう。
システムロケール「日本語(日本)」と「英語(米国)」(日本語版のシステムロケールを単純に変更しただけ)のそれぞれの環境で、PowerShellスクリプトの該当するコードを直接実行してみました。
システムロケール「日本語(日本)」の方は、標準出力を1行ずつ読み取っている次のコードが、1行目の「アイドル」のところで読み込みをストップしているのが分かりました(画面8)。結果として、日本語環境では1人以上のログオンユーザーを常に「0」(1-1)として報告するのです。
while ($line = $process.StandardOutput.ReadLine()) { $result += $line }
コードを以下のように書き換えると、標準出力を最後まで読み取りますが、「-1」された結果は「4」を返します。
while (($line = $process.StandardOutput.ReadLine()) -ne $null) { $result += $line }
日本語環境でquser.exeの標準出力は3行に見えますが、テキストファイルにリダイレクトしてみると、各行の間に空白行があり、全体で5行(-1すると4)になっていました。この余計な空白またはNull行の存在は、他のコマンドラインツールではそうならないものもあるので(例えば、「query session」)、quser.exe(および同等の「query user」)と日本語環境の組み合わせの問題のようです。言語環境の違いを考慮すると、ヘッダで調整するのではなく、quserコマンドの「Active」を含む行をカウントした方がよいでしょう。
というわけで、Windows Admin Centerの日本語環境における「ログインしているユーザー」の数「0」という誤りは、管理対象のサーバのシステムロケールを「英語(米国)」に変更することで解消できます(画面9)。または、システムロケール「日本語(日本)」のままで、「ベータ:ワールドワイド言語サポートでUnicode UTF-8を使用」オプションを有効化することでも解消できます(画面10)。
現在のWindowsは、全てのテキスト文字列を内部的にUnicode文字(UTF-16LE)として格納し、処理します。ユーザーやアプリが作成したテキストデータは別です。以前の「メモ帳」(Notepad.exe)の既定では、ANSIコード(日本語はShift-JIS)でテキストを保存していましたが、現在のメモ帳(Windows 10 バージョン1903以降)はUTF-8コードが既定になりました。
一方、Windowsのシステムロケールが「日本語(日本)」の場合、コマンドプロンプトやWindows PowerShellのコードページは「932(ANSI/OEM - 日本語Shift-JIS)」が既定です。バッチファイルやWSH(Windows Script Host)スクリプト、PowerShellスクリプトに日本語文字列が含まれる場合、ANSIコードで保存しないと文字化けやスクリプトエラーの原因になります。既定のシステムロケールであり、長く利用されてきた「日本語(日本)」を変更してしまうことは、日本語を扱うサーバ側のアプリやツール、スクリプトの実行に、今以上に意図しない影響を及ぼすかもしれません。
また、「ベータ:ワールドワイド言語サポートでUnicode UTF-8を使用」オプションは、あくまでも「β版」として提供される機能であり、運用環境では使用すべきではありません。
昔から国際化対応をうたってきたWindowsですが、日本語環境ではちょくちょくこんな問題に遭遇します。Shift-JISとの腐れ縁を断ち切れない限り、こうした問題はこれからも出てくるでしょう。最もシンプルな対応策は「見なかったことにする」「見ないようにする」ことかもしれません。
最後に、言語環境に依存することなく、ちゃんと動作するはずの「Get-NumberOfLoggedOnUsers」ファンクションを筆者なりに書いてみました。ただし、Windows Admin Centerのバグを修正できるというものではありません(コード署名があるので改変できません)。
さまざまなPowerShellスクリプトに組み込んで、「(Get-NumberOfLoggedOnUsers).Count」と呼び出せば、ログオン中のユーザーの有無やユーザー数を知ることができます。これを利用すれば、例えば、「タスクスケジューラ」でログオン中のユーザーがいないときにだけ実行するタスクを簡単に実装できるでしょう。動作確認は日本語環境でのみ行っています。
- ログオン中のユーザー数(0を含む)を取得する PowerShell スクリプト(筆者のブログ:山市良のえぬなんとかわーるど)
function Get-NumberOfLoggedOnUsers{ $count = 0 $error.Clear(); $process = New-Object System.Diagnostics.Process $process.StartInfo.FileName = "quser.exe" $process.StartInfo.UseShellExecute = $false $process.StartInfo.CreateNoWindow = $true $process.StartInfo.RedirectStandardOutput = $true $process.StartInfo.RedirectStandardError = $true $process.Start() | Out-Null $result = @() while (($line = $process.StandardOutput.ReadLine()) -ne $null) { if($line.Contains("Active")){ $result += $line } } if ([string]::isNullOrEmpty($process.StandardError.ReadToEnd())) { $count = $result.count } $process.WaitForExit() $process.Dispose() @{Count = $count} }
筆者紹介
山市 良(やまいち りょう)
岩手県花巻市在住。Microsoft MVP:Cloud and Datacenter Management(2020-2021)。SIer、IT出版社、中堅企業のシステム管理者を経て、フリーのテクニカルライターに。Microsoft製品、テクノロジーを中心に、IT雑誌、Webサイトへの記事の寄稿、ドキュメント作成、事例取材などを手掛ける。個人ブログは『山市良のえぬなんとかわーるど』。近著は『Windows版Docker&Windowsコンテナーテクノロジ入門』(日経BP社)、『ITプロフェッショナル向けWindowsトラブル解決 コマンド&テクニック集』(日経BP社)。
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- 次期Windows 10最新動向:リリース秒読みの「19H1」はこう変わる
間もなくリリースされるWindows 10の新しい機能アップデート「19H1」。それに実装される新機能をまとめてみた。また、同時に変更となるライフサイクルなどについても解説する。 - 【新元号発表目前!】Windows 10/Officeの新元号対応どうするどうなる!?
新元号への切り替えが2019年5月1日に行われる。Windows OS/Officeでこの新元号に対応するにはどうすればよいのか、注意すべき点はあるのかなどをまとめる。 - 【Windows 10】できる人は知っているキーボードショートカット
Windows 10でキーボードショートカットを使うと、マウスを使うよりも素早い操作が可能だ。ただ、種類も多く、知っていると便利なのに意外と使われていないものも多いようだ。ここでは基本的なキーボードショートカットを紹介する。 - Windows 10への移行計画を早急に進めるべき理由
本連載では、これからWindows 10への移行を本格的に進めようとしている企業/IT管理者に向け、移行計画、展開、管理、企業向けの注目の機能を解説していきます。第1回目は、「Windows 10に移行すべき理由」を説明します。