Windows服务自动修改注册表值失败求助:HKCU路径无法访问
Windows服务修改HKCU注册表失败问题排查
问题背景
开发了一个Windows服务,目标是检测到cs.exe进程启动时,自动修改HKCU\SOFTWARE\Valve\Half-Life\nextclient下的branch和PartnerId键值为指定内容,但运行后日志始终提示Registry path not found or no write access: HKCU\SOFTWARE\Valve\Half-Life\nextclient,无法完成修改。
服务代码
using Microsoft.Win32; using System; using System.Diagnostics; using System.IO; using System.Linq; using System.ServiceProcess; using System.Threading; public class Rab3ounService : ServiceBase { private static readonly string targetProcessRegistry = "cs.exe"; private const string NEW_REGISTRY_VALUE = "test"; private const int MONITORING_INTERVAL_MS = 5000; private static string logFilePath = @"C:\ProgramData\Rab3ounService\service_log.txt"; private static readonly HashSet<uint> registryEditedProcesses = new HashSet<uint>(); private static bool isRunning = true; private static StreamWriter logWriter = null; private static Thread workerThread = null; private static void Log(string message) { try { string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"); string logMessage = $"[{timestamp}] {message}"; logWriter?.WriteLine(logMessage); } catch (Exception ex) { try { EventLog.WriteEntry("Rab3ounService", $"Error writing to log file: {ex.Message}\nOriginal message: {message}", EventLogEntryType.Warning); } catch { } } } private static void ModifyNextclientRegistryKeys() { const string basePath = @"SOFTWARE\Valve\Half-Life\nextclient"; const string branchValueName = "branch"; const string partnerIdValueName = "PartnerId"; int successCount = 0; int failCount = 0; Log($"Attempting to modify registry keys under HKCU\\{basePath}"); Log("IMPORTANT: This modifies HKEY_CURRENT_USER for the account the service runs as!"); RegistryKey baseKey = null; try { baseKey = Registry.CurrentUser.OpenSubKey(basePath, true); if (baseKey == null) { Log($"Registry path not found or no write access: HKCU\\{basePath}"); return; } string[] subKeyNames = baseKey.GetSubKeyNames(); if (subKeyNames.Length == 0) { Log($"No subkeys found under HKCU\\{basePath}"); return; } Log($"Found {subKeyNames.Length} subkeys under HKCU\\{basePath}. Processing each..."); foreach (string subKeyName in subKeyNames) { RegistryKey subKey = null; bool currentSuccess = false; try { subKey = baseKey.OpenSubKey(subKeyName, true); if (subKey != null) { try { subKey.SetValue(branchValueName, NEW_REGISTRY_VALUE, RegistryValueKind.String); subKey.SetValue(partnerIdValueName, NEW_REGISTRY_VALUE, RegistryValueKind.String); Log($" -> SUCCESS: Set '{branchValueName}' and '{partnerIdValueName}' in subkey '{subKeyName}'"); currentSuccess = true; } catch (Exception setValueEx) { Log($" -> ERROR setting values in subkey '{subKeyName}': {setValueEx.Message}"); } } else { Log($" -> ERROR: Could not open subkey '{subKeyName}' for writing."); } } catch (System.Security.SecurityException secEx) { Log($" -> SECURITY ERROR accessing subkey '{subKeyName}': {secEx.Message}"); } catch (UnauthorizedAccessException uaEx) { Log($" -> UNAUTHORIZED access modifying subkey '{subKeyName}': {uaEx.Message}"); } catch (Exception ex) { Log($" -> GENERAL ERROR processing subkey '{subKeyName}': {ex.Message}"); } finally { subKey?.Close(); if (currentSuccess) successCount++; else failCount++; } } Log($"Finished processing registry subkeys. Success: {successCount}, Failures: {failCount}."); } catch (System.Security.SecurityException secEx) { Log($"Security error accessing base key HKCU\\{basePath}: {secEx.Message}"); } catch (UnauthorizedAccessException uaEx) { Log($"Unauthorized access opening base key HKCU\\{basePath}: {uaEx.Message}"); } catch (Exception ex) { Log($"Error accessing registry path HKCU\\{basePath}: {ex.Message}"); } finally { baseKey?.Close(); } } protected override void OnStart(string[] args) { try { string logDirectory = Path.GetDirectoryName(logFilePath); if (!Directory.Exists(logDirectory)) { Directory.CreateDirectory(logDirectory); } logWriter = new StreamWriter(logFilePath, true) { AutoFlush = true }; Log("=================================================="); Log("Service starting..."); } catch (Exception ex) { try { EventLog.WriteEntry(this.ServiceName, $"FATAL: Failed to initialize file logging to '{logFilePath}'. Error: {ex.Message}", EventLogEntryType.Error); } catch { } logWriter = null; } isRunning = true; registryEditedProcesses.Clear(); workerThread = new Thread(ServiceWorkerThread); workerThread.IsBackground = true; workerThread.Name = "Rab3ounMonitorThread"; workerThread.Start(); Log($"Service started successfully. Monitoring for: {targetProcessRegistry} (registry)"); Log($"Log file path: {logFilePath}"); Log($"Monitoring interval: {MONITORING_INTERVAL_MS} ms"); } protected override void OnStop() { Log("Service stopping request received..."); isRunning = false; if (workerThread != null && workerThread.IsAlive) { Log("Waiting for worker thread to finish..."); bool stopped = workerThread.Join(MONITORING_INTERVAL_MS + 2000); if (!stopped) { Log("Warning: Worker thread did not finish gracefully within the timeout."); } else { Log("Worker thread finished."); } } Log("Closing log file."); logWriter?.Close(); logWriter = null; Log("Service stopped."); Console.WriteLine("Rab3ounService stopped."); } private static void ServiceWorkerThread() { Log("Worker thread started."); while (isRunning) { HashSet<uint> currentRunningRegistryProcessIds = new HashSet<uint>(); bool registryCheckDone = false; try { #region Check Registry Process (cs.exe) try { Process[] registryProcesses = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(targetProcessRegistry)); foreach (Process process in registryProcesses) { using (process) { uint currentProcessId = (uint)process.Id; currentRunningRegistryProcessIds.Add(currentProcessId); if (!registryEditedProcesses.Contains(currentProcessId)) { Log($"Detected new instance of {targetProcessRegistry} (PID: {currentProcessId}). Applying registry changes..."); ModifyNextclientRegistryKeys(); registryEditedProcesses.Add(currentProcessId); Log($"Registry changes attempt completed for PID: {currentProcessId}. Marked as processed."); } } } registryCheckDone = true; } catch (Exception pEx) { Log($"Error checking for '{targetProcessRegistry}' processes: {pEx.Message}"); } #endregion #region Cleanup Tracking Sets if (registryCheckDone) registryEditedProcesses.RemoveWhere(id => !currentRunningRegistryProcessIds.Contains(id)); #endregion } catch (Exception ex) { Log($"FATAL ERROR in monitoring loop: {ex.ToString()}"); } int sleptTime = 0; while (isRunning && sleptTime < MONITORING_INTERVAL_MS) { Thread.Sleep(200); sleptTime += 200; } } Log("Worker thread finished."); } public static void Main(string[] args) { if (Environment.UserInteractive) { Console.WriteLine("Starting service in console mode for debugging..."); Rab3ounService service = new Rab3ounService(); service.OnStart(args); Console.WriteLine("Service running. Press Enter to stop."); Console.ReadLine(); Console.WriteLine("Stopping service..."); service.OnStop(); Console.WriteLine("Service stopped. Exiting console mode."); } else { ServiceBase.Run(new Rab3ounService()); } } }
运行日志
[2025-04-09 05:43:16.105] ================================================== [2025-04-09 05:43:16.111] Service starting... [2025-04-09 05:43:16.111] Service started successfully. Monitoring for: hl.exe (files) and cs.exe (registry) [2025-04-09 05:43:16.111] Log file path: C:\ProgramData\Rab3ounService\service_log.txt [2025-04-09 05:43:16.112] Monitoring interval: 5000 ms [2025-04-09 05:43:16.116] Service stopping request received... [2025-04-09 05:43:16.116] Waiting for worker thread to finish... 025-04-09 05:43:16.116] Worker thread started. [2025-04-09 05:43:16.116] Worker thread finished. to finish... [2025-04-09 05:43:16.116] Worker thread finished. [2025-04-09 05:43:16.119] Worker thread finished. [2025-04-09 05:43:16.119] Closing log file. [2025-04-09 05:44:03.251] ================================================== [2025-04-09 05:44:03.256] Service starting... [2025-04-09 05:44:03.256] Service started successfully. Monitoring for: hl.exe (files) and cs.exe (registry) [2025-04-09 05:44:03.256] Log file path: C:\ProgramData\Rab3ounService\service_log.txt [2025-04-09 05:44:03.256] Monitoring interval: 5000 ms [2025-04-09 05:44:03.257] Worker thread started. [2025-04-09 05:44:23.778] Detected new instance of cs.exe (PID: 3228). Applying registry changes... [2025-04-09 05:44:23.778] Attempting to modify registry keys under HKCU\SOFTWARE\Valve\Half-Life\nextclient [2025-04-09 05:44:23.778] IMPORTANT: This modifies HKEY_CURRENT_USER for the account the service runs as! [2025-04-09 05:44:23.778] Registry path not found or no write access: HKCU\SOFTWARE\Valve\Half-Life\nextclient [2025-04-09 05:44:23.778] Registry changes attempt completed for PID: 3228. Marked as processed. [2025-04-09 05:44:28.870] Detected new instance of cs.exe (PID: 6916). Applying registry changes... [2025-04-09 05:44:28.870] Attempting to modify registry keys under HKCU\SOFTWARE\Valve\Half-Life\nextclient [2025-04-09 05:44:28.870] IMPORTANT: This modifies HKEY_CURRENT_USER for the account the service runs as! [2025-04-09 05:44:28.870] Registry path not found or no write access: HKCU\SOFTWARE\Valve\Half-Life\nextclient [2025-04-09 05:44:28.870] Registry changes attempt completed for PID: 6916. Marked as processed. [2025-04-09 05:44:33.954] Detected new instance of cs.exe (PID: 13948). Applying registry changes... [2025-04-09 05:44:33.954] Attempting to modify registry keys under HKCU\SOFTWARE\Valve\Half-Life\nextclient [2025-04-09 05:44:33.954] IMPORTANT: This modifies HKEY_CURRENT_USER for the account the service runs as! [2025-04-09 05:44:33.954] Registry path not found or no write access: HKCU\SOFTWARE\Valve\Half-Life\nextclient [2025-04-09 05:44:33.954] Registry changes attempt completed for PID: 13948. Marked as processed.
解决方案
1. 核心问题分析
Windows服务默认以Local System账户运行,该账户的HKCU(当前用户注册表)是系统自身的配置分支,而非当前登录用户的注册表。而cs.exe是用户登录后启动的,对应的注册表项存在于当前登录用户的HKCU中,服务无法直接访问到这个路径。
2. 解决步骤
要修改当前登录用户的注册表,需要加载该用户的注册表 hive(NTUSER.DAT文件)到系统注册表的临时路径,修改完成后再卸载。
3. 修改后的关键代码
添加获取当前活跃用户SID的方法,并修改注册表操作逻辑:
using System.Management; // 需要添加System.Management引用 // 获取当前活跃用户的SID private static string GetCurrentActiveUserSid() { using (var searcher = new ManagementObjectSearcher("SELECT UserSID FROM Win32_ComputerSystem")) { foreach (var obj in searcher.Get()) { return obj["UserSID"]?.ToString(); } } return null; } // 修改后的注册表操作方法 private static void ModifyNextclientRegistryKeys() { const string basePath = @"SOFTWARE\Valve\Half-Life\nextclient"; const string branchValueName = "branch"; const string partnerIdValueName = "PartnerId"; int successCount = 0; int failCount = 0; string userSid = GetCurrentActiveUserSid(); if (string.IsNullOrEmpty(userSid)) { Log("Failed to get current active user SID."); return; } string tempHivePath = $@"HKEY_USERS\{userSid}"; string userNtuserPath = $@"C:\Users\{Environment.UserName}\NTUSER.DAT"; RegistryKey userHive = null; try { // 尝试加载用户注册表hive(需要管理员权限) Process regLoadProcess = Process.Start(new ProcessStartInfo { FileName = "reg.exe", Arguments = $"load {tempHivePath} \"{userNtuserPath}\"", Verb = "runas", CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden }); regLoadProcess?.WaitForExit(); // 打开用户注册表分支 userHive = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default) .OpenSubKey($@"{userSid}\{basePath}", true); if (
相关产品推荐
相关产品推荐

