はじめに
以前に次記事を書きました。
2024年12月からサーバー保全として全国の工場に Windows Server 2022 から順次入れ替え作業を行ないました。今回は工場によっては規模が小さいため、Windows 10 IoT をサーバー代わりに使用したいとなりました。
Windows 10 IoT ではOS制限により、同時に接続できるリモートデスクトップセッションは1台(1ユーザー)のみに固定となっています。
誰かが使用していると奪っていいのか悩んでしまいます。
リモートデスクトップの接続ユーザーを知りたい
前回は、SQLServer の CLR で実装したりしましたが、今回の DB は、PostgreSQL です。
また、Windows 10 IoT 版を作成して社内で公開したところ、NASサーバー版(Windows Server 2022)も欲しいという要望がありました。こちらには DB は何もインストールされていません。
ストアド を使わない方法をネット検索したところ、タスクスケジューラーの下記のトリガー種類が使用できることに気がつきました。
- ユーザーセッションへの接続時:リモートデスクトップなどでセッションに接続したときに実行します。
- ユーザーセッションからの切断時:リモートデスクトップなどのセッションから切断したときに実行します。
ユーザーセッションへの接続時
タスクスケジューラーのトリガー種類「ユーザーセッションへの接続時」で下記のPowershellスクリプトを実行させます。
C:¥RDPScripts フォルダを作成して、GetRDPClient.ps1 を配置します。そのフォルダにrdp_client.txt ファイルとしてクライアント名と日時を出力します。
$clientName = ""
$basePath = "HKCU:\Volatile Environment"
if (Test-Path $basePath) {
Get-ChildItem $basePath -ErrorAction SilentlyContinue | ForEach-Object {
$value = (Get-ItemProperty $_.PSPath -Name CLIENTNAME -ErrorAction SilentlyContinue).CLIENTNAME
if ($value -ne $null -and $value.Trim() -ne "" -and $value -ne "Console") {
$clientName = $value
}
}
}
if ($clientName -ne "") {
$filePath = Join-Path $PSScriptRoot "rdp_client.txt"
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
"$clientName,$timestamp" | Set-Content -Path $filePath -Encoding UTF8
Write-Output $clientName
}
else {
Write-Output ""
}
サーバー扱いなので別に気にしなくてもいいのですが、タスクスケジューラー実行時に PowerShellスクリプト の青い画面を表示させないようにしています。
ユーザーセッションからの切断時
タスクスケジューラーのトリガー種類「ユーザーセッションへの切断時」で下記のバッチを実行して、rdp_client.txt ファイルを削除させます。
@echo off
set FILE=%~dp0rdp_client.txt
if exist "%FILE%" (
del /f /q "%FILE%"
)
Web画面で一括表示
PostgreSQLのユーザー関数作成
Windows 10 IoT をサーバー代わりに使用している複数の工場で状態を一覧で見たいため、Web画面を ASP.NET で作成しています。
その時に、PostgreSQLのユーザー関数で rdp_client.txt ファイルを読み込み表示しています。
CREATE OR REPLACE FUNCTION GetRDPClient()
RETURNS TABLE (client_name text, update_time timestamp)
LANGUAGE plpgsql
AS $$
DECLARE
v_line text;
BEGIN
v_line := trim(replace(pg_read_file('C:/RDPScripts/rdp_client.txt'), chr(65279), ''));
IF v_line IS NULL OR v_line = '' THEN
RETURN;
END IF;
RETURN QUERY
SELECT NULLIF(split_part(v_line, ',', 1), '')::text,
NULLIF(split_part(v_line, ',', 2), '')::timestamp;
EXCEPTION
WHEN OTHERS THEN
RETURN;
END;
$$;
使い方
SELECT * FROM GetRDPClient()
ユーザー名と日時が返ります。
注意
PostgreSQL からファイルを読み込めるようにするため、rdp_client.txt ファイルまたはフォルダに Network Service の読み取り権限が必要です。
ファイルエクスプローラーのプロパティで設定もいいですが、次のバッチで実行できます。
icacls C:\RDPScripts\rdp_client.txt /grant "NT AUTHORITY\NETWORK SERVICE:R"
共有フォルダにアクセス
NASサーバーの場合、DBがインストールされていないため、ユーザー関数は使えません。
Web画面を ASP.NET にて共有フォルダにアクセスして rdp_client.txt ファイルを読むようにしています。
Microsoft Copilot で作成してもらいました。
try
{
string sharePath = @"\\" + ip + @"\RDPScripts";
using (new NetworkConnection(sharePath,ip + @$"\{username}", pw))
{
string filePath = Path.Combine(sharePath, "rdp_client.txt");
if (File.Exists(filePath))
{
string line = File.ReadAllText(filePath).Trim();
string[] cols = line.Split(',');
if (cols.Length >= 2)
{
clientName = cols[0];
logonTime = cols[1];
}
}
}
}
catch (Exception ex)
{
clientName = "取得失敗";
logonTime = ex.Message;
}
最後に
最初は、SQLServer の CLR実装と似た方法で模索していたのですが、PostgreSQL からレジストリ値を取得するのは権限設定とか面倒くさい上に、PostgreSQLサービスは通常、NT AUTHORITY\NetworkService や LocalSystem として動作しているので、RDPユーザーのレジストリ(HKCU)にアクセスできなくて値が無しで返ってきてしまうのです。
タスクスケジューラーの「ユーザーセッションへの接続時」と「ユーザーセッションからの切断時」で解決できて良かったです。