如何用本地管理员权限的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_CLOSEto 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
SeAssignPrimaryTokenPrivilegeandSeIncreaseQuotaPrivilegewhich are required to interact with cross-session desktops.
内容的提问来源于stack exchange,提问作者Jeb50
相关产品推荐
相关产品推荐

