Identificador de generación de máquinas virtuales

Windows 8 y Windows Server 2012 introducen la capacidad de software que se ejecuta en una máquina virtual para detectar que se puede haber producido un evento de cambio de hora. Los eventos de cambio de tiempo pueden producirse como resultado de una aplicación de una instantánea de máquina virtual o la restauración de una copia de seguridad de máquina virtual. Para obtener más información sobre esta funcionalidad, consulte las notas del producto Id. de generación de máquinas virtuales.

Requisitos previos

Para usar el identificador de generación de máquinas virtuales desde dentro de una máquina virtual, la máquina virtual debe cumplir lo siguiente.

  • La máquina virtual debe ejecutarse en un hipervisor que implemente compatibilidad con identificadores de generación de máquinas virtuales. Actualmente, estos son los siguientes:

    • Windows 8
    • Windows Server 2012
    • Microsoft Hyper-V Server 2012
  • La máquina virtual debe ejecutar un sistema operativo invitado que tenga compatibilidad con el identificador de generación de máquinas virtuales. Actualmente, estos son los siguientes.

    Los siguientes sistemas operativos tienen compatibilidad nativa con el identificador de generación de máquinas virtuales.

    • Windows 8
    • Windows Server 2012

    El siguiente funcionamiento se puede usar como sistema operativo invitado si se instalan los servicios de integración de Hyper-V desde 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)

Obtención del identificador de generación de máquinas virtuales

Para obtener mediante programación el identificador de generación de máquinas virtuales, realice los pasos siguientes.

Nota

Lo siguiente debe ejecutarse con privilegios de administrador o sistema para funcionar correctamente.

 

  1. Incluya el archivo de encabezado "vmgenerationcounter.h" en la aplicación. El archivo de encabezado contiene estas definiciones:

    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. Abra un identificador para el dispositivo "\\.\VmGenerationCounter" mediante la función CreateFile . Como alternativa, puede usar el administrador de PnP para consumir la interfaz de dispositivo GUID_DEVINTERFACE_VM_GENCOUNTER ({3ff2c92b-6598-4e60-8e1c-0ccf4927e319}). Estos objetos no estarán presentes si la aplicación no se ejecuta en una máquina virtual.

  3. Envíe el IOCTL_VMGENCOUNTER_READ IOCTL al controlador para recuperar el identificador de generación.

    El IOCTL_VMGENCOUNTER_READ IOCTL funciona en uno de los dos modos, el sondeo y el control de eventos.

    Para emitir el IOCTL en modo de sondeo, envíe el IOCTL con un búfer de entrada de longitud cero. En respuesta a esto, el controlador recupera el identificador de generación actual, lo escribe en el búfer de salida y completa el IOCTL.

    Para emitir el IOCTL en modo controlado por eventos, envíe el IOCTL con un búfer de entrada que contenga un identificador de generación existente. En respuesta a esto, el controlador espera hasta que el identificador de generación actual sea diferente del identificador de generación que se pasó. Cuando cambia el identificador de generación, el controlador escribe el identificador de generación actual en el búfer de salida y completa el IOCTL.

    En ambos modos, la estructura de VM_GENCOUNTER dicta el formato y la longitud del búfer de salida.

    El modo de sondeo se admite en todos los sistemas operativos invitados enumerados anteriormente. El modo controlado por eventos solo se admite en Windows Vista con SP2, Windows Server 2008 con SP2 y sistemas operativos posteriores. En sistemas operativos anteriores, el IOCTL producirá un error con el código de error ERROR_NOT_SUPPORTED cuando se emita en modo controlado por eventos.

    Es posible que el identificador de generación cambie entre el momento en que el controlador recupera y la hora en que se completa el IOCTL. Esto puede dar lugar a que la aplicación cliente reciba datos obsoletos. Para evitar esto, la aplicación cliente puede usar el modo controlado por eventos para asegurarse de que finalmente obtendrá información sobre las actualizaciones del identificador de generación. Al tomar el identificador actual de la aplicación cliente como entrada, el modo controlado por eventos evita posibles condiciones de carrera que harían que el autor de la llamada pierda las notificaciones.

En el ejemplo de código siguiente se muestra cómo realizar las acciones anteriores para obtener el identificador de generación de máquinas virtuales.

Nota

El código siguiente debe ejecutarse con privilegios de administrador o sistema para funcionar correctamente.

 

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

Determinar si se ha producido un evento de turno de tiempo

Después de obtener el identificador de generación de máquinas virtuales, debe almacenarlo para su uso futuro. Antes de que la aplicación realice una operación que tenga en cuenta el tiempo, como confirmar en una base de datos, debe volver a obtener el identificador de generación y compararlo con el valor almacenado. Si el identificador ha cambiado, significa que se ha producido un evento de cambio de hora y la aplicación debe actuar correctamente.