绝大多数桌面应用应写入HKEY_CURRENT_USERSoftwareMicrosoftWindowsCurrentVersionRun,因其无需管理员权限、仅对当前用户生效且更安全;HKEY_LOCAL_MACHINE需提权,普通场景易抛UnauthorizedAccessException异常。
开机自启必须写进 CurrentUser 还是 LocalMachine?
绝大多数桌面应用应该写入
,而不是
。前者无需管理员权限,用户级生效,且不会因系统多账户导致冲突;后者需要 UAC 提权,普通安装场景下会直接抛出
异常。
常见错误是硬写 LocalMachine 还没加 manifest 请求提权,结果程序静默失败——连日志都不报,因为
注册表
访问被拦截在底层。
普通工具类软件、个人配置型应用,一律走
只有真正需要系统级服务(如后台守护进程、驱动配套 UI)才考虑
,且必须配
写入路径建议用
做备选(对应当前用户的「启动」文件夹),但注意它只对 Shell 启动有效,不等价于 Run 键值
用 RegistryKey.CreateSubKey 写入 Run 项的正确姿势
别直接 OpenSubKey + SetValue ——如果父键不存在(比如某些精简版系统删了 Run 键),会抛
。必须用
并检查返回值是否为 null。
注意:
的第二个参数必须是完整可执行路径,不能带参数;如果要传参(比如静默启动),得包装成快捷方式或用启动脚本,注册表 Run 不支持命令行参数解析。
键名("MyApp")建议用产品名缩写,避免空格和特殊字符,否则某些旧版组策略可能误判
路径中反斜杠必须是双写
或用 @ 字符串,单反斜杠会触发转义错误
写入后无需重启,下次登录即生效;想立即测试,可手动运行
看是否同步出现快捷方式(仅限 Startup 文件夹方案)
卸载时忘记清理注册表 = 持续残留自启
这是最常被忽略的一环。安装时写了 Run,卸载时却只删文件、不碰注册表,用户重装后会出现两个同名启动项,甚至指向已删除路径,造成每次登录弹 CMD 窗口报错。
C知道
CSDN推出的一款AI技术问答工具
下载
卸载逻辑必须匹配写入位置:如果写的是
,卸载时也必须用
去 DeleteValue;用错根键会导致“以为删了,其实还在”。
推荐在主程序退出前加一句清理(适合便携版或免安装场景):
安装包(如 WiX/Inno)需显式声明注册表项为可卸载资源,不能只靠自定义操作脚本
调试时可用
过滤 RegOpenKey/RegSetValue,确认实际写入路径和值名是否符合预期
Win10/11 的「启动应用」设置页会隐藏注册表自启项吗?
不会隐藏,但会分类显示:通过 Run 键值添加的条目,在「设置 > 应用 > 启动」里默认显示为「已启用」,名称取自注册表键名("MyApp"),而非可执行文件名。用户可手动开关,这个开关只是修改对应键值的数据(置空或删值),不影响你代码里的逻辑。
真正要注意的是:如果用户在该页面关闭了你的应用,
仍会返回原路径(只是值被设为空字符串),所以检查自启状态时不能只看是否存在键值,得判断
。
不要依赖「设置页是否显示」来判断功能是否生效,它只是 UI 层封装,底层仍是注册表
某些杀软会劫持 Run 键的读写,表现为写入后立刻消失——此时应 fallback 到 Startup 文件夹方案
Windows Sandbox 或干净虚拟机中测试时,务必先确认 Run 键存在(有些镜像默认精简掉了),否则 CreateSubKey 可能返回 null
HKEY_CURRENT_USERSoftwareMicrosoftWindowsCurrentVersionRunHKEY_LOCAL_MACHINEUnauthorizedAccessExceptionCurrentUserLocalMachinerequestedExecutionLevel="requireAdministrator"Environment.GetFolderPath(Environment.SpecialFolder.Startup)NullReferenceExceptionCreateSubKeyusing (var key = Registry.CurrentUser.CreateSubKey(@"SoftwareMicrosoftWindowsCurrentVersionRun"))
{
if (key != null)
{
key.SetValue("MyApp", @"C:PathToMyApp.exe");
}
}SetValue\explorer shell:startupCurrentUserRegistry.CurrentUserRegistry.CurrentUser.OpenSubKey(...)?.DeleteValue("MyApp", false)Process MonitorRegistry.GetValue(...)value?.ToString() != ""