如何检测到Windows VirtualStore的文件重定向?

自Win Vista发布以来,微软为以32位进程运行的传统应用程序引入了文件虚拟化。 作为Microsoft用户帐户控制(UAC)的一部分发布,尝试写入受操作系统保护的任何位置的任何遗留应用程序都将重定向到VirtualStore。

此时,已采取措施确保有问题的应用程序现在作为UAC感知的64位进程运行,但是,这对解决将用户数据迁移到被认为是安全的虚拟化位置的问题几乎没有作用。

在解决此问题时,我发现在处理多个用户帐户时,在C:\ Program Files(x86)\ MyApp \ Data的旧版路径中进行了一些更改,同时,已对VirtualStore位于%localappdata%\ VirtualStore \ Programs \ MyApp \ Data。 问题是,我如何检测是否正在进行任何文件/文件夹虚拟化以及如何合并这两个位置?

编辑:我发现几个网站详细说明了问题以及如何复制它,但没有任何内容包括修复它的方法。 我确实找到了这个参考文件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

http://download.microsoftvirtuallabs.com/download/8/a/7/8a71365b-4c80-4e60-8185-8f12f59bf1d4/UACDataRedirection.pdf

并不容易,但我发现了如何检测是否启用了UAC虚拟化。 调用GetTokenInformation()并传入TokenVirtualizationEnabled作为信息类将返回是否启用了文件和注册表虚拟化。 这是一个C函数:

 // 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; } 

使用P / Invoke有点困难,但在这里,包括P / Invoke标题:

 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; } 

我尝试使用任务管理器打开和关闭UAC虚拟化,并validation返回了正确的结果。 可以通过调用SetTokenInformation()来启用和禁用虚拟化。

微软表示他们计划在未来的Windows版本中删除UAC虚拟化,并且计划不依赖于现有版本。 我看到有人建议制作一个不是UAC意识的单独程序将文件从VirtualStore移动到AppData,但我不知道这是不是一个好的解决方案。

FWIW,这是Delphi中检测代码的一个版本:

 unit checkvirtual; interface uses windows; function GetVirtualizationEnabled(var enabled:Boolean):Boolean; implementation // Gets whether the current process has UAC virtualization enabled. // Returns TRUE on success and FALSE on failure. function GetVirtualizationEnabled(var enabled:Boolean):Boolean; var token:THandle; tmpEnabled:DWORD; returnLen:DWORD; begin result:=false; enabled:=false; if not(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, token)) then exit; try if not(GetTokenInformation(token, TokenVirtualizationEnabled, @tmpEnabled, sizeof(tmpEnabled), returnLen)) then exit; enabled:=tmpEnabled<>0; result:=true; finally CloseHandle(token); end; end; end. 

听起来你正在试图标记你是否正在从本地应用程序数据路径“运行”,在这种情况下:

 var currentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); Console.WriteLine( "Current path is:{0}, AppData Path is {1}, Current is subpath of appdata path:{2}", currentPath, appDataPath, currentPath.StartsWith(appDataPath) );