2012-12-22 13 views
9

Dal rilascio di Win Vista, Microsoft ha introdotto la virtualizzazione dei file per le applicazioni legacy in esecuzione come processi a 32 bit. Rilasciato come parte del Controllo dell'account utente (UAC) di Microsoft, tutte le applicazioni legacy che tentano di scrivere in posizioni considerate protette dal sistema operativo vengono reindirizzate al VirtualStore.Come rilevare il reindirizzamento dei file su Windows VirtualStore?

A questo punto, sono stati presi provvedimenti per garantire che l'applicazione in questione venga ora eseguita come un processo a 64 bit che è in grado di riconoscere l'UAC, tuttavia, questo non consente di risolvere il problema della migrazione dei dati degli utenti in una posizione considerata sicuro dalla virtualizzazione.

Durante la risoluzione di questo problema, ho riscontrato che quando si gestiscono più account utente sono state apportate alcune modifiche all'interno del percorso legacy che si trova in C: \ Programmi (x86) \ MyApp \ Data mentre, allo stesso tempo, si modifica sono stati apportati al VirtualStore situato in% localappdata% \ VirtualStore \ Programs \ MyApp \ Data. Essendo la domanda, come posso rilevare se si verifica una virtualizzazione di file/cartelle e come posso unire le due posizioni?

EDIT: Ho trovato diversi siti Web che descrivono il problema e come duplicarlo, ma nulla che includa un modo per risolverlo. Ho trovato questo riferimento FILE_ATTRIBUTE_VIRTUAL che definisce un attributo di file che sembra promettente - Ho trovato un altro riferimento da qualche parte, anche se non riesco a ricordare dove, che afferma che questo è l'attributo utilizzato da Windows per indicare che la virtualizzazione dei file è in atto e contrassegna il richiesta di reindirizzamento.

Questi link descrivono il problema:

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

risposta

14

Non è stato facile, ma ho scoperto come rilevare se la virtualizzazione del controllo dell'account utente è abilitata. Chiamando GetTokenInformation() e passando TokenVirtualizationEnabled come classe di informazioni restituirà se la virtualizzazione di file e registro è abilitata. Ecco una funzione C per farlo:

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

Un po 'più difficile con P/Invoke, ma qui è, tra cui il P/Invoke intestazioni:

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

Ho provato a girare la virtualizzazione UAC e spento con Task Manager e verificato che venga restituito il risultato corretto. L'abilitazione e disabilitazione della virtualizzazione può essere effettuata chiamando SetTokenInformation().

Microsoft dichiara di aver intenzione di rimuovere la virtualizzazione UAC in una versione futura di Windows e che i programmi non si basino su di essa esistenti. Ho visto un suggerimento da parte di qualcuno di creare un programma separato che non è in grado di controllare i file dal VirtualStore ad AppData, ma non so se sia una buona soluzione o meno.

+0

Questo metodo rileva se la virtualizzazione è abilitata in generale o se è abilitata per uno specifico file/cartella? – Simpleton

+0

In generale. Se la virtualizzazione è abilitata in un processo, tutte le scritture da quel processo nella cartella Programmi, la cartella Windows e HKEY_LOCAL_MACHINE nel registro vengono reindirizzati (e un altro paio). Non può essere abilitato per alcune di queste cartelle e non per altre. –

+2

Rileggendo la tua domanda iniziale, sembra che tu non voglia scoprire se la virtualizzazione è in atto. Se si sta eseguendo un processo a 64 bit con conoscenza del controllo dell'account utente, la virtualizzazione è disabilitata. Per accedere ai dati inseriti nel VirtualStore da una vecchia versione del programma (che aveva la virtualizzazione abilitata), il programma può cercare in% LOCALAPPDATA% \ VirtualStore per vedere se ci sono dei suoi dati lì dentro. Se esiste, può spostare i dati nella posizione corretta, unendola se necessario. –

1

Suona come si sta cercando di bandiera se si sta "eseguendo" dal percorso dei dati app locali, nel qual caso:

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) 
); 
+0

Grazie per la risposta!Sto cercando una soluzione per non essere solo consapevole del controllo dell'account utente, ma per rilevare attivamente se il reindirizzamento dei file o la virtualizzazione è in atto. Se possibile, mi piacerebbe essere in grado di rilevare questo tempo il processo è attualmente in esecuzione o meno - dal momento che si tratta di un'applicazione basata su rete. A questo punto, non sono stato in grado di trovare alcun metodo per identificare se il reindirizzamento di file al VirtualStore è o è mai avvenuto; a corto di ricerca attraverso ogni profilo di utenti per i dati/file di VirtualStore, non riesco a pensare a nessun altro modo di identificare i dati di VirtualStore. – Simpleton

+0

@Simpleton Ah, capisco - mi dispiace, non sono intimamente consapevole dei dettagli del VirtualStore abbastanza da dare quella risposta particolare; questo blurb ti farebbe sapere una volta che l'app è stata avviata. – JerKimball

3

FWIW, ecco un versione del codice di rilevamento a Delfi:

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. 
Problemi correlati