如何获取目标计算机所有活跃会话并模拟执行壁纸修改代码?
问题描述
我有如下代码用于修改壁纸:
public static void Registrar(string caminho, Estilo estilo) { try { RegistryKey registryKey = Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop", true); switch (estilo) { case Estilo.Fill: registryKey.SetValue(@"WallpaperStyle", 10.ToString()); registryKey.SetValue(@"TileWallpaper", 0.ToString()); break; case Estilo.Fit: registryKey.SetValue(@"WallpaperStyle", 6.ToString()); registryKey.SetValue(@"TileWallpaper", 0.ToString()); break; case Estilo.Span: registryKey.SetValue(@"WallpaperStyle", 22.ToString()); registryKey.SetValue(@"TileWallpaper", 0.ToString()); break; case Estilo.Stretch: registryKey.SetValue(@"WallpaperStyle", 2.ToString()); registryKey.SetValue(@"TileWallpaper", 0.ToString()); break; case Estilo.Tile: registryKey.SetValue(@"WallpaperStyle", 0.ToString()); registryKey.SetValue(@"TileWallpaper", 1.ToString()); break; case Estilo.Center: registryKey.SetValue(@"WallpaperStyle", 0.ToString()); registryKey.SetValue(@"TileWallpaper", 0.ToString()); break; default: registryKey.SetValue(@"WallpaperStyle", 10.ToString()); registryKey.SetValue(@"TileWallpaper", 0.ToString()); break; } SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, caminho, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE); } catch { throw; } }
背景:需要向组织内多台机器发送并执行程序,通过LogMeIn登录上传执行,但当前代码仅修改LogMeIn登录用户的壁纸。
问题:需要获取目标计算机的所有活跃会话,模拟这些会话执行上述代码,实现为所有会话修改壁纸。
补充说明:尝试过以下方案,但出现“无法模拟”错误:
while (!stoppingToken.IsCancellationRequested) { IntPtr usuarioSessaoToken = IntPtr.Zero; Dictionary<string, IntPtr> usuariosLogados = new Dictionary<string, IntPtr>(); foreach (Process process in Process.GetProcesses()) { try { OpenProcessToken(process.Handle, TOKEN_QUERY, out usuarioSessaoToken); if (usuarioSessaoToken != null && usuarioSessaoToken != IntPtr.Zero) { using (WindowsIdentity windowsIdentity = new WindowsIdentity(usuarioSessaoToken)) { if (!usuariosLogados.ContainsKey(windowsIdentity.Name) && !windowsIdentity.Name.Contains("NT ") && !windowsIdentity.Name.Contains("Font ") && !windowsIdentity.Name.Contains("Window ")) { usuariosLogados.Add(windowsIdentity.Name, usuarioSessaoToken); } } } } catch { } } foreach (var usuarioLogado in usuariosLogados) { Console.WriteLine("{0}: {1}", usuarioLogado.Value, usuarioLogado.Key); using (WindowsIdentity windowsIdentity = new WindowsIdentity(usuarioLogado.Value)) { WindowsIdentity.RunImpersonated(windowsIdentity.AccessToken, () => { _OnSite.InicializarProcesso(modeloConfiguracao); }); } if (usuarioLogado.Value != IntPtr.Zero) { CloseHandle(usuarioLogado.Value); } } if (usuarioSessaoToken != IntPtr.Zero) { CloseHandle(usuarioSessaoToken); } ArquivoLog.EscreverInformacao("PROCESSO DE GERENCIAMENTO INTERNO FINALIZADO."); ArquivoLog.EscreverInformacao("ENTRARÁ EM ESPERA DE TRÊS (3) HORAS."); await Task.Delay(TimeSpan.FromHours(3), stoppingToken); }
求可行解决方案?
可行解决方案
核心思路
要修改所有活跃用户的壁纸,关键在于:
- 正确获取每个活跃用户的SID和会话ID
- 直接修改对应用户的注册表项(绕过模拟权限问题)
- 向对应会话发送壁纸更新通知
具体实现步骤
1. 定义必要的Win32 API调用
using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Security.Principal; using Microsoft.Win32; public static class Win32Api { public const int SPI_SETDESKWALLPAPER = 0x0014; public const int SPIF_UPDATEINIFILE = 0x01; public const int SPIF_SENDWININICHANGE = 0x02; [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern bool SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni); [DllImport("wtsapi32.dll")] public static extern bool WTSEnumerateSessions(IntPtr hServer, int Reserved, int Version, ref IntPtr ppSessionInfo, ref int pCount); [DllImport("wtsapi32.dll")] public static extern void WTSFreeMemory(IntPtr pMemory); [DllImport("wtsapi32.dll", CharSet = CharSet.Auto)] public static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WTS_INFO_CLASS wtsInfoClass, ref IntPtr ppBuffer, ref int pBytesReturned); [DllImport("advapi32.dll", SetLastError = true)] public static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle); [DllImport("advapi32.dll", SetLastError = true)] public static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess, IntPtr lpTokenAttributes, SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, TOKEN_TYPE TokenType, out IntPtr phNewToken); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool CloseHandle(IntPtr hObject); public enum WTS_INFO_CLASS { WTSUserName = 5, WTSDomainName = 7 } public enum SECURITY_IMPERSONATION_LEVEL { SecurityImpersonation } public enum TOKEN_TYPE { TokenPrimary = 1 } public struct WTS_SESSION_INFO { public int SessionId; public IntPtr pWinStationName; public WTS_CONNECTSTATE_CLASS State; } public enum WTS_CONNECTSTATE_CLASS { WTSActive } }
2. 获取所有活跃用户会话ID
public static List<int> GetActiveSessionIds() { List<int> activeSessions = new List<int>(); IntPtr serverHandle = IntPtr.Zero; IntPtr sessionInfoPtr = IntPtr.Zero; int sessionCount = 0; try { if (Win32Api.WTSEnumerateSessions(serverHandle, 0, 1, ref sessionInfoPtr, ref sessionCount)) { int sessionSize = Marshal.SizeOf(typeof(Win32Api.WTS_SESSION_INFO)); IntPtr currentSessionPtr = sessionInfoPtr; for (int i = 0; i < sessionCount; i++) { Win32Api.WTS_SESSION_INFO sessionInfo = (Win32Api.WTS_SESSION_INFO)Marshal.PtrToStructure(currentSessionPtr, typeof(Win32Api.WTS_SESSION_INFO)); if (sessionInfo.State == Win32Api.WTS_CONNECTSTATE_CLASS.WTSActive) { activeSessions.Add(sessionInfo.SessionId); } currentSessionPtr = IntPtr.Add(currentSessionPtr, sessionSize); } } } finally { if (sessionInfoPtr != IntPtr.Zero) { Win32Api.WTSFreeMemory(sessionInfoPtr); } } return activeSessions; }
3. 修改指定会话用户的壁纸
public static void SetWallpaperForSession(int sessionId, string wallpaperPath, Estilo estilo) { IntPtr serverHandle = IntPtr.Zero; IntPtr userNamePtr = IntPtr.Zero; IntPtr domainNamePtr = IntPtr.Zero; int bytesReturned = 0; try { // 获取会话用户名和域名 Win32Api.WTSQuerySessionInformation(serverHandle, sessionId, Win32Api.WTS_INFO_CLASS.WTSUserName, ref userNamePtr, ref bytesReturned); string userName = Marshal.PtrToStringAuto(userNamePtr); Win32Api.WTSQuerySessionInformation(serverHandle, sessionId, Win32Api.WTS_INFO_CLASS.WTSDomainName, ref domainNamePtr, ref bytesReturned); string domainName = Marshal.PtrToStringAuto(domainNamePtr); string fullUserName = $"{domainName}\\{userName}"; // 获取用户SID NTAccount ntAccount = new NTAccount(fullUserName); SecurityIdentifier sid = (SecurityIdentifier)ntAccount.Translate(typeof(SecurityIdentifier)); string sidString = sid.ToString(); // 修改用户注册表项 using (RegistryKey userKey = Registry.Users.OpenSubKey($@"{sidString}\Control Panel\Desktop", true)) { if (userKey == null) return; switch (estilo) { case Estilo.Fill: userKey.SetValue("WallpaperStyle", "10", RegistryValueKind.String); userKey.SetValue("TileWallpaper", "0", RegistryValueKind.String); break; case Estilo.Fit: userKey.SetValue("WallpaperStyle", "6", RegistryValueKind.String); userKey.SetValue("TileWallpaper", "0", RegistryValueKind.String); break; case Estilo.Span: userKey.SetValue("WallpaperStyle", "22", RegistryValueKind.String); userKey.SetValue("TileWallpaper", "0", RegistryValueKind.String); break; case Estilo.Stretch: userKey.SetValue("WallpaperStyle", "2", RegistryValueKind.String); userKey.SetValue("TileWallpaper", "0", RegistryValueKind.String); break; case Estilo.Tile: userKey.SetValue("WallpaperStyle", "0", RegistryValueKind.String); userKey.SetValue("TileWallpaper", "1", RegistryValueKind.String); break; case Estilo.Center: userKey.SetValue("WallpaperStyle", "0", RegistryValueKind.String); userKey.SetValue("TileWallpaper", "0", RegistryValueKind.String); break; default: userKey.SetValue("WallpaperStyle", "10", RegistryValueKind.String); userKey.SetValue("TileWallpaper", "0", RegistryValueKind.String); break; } userKey.SetValue("Wallpaper", wallpaperPath, RegistryValueKind.String); } // 获取会话进程令牌并发送更新通知 IntPtr primaryToken = GetSessionPrimaryToken(sessionId); if (primaryToken != IntPtr.Zero) { WindowsIdentity.RunImpersonated(primaryToken, () => { Win32Api.SystemParametersInfo(Win32Api.SPI_SETDESKWALLPAPER, 0, wallpaperPath, Win32Api.SPIF_UPDATEINIFILE | Win32Api.SPIF_SENDWININICHANGE); }); Win32Api.CloseHandle(primaryToken); } } finally { if (userNamePtr != IntPtr.Zero) Win32Api.WTSFreeMemory(userNamePtr); if (domainNamePtr != IntPtr.Zero) Win32Api.WTSFreeMemory(domainNamePtr); } } // 获取会话主令牌 private static IntPtr GetSessionPrimaryToken(int sessionId) { foreach (Process process in Process.GetProcesses()) { if (process.SessionId == sessionId && process.ProcessName.Equals("explorer", StringComparison.OrdinalIgnoreCase)) { IntPtr processToken = IntPtr.Zero; if (Win32Api.OpenProcessToken(process.Handle, 0x0002 | 0x0008, out processToken)) { IntPtr primaryToken = IntPtr.Zero; if (Win32Api.DuplicateTokenEx(processToken, 0x0010 | 0x0020 | 0x0008, IntPtr.Zero, Win32Api.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, Win32Api.TOKEN_TYPE.TokenPrimary, out primaryToken)) { Win32Api.CloseHandle(processToken); return primaryToken; } Win32Api.CloseHandle(processToken); } break; } } return IntPtr.Zero; }
4. 批量修改所有活跃会话壁纸
public static void SetWallpaperForAllActiveSessions(string wallpaperPath, Estilo estilo) { List<int> activeSessions = GetActiveSessionIds(); foreach (int sessionId in activeSessions) { try { SetWallpaperForSession(sessionId, wallpaperPath, estilo); } catch (Exception ex) { ArquivoLog.EscreverErro($"无法修改会话 {sessionId} 的壁纸: {ex.Message}"); } } }
关键注意事项
- 权限要求:程序必须以管理员权限运行,否则无法访问其他用户的注册表和进程令牌。
- 路径要求:壁纸路径必须是目标机器的本地绝对路径,不能使用网络共享路径(除非映射为本地驱动器)。
- 资源释放:所有Win32 API获取的指针和句柄必须及时释放,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Rodrigo Duarte
相关产品推荐
相关产品推荐

