应用程序无法写入注册表,即使用户具有管理员权限

6

我正在使用 Visual Studio 2010,编写一个需要在 HKLM\Software\myapp 下设置(和读取)新的注册表值的程序。

该程序基于.NET 2.0,目前运行在 Windows 7 64位系统上。以下是我的代码:

RegistryKey softwareKey = Registry.LocalMachine.OpenSubKey("Software", true);
RegistryKey MyKey = softwareKey.CreateSubKey("MyApp");
RegistryKey  = MyKey.CreateSubKey("MyKey");
selfPlacingWindowKey.SetValue("instaldateperson", datestr + usrname);     

我在运行Visual Studio 2010时遇到的问题是,它会作为我的用户身份运行应用程序,我是一个用户并且属于本地管理员组..但是我无法创建注册表项(尽管我是本地管理员组的成员,有权这样做)。我也不知道如何以登录形式执行此操作(但这也不是我想要的,因为那样我将在代码中放置Adminuser和密码,而我已经是管理员??那为什么还要这样做呢?)
如果完全不可能,是否有创建注册表项的选项?
某种方式将其添加到项目中?我有点困惑。

这个问题很简单,涉及到 HKLM,该应用程序处理硬件设置并作为安装程序的一部分。最终,我将注册表部分放入机器的部署部分,因此我可以在 Windows 安装后(就在用户登录之前)运行 VB 脚本。这对我解决了这个需求。由于这是一个好的工作解决方案而不是一个解决方案,所以我保持这种方式。 - user613326
6个回答

10

仅仅因为你以管理员身份运行(或使用具有管理特权的帐户)并不意味着这些管理权限总是生效。这是一项安全措施,防止恶意软件利用那些愚蠢地使用管理员权限进行计算机操作的用户。

要使用你的管理权限,你需要提升进程权限。有两种方法可以做到:

  1. 使用一个指示应用程序需要管理员权限的清单,在启动时强制提升权限。

    这意味着你的应用程序将始终以提升的权限运行,并且应该只在你的应用程序需要此权限时使用。例如,Windows注册表编辑器(RegEdit)就是这样做的,因为没有管理员权限你几乎无法在里面执行任何操作。

    关于如何实现这一点,可以在MSDN网站上找到相关信息,或者参考我在这里给出的答案。基本上,你只需要将以下行添加到你的清单中:

