Identificatore di generazione della macchina virtuale

Windows 8 e Windows Server 2012 introducono la possibilità per il software in esecuzione in una macchina virtuale di rilevare che si è verificato un evento di spostamento temporale. Gli eventi di spostamento temporale possono verificarsi in seguito a un'applicazione di uno snapshot di macchina virtuale o al ripristino di un backup di una macchina virtuale. Per altre informazioni su questa funzionalità, vedere il white paper Virtual Machine Generation ID (ID generazione macchina virtuale).

Prerequisiti

Per usare l'identificatore di generazione della macchina virtuale dall'interno di una macchina virtuale, la macchina virtuale deve essere conforme alle condizioni seguenti.

  • La macchina virtuale deve essere in esecuzione in un hypervisor che implementa il supporto per gli identificatori di generazione di macchine virtuali. Attualmente, questi sono i seguenti:

    • Windows 8
    • Windows Server 2012
    • Microsoft Hyper-V Server 2012
  • La macchina virtuale deve eseguire un sistema operativo guest con supporto per l'identificatore di generazione della macchina virtuale. Attualmente, questi sono i seguenti.

    I sistemi operativi seguenti supportano il supporto nativo per l'identificatore di generazione della macchina virtuale.

    • Windows 8
    • Windows Server 2012

    Il sistema operativo seguente può essere usato come sistema operativo guest se vengono installati i servizi di integrazione Hyper-V da Windows 8 o Windows Server 2012.

    • Windows Server 2008 R2 con Service Pack 1 (SP1)
    • Windows 7 con Service Pack 1 (SP1):
    • Windows Server 2008 con Service Pack 2 (SP2)
    • Windows Server 2003 R2
    • Windows Server 2003 con Service Pack 2 (SP2)
    • Windows Vista con Service Pack 2 (SP2)
    • Windows XP con Service Pack 3 (SP3)

Recupero dell'identificatore di generazione della macchina virtuale

Per ottenere l'identificatore di generazione della macchina virtuale a livello di codice, seguire questa procedura.

Nota

Per funzionare correttamente, è necessario eseguire quanto segue con privilegi di amministratore o di sistema.

 

  1. Includere il file di intestazione "vmgenerationcounter.h" nell'app. Il file di intestazione contiene queste definizioni:

    DEFINE_GUID(
        GUID_DEVINTERFACE_VM_GENCOUNTER,
        0x3ff2c92b, 
        0x6598, 
        0x4e60, 
        0x8e, 
        0x1c, 
        0x0c, 
        0xcf, 
        0x49, 
        0x27, 
        0xe3, 
        0x19);
    
    #define VM_GENCOUNTER_SYMBOLIC_LINK_NAME L"VmGenerationCounter"
    
    #define IOCTL_VMGENCOUNTER_READ CTL_CODE( \
        FILE_DEVICE_ACPI, \
        0x1, METHOD_BUFFERED, \
        FILE_READ_ACCESS | FILE_WRITE_ACCESS)
    
    typedef struct _VM_GENCOUNTER
    {
        ULONGLONG GenerationCount;
        ULONGLONG GenerationCountHigh;
    } VM_GENCOUNTER, *PVM_GENCOUNTER;
    
  2. Aprire un handle per il dispositivo "\.\VmGenerationCounter" usando la funzione CreateFile . In alternativa, è possibile usare la gestione PnP per utilizzare l'interfaccia del dispositivo GUID_DEVINTERFACE_VM_GENCOUNTER ({3ff2c92b-6598-4e60-8e1c-0ccf4927e319}). Questi oggetti non saranno presenti se l'app non è in esecuzione in una macchina virtuale.

  3. Inviare il IOCTL_VMGENCOUNTER_READ IOCTL al driver per recuperare l'identificatore di generazione.

    Il IOCTL_VMGENCOUNTER_READ IOCTL opera in una delle due modalità, il polling e l'evento basati su eventi.

    Per eseguire L'IOCTL in modalità polling, inviare IOCTL con un buffer di input di lunghezza zero. In risposta a questo problema, il driver recupera l'identificatore di generazione corrente, lo scrive nel buffer di output e completa L'IOCTL.

    Per eseguire L'IOCTL in modalità guidata dagli eventi, inviare IOCTL con un buffer di input che contiene un identificatore di generazione esistente. In risposta a questo problema, il driver attende fino a quando l'identificatore di generazione corrente diventa diverso dall'identificatore di generazione passato. Quando l'identificatore di generazione viene modificato, il driver scrive l'identificatore di generazione corrente nel buffer di output e completa IOCTL.

    In entrambe le modalità, il formato e la lunghezza del buffer di output sono definiti dalla struttura VM_GENCOUNTER .

    La modalità di polling è supportata in tutti i sistemi operativi guest elencati in precedenza. La modalità guidata dagli eventi è supportata solo in Windows Vista con SP2, Windows Server 2008 con SP2 e sistemi operativi successivi. Nei sistemi operativi precedenti, L'IOCTL avrà esito negativo con il codice di errore ERROR_NOT_SUPPORTED quando rilasciato in modalità guidata dagli eventi.

    È possibile che l'identificatore di generazione cambi tra il momento in cui viene recuperato dal driver e l'ora di completamento dell'IOCTL. Ciò può comportare la ricezione di dati non aggiornati nell'app client. Per evitare questo problema, l'app client può usare la modalità guidata dagli eventi per assicurarsi che alla fine apprenderà eventuali aggiornamenti all'identificatore di generazione. Prendendo l'identificatore corrente dell'app client come input, la modalità guidata dagli eventi evita potenziali race condition che causerebbero la mancata notifica del chiamante.

