【发布时间】:2011-07-21 12:44:11
【问题描述】:
我的问题很简单,但是我在谷歌上搜索了很长时间才找到答案。
如何将REG_KEY_DONT_VIRTUALIZE 标志设置为我创建的注册表项(即HKLM\Software\MyApp)?
我希望我的程序独立于用户。每个启动我的应用程序的用户都应该可以访问位于该位置的相同配置选项)。
更改应用程序清单我可以通过以管理员身份运行程序来禁用注册表虚拟化,但我希望普通用户能够运行程序并读取注册表值。
【问题讨论】:
我的问题很简单,但是我在谷歌上搜索了很长时间才找到答案。
如何将REG_KEY_DONT_VIRTUALIZE 标志设置为我创建的注册表项(即HKLM\Software\MyApp)?
我希望我的程序独立于用户。每个启动我的应用程序的用户都应该可以访问位于该位置的相同配置选项)。
更改应用程序清单我可以通过以管理员身份运行程序来禁用注册表虚拟化,但我希望普通用户能够运行程序并读取注册表值。
【问题讨论】:
如果您不希望您的应用被虚拟化,那么您可以使用清单来表明这一点。如果您在密钥上使用REG_KEY_DONT_VIRTUALIZE,那么所有的写操作都会失败,因为您的用户没有对 HKLM 的写访问权限。
如果您希望所有用户共享配置,那么您必须将配置存储在文件中而不是注册表中。注册表中没有合适的地方由所有用户共享并允许标准用户写入访问权限。
【讨论】:
这很不清楚,虚拟化仅对旧的非 UAC 兼容程序启用,并且始终允许读取。我不得不假设 写作 是问题所在。使用您的安装程序或 Regedit.exe 更改密钥的权限,以便每个人都具有写入权限。
【讨论】:
在不更改或向密钥添加 ACL 的情况下,您可以通过使用带有 RegistryView.Registry64 标志的 RegistryKey.OpenBaseKey API 确保以编程方式使用的密钥正在查看注册表的 64 位部分。
无论是否为应用启用注册表虚拟化,这似乎都适用于 32 位应用。
private const string MyRegistryKeyPath = "Software\\My Company\\My App";
private static RegistryKey OpenMyAppRegistryKey(bool requireWriteAccess = false)
{
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
{
return requireWriteAccess
? baseKey.CreateSubKey(MyRegistryKeyPath, RegistryKeyPermissionCheck.ReadWriteSubTree)
: baseKey.OpenSubKey(MyRegistryKeyPath, RegistryKeyPermissionCheck.ReadSubTree);
}
}
如果requireWriteAccess为false,如果指定的key不存在,此方法将返回null。
我还应该指出,此代码需要提升权限才能打开密钥以进行写访问。但我相信它可以确保使用以这种方式打开的密钥进行的未提升读取只会来自注册表的 64 位视图。
【讨论】:
迄今为止,还没有用于设置注册表项标志的 C# 或 C API。
我认为最安全的方法是使用 CreateProcess 启动 REG.exe 命令行工具。
但是,为了记录,我已经粘贴了来自this blog 的一些“C”代码,它演示了使用未记录 API 的另一种方式:
typedef enum _CONTROL_FLAGS {
RegKeyClearFlags = 0,
RegKeyDontVirtualize = 2,
RegKeyDontSilentFail = 4,
RegKeyRecurseFlag = 8
} CONTROL_FLAGS;
typedef struct _KEY_CONTROL_FLAGS_INFORMATION {
ULONG ControlFlags;
} KEY_CONTROL_FLAGS_INFORMATION, *PKEY_CONTROL_FLAGS_INFORMATION;
typedef enum _KEY_SET_INFORMATION_CLASS {
KeyWriteTimeInformation,
KeyWow64FlagsInformation,
KeyControlFlagsInformation,
KeySetVirtualizationInformation,
KeySetDebugInformation,
MaxKeySetInfoClass // MaxKeySetInfoClass should always be the last enum
} KEY_SET_INFORMATION_CLASS;
NTSYSAPI NTSTATUS NTAPI NtSetInformationKey(
IN HANDLE KeyHandle,
IN KEY_SET_INFORMATION_CLASS InformationClass,
IN PVOID KeyInformationData,
IN ULONG DataLength );
typedef NTSYSAPI NTSTATUS (NTAPI* FuncNtSetInformationKey) (
HANDLE KeyHandle,
KEY_SET_INFORMATION_CLASS InformationClass,
PVOID KeyInformationData,
ULONG DataLength );
BOOL CRegLonMigration::SetDontVirtualizeFlag(LPCTSTR keyPath)
{
FuncNtSetInformationKey ntsik = (FuncNtSetInformationKey)GetProcAddress(GetModuleHandle( _T("ntdll.dll") ), "NtSetInformationKey" );
KEY_CONTROL_FLAGS_INFORMATION kcfi = {0};
kcfi.ControlFlags = RegKeyDontVirtualize | RegKeyRecurseFlag;
HKEY hKey = NULL;
LSTATUS status;
if (ERROR_SUCCESS == (status = ::RegOpenKeyEx(ROOT_KEY, keyPath, 0, KEY_ALL_ACCESS, &hKey)))
{
NTSTATUS status = ntsik( hKey, KeyControlFlagsInformation, &kcfi, sizeof( KEY_CONTROL_FLAGS_INFORMATION ) );
RegCloseKey( hKey );
return TRUE;
}
return FALSE;
}
【讨论】: