Win11下C#程序登录启动异常,求用户已登录验证方案
解决方案
针对Win11下任务计划程序提前启动程序的问题,以下几种代码验证方式可以准确判断用户是否完成登录(桌面环境就绪):
方法1:检查Shell窗口是否就绪
用户登录完成后,Windows会启动资源管理器并创建交互式桌面窗口,通过GetShellWindow() API可以判断该窗口是否存在,返回非零句柄则说明用户已登录。
C#代码实现:
using System; using System.Runtime.InteropServices; public static class LoginChecker { [DllImport("user32.dll")] private static extern IntPtr GetShellWindow(); public static bool IsUserLoggedIn() { // 循环等待避免单次检查误判,最多等待30秒 for (int i = 0; i < 30; i++) { if (GetShellWindow() != IntPtr.Zero) return true; System.Threading.Thread.Sleep(1000); } return false; } }
方法2:验证活跃交互式会话
通过Windows Terminal Services API,检查当前进程的会话是否为活跃的本地交互式会话,排除系统会话或已初始化但未登录的用户会话。
C#代码实现:
using System; using System.Runtime.InteropServices; public static class LoginChecker { [DllImport("kernel32.dll")] private static extern uint GetCurrentProcessId(); [DllImport("kernel32.dll")] private static extern bool ProcessIdToSessionId(uint dwProcessId, out uint pSessionId); [DllImport("wtsapi32.dll")] private static extern bool WTSGetActiveConsoleSessionId(out uint pSessionId); [DllImport("wtsapi32.dll")] private static extern bool WTSQuerySessionInformation(IntPtr hServer, uint sessionId, WTS_INFO_CLASS wtsInfoClass, out IntPtr ppBuffer, out uint pBytesReturned); [DllImport("wtsapi32.dll")] private static extern void WTSFreeMemory(IntPtr pMemory); private enum WTS_INFO_CLASS { WTSIsRemoteSession = 14 } public static bool IsInteractiveSessionActive() { // 获取当前进程会话ID if (!ProcessIdToSessionId(GetCurrentProcessId(), out uint currentSessionId)) return false; // 获取活跃控制台会话ID if (!WTSGetActiveConsoleSessionId(out uint activeSessionId)) return false; // 会话ID不匹配直接返回false if (currentSessionId != activeSessionId) return false; // 验证会话是否为本地交互式(非远程会话) if (WTSQuerySessionInformation(IntPtr.Zero, currentSessionId, WTS_INFO_CLASS.WTSIsRemoteSession, out IntPtr buffer, out uint bytesReturned)) { bool isRemote = Marshal.ReadInt32(buffer) != 0; WTSFreeMemory(buffer); return !isRemote; } return false; } }
方法3:等待Explorer进程启动
用户登录后,系统会启动explorer.exe作为桌面壳程序,检查该进程是否存在且属于当前用户会话:
C#代码实现:
using System; using System.Diagnostics; using System.Runtime.InteropServices; public static class LoginChecker { [DllImport("kernel32.dll")] private static extern bool ProcessIdToSessionId(uint dwProcessId, out uint pSessionId); public static bool IsExplorerRunning() { ProcessIdToSessionId((uint)Process.GetCurrentProcess().Id, out uint currentSessionId); for (int i = 0; i < 30; i++) { foreach (Process proc in Process.GetProcessesByName("explorer")) { if (ProcessIdToSessionId((uint)proc.Id, out uint procSessionId) && procSessionId == currentSessionId) return true; } System.Threading.Thread.Sleep(1000); } return false; } }
为什么之前的方法无效?
- Session ID非零:Windows在用户登录前会提前创建用户会话(会话ID非零),但此时桌面环境未加载,因此无法通过Session ID判断登录状态。
- WMI读取UserName:用户会话初始化时就会关联用户名,但这并不代表用户已完成登录并进入桌面。
使用建议
优先选择方法1,因为GetShellWindow()直接对应用户桌面窗口的创建状态,最贴合实际登录完成的判定。在程序启动时调用IsUserLoggedIn(),返回true后再执行核心逻辑即可。
内容的提问来源于stack exchange,提问作者ishahak
相关产品推荐
相关产品推荐

