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

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 (
相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 12:08:05