自Win Vista发布以来,Microsoft引入了以32位进程运行的旧版应用程序的文件虚拟化。作为Microsoft用户帐户控制(UAC)的一部分,发布的所有试图写入被操作系统保护的位置的旧应用程序都将重定向到VirtualStore。
[此时,已采取措施确保所讨论的应用程序现在可以作为UAC感知的64位进程运行,但是,这并不能解决将用户数据迁移到安全的位置的问题。虚拟化。
[解决此问题时,我发现在处理多个用户帐户时,已对位于C:\Program Files(x86)\MyApp\Data
的旧路径进行了一些更改,而与此同时,对位于%localappdata%\VirtualStore\Programs\MyApp\Data
的VirtualStore也进行了更改。问题是,如何检测是否正在进行文件/文件夹虚拟化以及如何合并这两个位置?
EDIT:我发现了几个网站,详细介绍了该问题以及如何重复该问题,但没有任何网站提供解决此问题的方法。我确实找到了这个引用FILE_ATTRIBUTE_VIRTUAL,它定义了一个看似有希望的文件属性-我在某个地方找到了另一个引用,尽管我不记得在哪里,它指出这是Windows用来表示文件虚拟化正在发生的属性,并且标记重定向请求。
这些链接描述了问题:
http://www.c-sharpcorner.com/uploadfile/GemingLeader/windows-file-and-registry-virtualization/
http://www.codeproject.com/Articles/66275/Windows-Vista-File-and-Registry-Virtualization
并不容易,但是我发现了如何检测是否已启用UAC虚拟化。调用GetTokenInformation()
并传递GetTokenInformation()
作为信息类将返回是否启用了文件和注册表虚拟化。这是一个C函数来实现:
TokenVirtualizationEnabled
使用P / Invoke有点困难,但是这里包括P / Invoke标头:
// Gets whether the current process has UAC virtualization enabled.
// Returns TRUE on success and FALSE on failure.
BOOL GetVirtualizationEnabled(BOOL *enabled) {
HANDLE token;
DWORD tmpEnabled;
DWORD returnLen;
BOOL retVal = TRUE;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
return FALSE;
if(!GetTokenInformation(token, TokenVirtualizationEnabled,
&tmpEnabled, sizeof(tmpEnabled), &returnLen)) {
retVal = FALSE;
goto err;
}
*enabled = tmpEnabled;
err:
CloseHandle(token);
return retVal;
}
我尝试使用任务管理器打开和关闭UAC虚拟化,并验证是否返回了正确的结果。可以通过调用enum TOKEN_INFORMATION_CLASS
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
TokenElevationType,
TokenLinkedToken,
TokenElevation,
TokenHasRestrictions,
TokenAccessInformation,
TokenVirtualizationAllowed,
TokenVirtualizationEnabled,
TokenIntegrityLevel,
TokenUIAccess,
TokenMandatoryPolicy,
TokenLogonSid,
MaxTokenInfoClass
}
public const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000;
public const UInt32 STANDARD_RIGHTS_READ = 0x00020000;
public const UInt32 TOKEN_ASSIGN_PRIMARY = 0x0001;
public const UInt32 TOKEN_DUPLICATE = 0x0002;
public const UInt32 TOKEN_IMPERSONATE = 0x0004;
public const UInt32 TOKEN_QUERY = 0x0008;
public const UInt32 TOKEN_QUERY_SOURCE = 0x0010;
public const UInt32 TOKEN_ADJUST_PRIVILEGES = 0x0020;
public const UInt32 TOKEN_ADJUST_GROUPS = 0x0040;
public const UInt32 TOKEN_ADJUST_DEFAULT = 0x0080;
public const UInt32 TOKEN_ADJUST_SESSIONID = 0x0100;
public const UInt32 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
public const UInt32 TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY |
TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE |
TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT |
TOKEN_ADJUST_SESSIONID);
[DllImport("advapi32.dll", SetLastError=true)]
static extern bool GetTokenInformation(
IntPtr TokenHandle,
TOKEN_INFORMATION_CLASS TokenInformationClass,
IntPtr TokenInformation,
int TokenInformationLength,
out uint ReturnLength);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass,
ref uint TokenInformation, uint TokenInformationLength);
[DllImport("advapi32.dll", SetLastError=true)]
static extern bool OpenProcessToken(IntPtr ProcessHandle,
uint DesiredAccess, out IntPtr TokenHandle);
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool CloseHandle(IntPtr hObject);
static bool TryGetVirtualizationEnabled(out bool enabled) {
IntPtr processHandle = Process.GetCurrentProcess().Handle;
IntPtr token;
uint returnLen;
object tmpEnabled = new uint();
enabled = false;
GCHandle handle = GCHandle.Alloc(tmpEnabled, GCHandleType.Pinned);
try {
if(!OpenProcessToken(processHandle, TOKEN_QUERY, out token))
return false;
try {
if(!GetTokenInformation(token, TOKEN_INFORMATION_CLASS.TokenVirtualizationEnabled,
handle.AddrOfPinnedObject(), Marshal.SizeOf(typeof(uint)), out returnLen))
return false;
enabled = (uint)tmpEnabled != 0;
} finally {
CloseHandle(token);
}
} finally {
handle.Free();
}
return true;
}
来启用和禁用虚拟化。
Microsoft表示,他们计划在将来的Windows版本中删除UAC虚拟化,并使程序不依赖于它。我看到有人建议制作一个单独的程序,该程序不知道UAC会将文件从VirtualStore移到AppData,但我不知道这是否是一个好的解决方案。
FWIW,这是Delphi中检测代码的版本:
如果您正在从本地应用程序数据路径“运行”,则您尝试标记的声音,在这种情况下: