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

如何用本地管理员权限的C#程序处理Win2012多会话应用错误弹窗

Solution to Close Cross-Session AutoApp Error Dialogs via Admin C# Monitor

Got it, let's break down how to solve this problem. The core issue here is that those error dialogs are running under non-admin user sessions, and your admin-level console app can't directly interact with windows from other sessions by default. Here's a step-by-step implementation:

Key Concepts to Know

  • Session Isolation: Windows isolates user sessions, so admin processes can't see or interact with windows in non-admin sessions unless you explicitly switch to that session's desktop.
  • Windows API Calls: We'll use native Win32 APIs to enumerate sessions, switch to the target desktop, find the error dialog, and simulate the OK click.

Full C# Implementation

First, add the necessary P/Invoke declarations and using directives:

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;

public class AutoAppErrorMonitor
{
    // Win32 API Declarations
    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    [DllImport("wtsapi32.dll")]
    private static extern bool WTSEnumerateSessions(IntPtr hServer, int Reserved, int Version, ref IntPtr ppSessionInfo, ref int pCount);

    [DllImport("wtsapi32.dll")]
    private static extern void WTSFreeMemory(IntPtr pMemory);

    [DllImport("user32.dll")]
    private static extern IntPtr OpenDesktop(string lpszDesktop, uint dwFlags, bool fInherit, uint dwDesiredAccess);

    [DllImport("user32.dll")]
    private static extern bool SetThreadDesktop(IntPtr hDesktop);

