当前位置: 首页 > 编程 > 正文
韩 逸

环境 Win10专业版64位 Visual Studio 2012 权限 Win10下使用 RegOpenKe […]

环境

  1. Win10专业版64位
  2. Visual Studio 2012

权限

Win10下使用 RegOpenKeyEx() 和 RegCreateKeyEx() 会出现0x00000005(拒绝访问)的错误.是因为权限不足.

可将其权限设置为管理员权限.
右键属性->链接器->清单文件->UAC执行级别->改为requireAdministrator

此时再重新运行打开注册表会成功.

注册表重定向

在使用RegCreateKeyEx()向下面路径写入子键的时却出了问题:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\


从返回值中可得知创建成功,但在注册表中却找不到此子键.

起初原因是认为代码逻辑写错了,于是重新检查了一次参数后将可执行文件复制到虚拟机中XP环境下测试却正常能创建成功.

在群内一朋友的告知下得知可能是Windows 64位系统 注册表重定位导致了该问题,网上查询了一些资料.


众所周知,注册表是windows系统的数据库,系统本身以及安装的程序都依赖注册表.当windows进化到64位,还要兼容大量的32位老程序,便碰到了注册表的问题.

注册表树最大可以有512级深度,通过注册表API一次可以创建32级深的键值.

为了解决64位系统遇到的问题,Windows使用了三套方案,共享(Shared),注册表重定向(Registry Redirector)和注册表反射(Registry Reflection).

  • 共享,顾名思义,保存可以被32位和64位共同使用的注册表.

  • 反射,这是一个早期的方案,只用于Windows Server 2008, Windows Vista,Windows Server 2003, 和Windows XP,从Windows 7 和 Windows Server 2008 R2 开始被移除.

    它的做法是备份和同步,就是把同一份注册表保存到两个物理位置,这两个位置会分别被32位或64位程序使用.保存发生在RegCloseKey调用结束.使用 RegDisableReflectionKey和RegEnableReflectionKey方法可以禁用/启用反射机制.

  • 重定向,主要使用的机制,它会区分32位和64位程序(Application),分别提供给他们不同的注册表物理存储位置,但会映射成同一个逻辑视图(View),这个过程对程序本身是透明的.也就是说,一个32位程序可以像在32位系统中一样来使用注册表,虽然它们在64位系统上被存储在不同的物理位置.

    32位程序重定向的注册表存放在Wow6432Node下,例如,HKEY_LOCAL_MACHINE\Software 会被重定向到 HKEY_LOCAL_MACHINE\Software\Wow6432Node.但是,这些操作应该由系统而非程序本身来做.

* 上面是系统的解决方案,如果你需要显式地访问64位注册表或32位注册表,可以使用KEY_WOW64_64KEY和KEY_WOW64_32KEY标识.

微软NT 6.0开始推出注册表虚拟化.
MSDN上的说法是:

注册表虚拟化是一种应用程序兼容技术,让那些可能带来全局影响的注册表写入操作重定向到每个用户的位置。这个读取或者写入重定向对于程序而言都是透明的。该技术从 Windows Vista 开始支持。

准确来说,因为向HKEY_LOCAL_MACHINE写入注册表,是会影响到电脑上所有用户,为了避免这种全局的影响,微软针对其写入操作进行来了重定向.究其原因,是XP上并没有UAC,任意程序都可以随意写入HKEY_LOCAL_MACHINE.然而,从 Windows Vista 开始引入 UAC 之后,微软当然不允许低权限程序来随意操作 HKEY_LOCAL_MACHINE 了,但这样的话又可能会权限问题写入失败,就有可能导致程序运行出错,所以,为了早期的程序能正常运行且不影响现有注册表,微软引入了这个技术,以保证老的程序不会因为权限问题导致注册表写入失败。那么如何避免重定向呢?微软说要嵌入 manifest 并指定应用程序的执行权限,否则程序的注册表读写操作将注册表虚拟化技术重定向到其他位置.manifest 文件在 Visual Studio 中被称为清单文件.


* asInvoker
以和调用该程序的进程同样的权限级别执行。也可以在右键菜单中选择使用管理员权限执行,但程序不会主动请求管理员权限,即便当前用户具备以管理员执行的条件。
* highestAvailable
以当前用户可以获得的最高权限来执行。即当前用户具备以管理员执行的条件时,会请求管理员权限,这种情况下和 requireAdministrator 一样。如果当前用户不具备管理员权限,则类似于 asInvoker 的情况。
* requireAdministrator
始终请求管理员权限。如果当前用户不具备管理员权限,则程序无法执行。

如果工程中并没有嵌入清单文件,或者嵌入的清单文件并没有指定执行权限,那么工程的注册表写入将会被重定向,而不是返回代码失败.如下面的代码:

void CRegDemoDlg::OnBnClickedRegcreate()
{
    // TODO: 在此添加控件通知处理程序代码
    HKEY hSubKey = NULL;
    TCHAR *lpszSubKey = _T("SOFTWARE\\TestKey");
    LONG lRet = RegCreateKeyEx(
        HKEY_LOCAL_MACHINE,
        lpszSubKey,
        0,
        NULL,
        REG_OPTION_NON_VOLATILE,
        KEY_ALL_ACCESS,
        NULL,
        &hSubKey,
        NULL
        );

    if (lRet == ERROR_SUCCESS)
    {
        TCHAR szValue[] = _T("TestValue");
        TCHAR szData[] = _T("TestData");
        DWORD dwSize = lstrlen(szData) * sizeof(TCHAR) + sizeof(TCHAR);
        lRet = RegSetValueEx(hSubKey, szValue, 0, REG_SZ, (BYTE *)szData, dwSize);

        if (lRet == NO_ERROR)
        {
            AfxMessageBox(_T("创建或者打开成功."));
        }
        RegCloseKey(hSubKey);
    }
    else
    {
        AfxMessageBox(_T("创建或者打开失败."));
    }

}

  • 在工程未嵌入清单文件或者其中不包含权限信息时,且程序未以管理员权限执行的情况下,期望的返回值是 ERROR_ACCESS_DENIED , 实际返回值 却是 ERROR_SUCCESS .调用 RegSetValueEx 也同样会成功,然而,打开注册表编辑器在HKEY_LOCAL_MACHINE\SOFTWARE下没有写入任何信息,使用Registry Workshop建立执行前后两个注册表快照,对比之后发现,注册表的写入被重定向到:
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\TestKey

  • 当读取的时候,也是从上述位置读取,因此实际上也返回成功.就会造成一种假象,注册表系列API有BUG,明明没有写入任何值,却返回成功,而且看似根本没有写进去却还能读取成功.

  • 在 64 位系统上,32 位程序读写部分注册表路径时,会被系统所重定向,这有些类似于读写 System32 文件夹的处理方式。比如,写入 HKEY_LOCAL_MACHINE\Software\TestKey,却发现实际写入到 HKEY_LOCAL_MACHINE\Software\Wow6432Node\TestKey,读取亦是如此.

  • 在 32 位系统上,不存在这些问题。

本文固定链接: http://blog.050k.com/?p=388 | 简单生活's Blog

32位程序在64位注册表上重定向问题:等您坐沙发呢!

发表评论

快捷键:Ctrl+Enter