質問: C# 7.0 (.NET 4.5.2 )でサービスプログラムを開発しています。 実行環境は Windows8.1, Windows10, Windows Server 2016, Windows Server 2019 です。 ログイン済の状態でサービスを起動した時にログイン済かどうかを判定したいのですが、 (OnSessionChange で行う処理を OnStart 時にも実装しようとしています) リモートデスクトップ接続のセッションに対して正しく判定できる方法が分かりません。 win32 API を介した 以下方法を試してみましたが どれも サービスプログラムからは 正しく判定できませんでした。 ・アクティブなセッション識別子(SID)の値 WTSGetActiveConsoleSessionId() ・SM_REMOTESESSION(0x1000) に対するシステム値 GetSystemMetrics(SM_REMOTESESSION) != 0 ・OS_TERMINALCLIENT(14) に対する値 IsOS(OS_TERMINALCLIENT) 妙案をアドバイスいただけますようお願いいたします。
回答: おそらくWindowsVista以降のセッション0分離の影響で、サービスプロセスは必ずUIの無いセッション0で動作するために発生している現象と思われます。 トラブルシューター泣かせのWindowsの仕様変更──忘れられた互換性機能? (1/2) 第 14 回 Windows Vista のセキュリティ機能 ~ Windows Service Hardening ~ (セッション0の分離)サービスからexeを実行する方法 それで単発のAPIではなく、すべてのセッションの情報を取得して、リモートデスクトップ接続のセッションがあるかどうかを判断する方法が取れるのではないでしょうか。 管理者コマンドプロンプトで`query session`すると情報が取れるので、それをプログラムでやろうというわけです。 Windowsでリモートデスクトップのセッションを確認・変更する方法 プログラム的にはこの記事を参考に(まあほぼ丸写しでも)処理を組み込めば、セッションの情報を取得出来るでしょう。(Windows10同士の接続では出来ました) リモート接続先で接続元の情報を C# で取得する 以下のような情報で判断できるでしょう。 `WTSWinStationName`が`RDP-Tcp#n`なもの(nは数字) `WTSConnectState`(接続状態)が`WTSActive` `WTSClientName`が接続元のコンピュータ名 `WTSUserName`がログインユーザー名(紹介先記事では取得していない) 上記記事から関連する構造体やEnumの抜粋 ``` private struct WTS_SESSION_INFO { public uint SessionID; [MarshalAs(UnmanagedType.LPStr)] public string WinStationName; public WTS_CONNECTSTATE_CLASS State; } public enum WTS_CONNECTSTATE_CLASS { WTSActive, WTSConnected, WTSConnectQuery, WTSShadow, WTSDisconnected, WTSIdle, WTSListen, WTSReset, WTSDown, WTSInit } public enum WTS_INFO_CLASS { ~途中省略~ WTSSessionId, WTSUserName, WTSWinStationName, WTSConnectState, WTSClientName, ~途中省略~ } ``` 他にC++で類似の情報を取得する記事 Windowsサービスから現在のログオンユーザを取得する