    [DllImport("user32.dll")]
    private static extern bool CloseDesktop(IntPtr hDesktop);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, uint BufferLength, IntPtr PreviousState, IntPtr ReturnLength);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr GetCurrentProcess();

    // Constants
    private const uint BM_CLICK = 0x00F5;
    private const uint DESKTOP_SWITCHDESKTOP = 0x0100;
    private const uint TOKEN_ADJUST_PRIVILEGES = 0x0020;
    private const uint TOKEN_QUERY = 0x0008;
    private const uint SE_PRIVILEGE_ENABLED = 0x00000002;
    private const string SE_ASSIGNPRIMARYTOKEN_NAME = "SeAssignPrimaryTokenPrivilege";
    private const string SE_INCREASE_QUOTA_NAME = "SeIncreaseQuotaPrivilege";

    [StructLayout(LayoutKind.Sequential)]
    private struct TOKEN_PRIVILEGES
    {
        public uint PrivilegeCount;
        public LUID_AND_ATTRIBUTES Privileges;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct LUID_AND_ATTRIBUTES
    {
        public LUID Luid;
        public uint Attributes;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct LUID
    {
        public uint LowPart;
        public int HighPart;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct WTS_SESSION_INFO
    {
        public int SessionId;
        [MarshalAs(UnmanagedType.LPStr)] public string pWinStationName;
        public WTS_CONNECTSTATE_CLASS State;
    }

    private enum WTS_CONNECTSTATE_CLASS
    {
        WTSActive,
        WTSConnected,
        WTSConnectQuery,
        WTSShadow,
        WTSDisconnected,
        WTSIdle,
        WTSListen,
        WTSReset,
        WTSDown,
        WTSInit
    }

    public static void Main(string[] args)
    {
        // Enable required privileges first
        EnablePrivileges();

        // Monitor loop (run every 10 seconds, adjust as needed)
        while (true)
        {
            Console.WriteLine("Scanning for AutoApp error dialogs...");
            CheckAndCloseErrorDialogs();
            System.Threading.Thread.Sleep(10000);
        }
    }

    private static void EnablePrivileges()
    {
        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out IntPtr tokenHandle))
        {
            Console.WriteLine($"OpenProcessToken failed: {Marshal.GetLastWin32Error()}");
            return;
        }

        foreach (string privilege in new[] { SE_ASSIGNPRIMARYTOKEN_NAME, SE_INCREASE_QUOTA_NAME })
        {
            LUID luid;
            if (!LookupPrivilegeValue(null, privilege, out luid))
            {
                Console.WriteLine($"LookupPrivilegeValue failed for {privilege}: {Marshal.GetLastWin32Error()}");
                continue;
            }

            TOKEN_PRIVILEGES tp = new TOKEN_PRIVILEGES
            {
                PrivilegeCount = 1,
                Privileges = new LUID_AND_ATTRIBUTES { Luid = luid, Attributes = SE_PRIVILEGE_ENABLED }
            };

            if (!AdjustTokenPrivileges(tokenHandle, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero))
            {
                Console.WriteLine($"AdjustTokenPrivileges failed for {privilege}: {Marshal.GetLastWin32Error()}");
            }
        }

        Marshal.Close(tokenHandle);
    }

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid);

    private static void CheckAndCloseErrorDialogs()
    {
        IntPtr serverHandle = IntPtr.Zero; // Local machine
        IntPtr sessionInfoPtr = IntPtr.Zero;
        int sessionCount = 0;

        try
        {
            if (!WTSEnumerateSessions(serverHandle, 0, 1, ref sessionInfoPtr, ref sessionCount))
            {
                Console.WriteLine($"WTSEnumerateSessions failed: {Marshal.GetLastWin32Error()}");
                return;
            }

            // Iterate through all active sessions
            for (int i = 0; i < sessionCount; i++)
            {
                IntPtr currentSessionPtr = IntPtr.Add(sessionInfoPtr, i * Marshal.SizeOf(typeof(WTS_SESSION_INFO)));
                WTS_SESSION_INFO sessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure(currentSessionPtr, typeof(WTS_SESSION_INFO));

                if (sessionInfo.State != WTS_CONNECTSTATE_CLASS.WTSActive && sessionInfo.State != WTS_CONNECTSTATE_CLASS.WTSConnected)
                    continue;

                // Switch to the session's desktop
                string desktopName = $"WinSta0\\Default"; // Default desktop for the session
                IntPtr desktopHandle = OpenDesktop(desktopName, 0, false, DESKTOP_SWITCHDESKTOP);
                if (desktopHandle == IntPtr.Zero)
                {
                    Console.WriteLine($"OpenDesktop failed for session {sessionInfo.SessionId}: {Marshal.GetLastWin32Error()}");
                    continue;
                }

                if (SetThreadDesktop(desktopHandle))
                {
                    // Find the error dialog
                    IntPtr errorDialog = FindWindow(null, "AutoApp.exe - Application Error");
                    if (errorDialog != IntPtr.Zero)
                    {
                        // Find the OK button (class name is "Button", text is "OK")
                        IntPtr okButton = FindWindowEx(errorDialog, IntPtr.Zero, "Button", "OK");
                        if (okButton != IntPtr.Zero)
                        {
                            // Simulate click
                            SendMessage(okButton, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
                            Console.WriteLine($"Closed error dialog in session {sessionInfo.SessionId}");
                        }
                        else
                        {
                            // Fallback: Send WM_CLOSE to the dialog if OK button isn't found
                            SendMessage(errorDialog, 0x0010, IntPtr.Zero, IntPtr.Zero); // WM_CLOSE
                            Console.WriteLine($"Closed error dialog via WM_CLOSE in session {sessionInfo.SessionId}");
                        }
                    }
                }
                else
                {
                    Console.WriteLine($"SetThreadDesktop failed for session {sessionInfo.SessionId}: {Marshal.GetLastWin32Error()}");
                }

                CloseDesktop(desktopHandle);
            }
        }
        finally
        {
            if (sessionInfoPtr != IntPtr.Zero)
                WTSFreeMemory(sessionInfoPtr);
        }
    }
}

Important Notes

  • Run as Administrator: This program must be executed with full admin privileges to access other user sessions and adjust token privileges.
  • Session Handling: The code targets active/connected sessions where users are logged in (interactive sessions).
  • Fallback Mechanism: If the OK button can't be found, it sends WM_CLOSE to the dialog directly as a backup.
  • Monitoring Loop: The example uses a 10-second interval; adjust this based on how frequently you expect errors to pop up.
  • Privileges: The code enables SeAssignPrimaryTokenPrivilege and SeIncreaseQuotaPrivilege which are required to interact with cross-session desktops.

内容的提问来源于stack exchange,提问作者Jeb50

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:57:06