Nell'esempio di codice seguente viene illustrato come eseguire le azioni precedenti per ottenere l'identificatore di generazione della macchina virtuale.

Nota

Il codice seguente deve essere eseguito con privilegi di amministratore o di sistema per funzionare correttamente.

 

HRESULT GetVmCounter(bool fWaitForChange)
{
    BOOL success = FALSE;
    DWORD error = ERROR_SUCCESS;
    VM_GENCOUNTER vmCounterOutput = {0};
    DWORD size = 0;
    HANDLE handle = INVALID_HANDLE_VALUE;
    HRESULT hr = S_OK;

    handle = CreateFile(
        L"\\\\.\\" VM_GENCOUNTER_SYMBOLIC_LINK_NAME,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        0,
        NULL);

    if (handle == INVALID_HANDLE_VALUE)
    {
        error = GetLastError();

        wprintf(
            L"Unable to open device %s. Error code = %d.", 
            VM_GENCOUNTER_SYMBOLIC_LINK_NAME, 
            error);

        hr = HRESULT_FROM_WIN32(error);

        goto Cleanup;
    }

    /*
    Call into the driver. 

    Because the 4th parameter to DeviceIoControl (nInBufferSize) is zero, this 
    is a polling request rather than an event-driven request.
    */
    success = DeviceIoControl(
        handle,
        IOCTL_VMGENCOUNTER_READ,
        NULL,
        0,
        &vmCounterOutput,
        sizeof(vmCounterOutput),
        &size,
        NULL);

    if (!success)
    {
        error = GetLastError();

        wprintf(L"Call IOCTL_VMGENCOUNTER_READ failed with %d.", error);

        hr = HRESULT_FROM_WIN32(error);

        goto Cleanup;
    }

    wprintf(
        L"VmCounterValue: %I64x:%I64x",
        vmCounterOutput.GenerationCount,
        vmCounterOutput.GenerationCountHigh);

    if (fWaitForChange)
    {
        /*
        Call into the driver again in event-driven mode. DeviceIoControl won't 
        return until the generation identifier has changed.
        */
        success = DeviceIoControl(
            handle,
            IOCTL_VMGENCOUNTER_READ,
            &vmCounterOutput,
            sizeof(vmCounterOutput),
            &vmCounterOutput,
            sizeof(vmCounterOutput),
            &size,
            NULL);

        if (!success)
        {
            error = GetLastError();

            wprintf(L"Call IOCTL_VMGENCOUNTER_READ failed with %d.", error);

            hr = HRESULT_FROM_WIN32(error);

            goto Cleanup;
        }

        wprintf(
            L"VmCounterValue changed to: %I64x:%I64x",
            vmCounterOutput.GenerationCount,
            vmCounterOutput.GenerationCountHigh);
    }

Cleanup:

    if (handle != INVALID_HANDLE_VALUE)
    {
        CloseHandle(handle);
    }

    return hr;
};

Determinare se si è verificato un evento di spostamento temporale

Dopo aver ottenuto l'identificatore di generazione della macchina virtuale, è necessario archiviarlo per un uso futuro. Prima che l'app esegua un'operazione sensibile al tempo, ad esempio il commit in un database, è necessario ottenere nuovamente l'identificatore di generazione e confrontarlo con il valore archiviato. Se l'identificatore è stato modificato, significa che si è verificato un evento di spostamento temporale e che l'app deve agire in modo appropriato.