You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 02:52:21