<requestedExecutionLevel level="requireAdministrator" />
如果你只需要某些任务的管理员权限(例如将特定设置保存到注册表中),而你的应用程序的其余部分不需要该权限,那么你应该启动一个新的提升进程来完成这个任务。没有办法临时提升当前进程的权限,因此你实际上需要启动一个新的进程。
这种方法的优点是,你的应用程序不必一直以管理员权限运行(这增加了安全性),那些没有管理员权限的用户仍然可以运行你的应用程序来完成其他所有事情(即所有除了需要提升权限的一两个任务外的所有事情)。
你可以通过使用Process类编写出包含写入注册表所需逻辑的单独进程,并使用runas动词为该进程请求提升权限。有关更多信息,请参见此问题。我还写了一个答案,其中提供了如何从C#完成这个过程的完整描述和示例代码。
当然,正如其他答案所提到的,更有可能的是你的应用程序设计有误。Windows安全模型的整个逻辑是,常规应用程序不需要管理员权限。它们不需要写入注册表或执行其他可能会危及计算机的操作。如果你需要持久化设置,我可以建议另外两种可能的方法:
1. 认识到Windows确实是一个多用户操作系统,并且只为当前用户编写你的设置。这很有道理,因为不同的用户通常具有不同的设置和偏好。你应该使用HKEY_CURRENT_USER而不是需要管理员权限才能访问的HKEY_LOCAL_MACHINE注册表分支。将你的第一行代码改为:
 RegistryKey softwareKey = Registry.CurrentUser.OpenSubKey("Software", true);
  • 通过使用.NET内置的逻辑来持久化应用程序的设置,省去了编写注册表的所有麻烦和复杂性。开始阅读这里这里或者在MSDN上学习如何做到这一点。我认为这绝对是您最好的选择。不要编写复杂的代码来做一些您使用的框架已经内置支持并轻松完成的事情。


  • 好的,关键点是在安装过程中我想设置一些密钥,而这些密钥不应该是HKCU的一部分,而是应该作为HKLM的一部分。 - user613326
    非常好的回答。值得一提的是,我还不得不将平台目标设置为“任何 CPU”,而不是“x86”,才能够以编程方式写入 HKLM。花了几个小时才弄清楚…… o_O - Dan W

    3
    你的应用程序设计可能有误。标准桌面应用程序不应该写入HKEY_LOCAL_MACHINE。由于用户账户控制(UAC)的存在,你需要具有管理员权限,并运行在提升的进程中,才能够写入 HKLM。
    如果你的应用程序确实需要对 HKLM 进行更改,则应该考虑在安装时完成,因为安装程序将以提升的方式运行。
    如果桌面应用程序确实需要写入 HKLM,则应该考虑将需要运行提升的部分分离到一个单独的进程中。否则,用户将非常不满意每次运行你的应用程序都需要通过 UAC 对话框进行确认,即使他们并没有使用那个写入 HKLM 的部分。而且,如果你强制整个应用程序都需要提升运行,则标准用户将无法运行它。

    如何在我的应用程序代码中进行一些安装后的操作?这些操作将从我的应用程序内部例程中触发,以便在安装时执行。 - user613326
    我不完全理解那个问题。如果它在安装时触发,那么它就不是后安装。 - David Heffernan

    2
    您应该在方法中的键后面添加true值来授予可写权限。
    Registry.LocalMachine.CreateSubKey(@"YOURKEY", true);
    Registry.LocalMachine.OpenSubKey(@"YOURKEY", true);
    

    此外,您需要提升权限才能执行该操作,然后可以创建一个.manifest文件并在其中设置级别为requireAdministrator,如下所示:

    <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
    

    注意: 在你的代码中:

    RegistryKey softwareKey = Registry.LocalMachine.OpenSubKey("Software", true);
    RegistryKey MyKey = softwareKey.CreateSubKey("MyApp");
    RegistryKey  = MyKey.CreateSubKey("MyKey");
    selfPlacingWindowKey.SetValue("instaldateperson", datestr + usrname);
    

    检查对象标签是否正确,您正在使用标签softwareKeyMyKey创建一个对象,但是要设置值,您正在使用另一个标签selfPlacingWindowKey


    2
    您无法在运行Visual Studio时在HKEY_LOCAL_MACHINE下创建密钥的原因是因为Visual Studio没有作为提升的进程运行。对于最终用户,应用程序的清单需要指示需要完全管理员权限。这里是关于嵌入UAC清单的文档。如果注册表键不需要在整个计算机上全局使用,则考虑写入HKEY_CURRENT_USER。

    0
    继续。在您的项目中创建一个.manifest文件。将其命名为可执行文件相同的名称。"name_of_executable.exe.manifest"最后更改或创建以下标记:
    <requestedExecutionLevel level = "requireAdministrator" uiAccess = "false" />
    

    0

    之前的回答提到,您需要在清单中指定需要管理员权限才能使用HKLM

    您可以通过以下方式创建清单:

    1. 右键单击您的项目,
    2. 转到添加新项
    3. 点击常规
    4. 点击应用程序清单文件
    5. 点击添加
    6. 打开您的清单文件并找到并取消注释requireAdministrator

    可能会让您困惑的更微妙的事情是,在项目设置中选中了Prefer 32bit

    这意味着注册表无法获得所需的访问掩码,而且没有内部异常会默默失败,因为错误处理程序需要相同的访问掩码,因此它无法被利用。

    除非您绝对需要跨用户保留设置,否则应避免将东西存储在注册表的此部分中,也不应将密码或其他敏感信息存储在其中,即使使用ACLs也无法可靠地控制访问。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接