Windows 8 および Windows Server 2012 では、仮想マシン上で実行されているソフトウェアがタイム シフト イベントが発生したことを検出する機能が導入されています。 タイム シフト イベントは、仮想マシン スナップショットのアプリケーションまたは仮想マシンのバックアップの復元の結果として発生する可能性があります。 この機能の詳細については、仮想マシン生成 ID に関するホワイト ペーパーを参照してください。
前提 条件
仮想マシン内から仮想マシン生成識別子を使用するには、仮想マシンが次に準拠している必要があります。
仮想マシンは、仮想マシン生成識別子のサポートを実装するハイパーバイザーで実行されている必要があります。 現在、これらは次のとおりです。
- Windows 8
- Windows Server 2012
- Microsoft Hyper-V Server 2012
仮想マシンは、仮想マシン生成識別子をサポートするゲスト オペレーティング システムを実行している必要があります。 現在、これらは次のとおりです。
次のオペレーティング システムでは、仮想マシン生成識別子がネイティブにサポートされています。
- Windows 8
- Windows Server 2012
Windows 8 または Windows Server 2012 の Hyper-V 統合サービスがインストールされている場合は、次のオペレーティング をゲスト オペレーティング システムとして使用できます。
- Windows Server 2008 R2 Service Pack 1 (SP1)
- Windows 7 Service Pack 1 (SP1)
- Windows Server 2008 Service Pack 2 (SP2)
- Windows Server 2003 R2
- Windows Server 2003 Service Pack 2 (SP2)
- Windows Vista Service Pack 2 (SP2)
- Windows XP Service Pack 3 (SP3)
仮想マシン生成識別子の取得
仮想マシン生成識別子をプログラムで取得するには、次の手順を実行します。
手記
正しく機能するには、管理者特権またはシステム特権を使用して以下を実行する必要があります。
アプリにヘッダー ファイル "vmgenerationcounter.h" を含めます。 ヘッダー ファイルには、次の定義が含まれています。
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;
CreateFile 関数を使用して、"\\.\VmGenerationCounter" デバイスへのハンドルを開きます。 または、PnP マネージャーを使用して、デバイス インターフェイス GUID_DEVINTERFACE_VM_GENCOUNTER ({3ff2c92b-6598-4e60-8e1c-0ccf4927e319}) を使用することもできます。 アプリが仮想マシンで実行されていない場合、これらのオブジェクトは存在しません。
IOCTL_VMGENCOUNTER_READ IOCTL をドライバーに送信して、世代識別子を取得します。
IOCTL_VMGENCOUNTER_READ IOCTL は、ポーリング 、およびイベント ドリブン 2 つのモードのいずれかで動作します。
ポーリング モードで IOCTL を発行するには、入力バッファーの長さが 0 の IOCTL を送信します。 これに応じて、ドライバーは現在の生成識別子を取得し、出力バッファーに書き込み、IOCTL を完了します。
イベント ドリブン モードで IOCTL を発行するには、既存の世代識別子を含む入力バッファーを使用して IOCTL を送信します。 これに対して、ドライバーは、現在の世代識別子が渡された世代識別子と異なるまで待機します。 生成識別子が変更されると、ドライバーは現在の世代識別子を出力バッファーに書き込み、IOCTL を完了します。
どちらのモードでも、出力バッファーの形式と長さは、VM_GENCOUNTER 構造体によって決まります。
ポーリング モードは、上記のすべてのゲスト オペレーティング システムでサポートされています。 イベント ドリブン モードは、WINDOWS Vista SP2、Windows Server 2008 SP2 以降のオペレーティング システムでのみサポートされます。 以前のオペレーティング システムでは、イベント ドリブン モードで発行された場合、IOCTL はエラー コード ERROR_NOT_SUPPORTED で失敗します。
ドライバーによって取得された時刻と IOCTL が完了するまでの間に、生成識別子が変更される可能性があります。 これにより、クライアント アプリが古いデータを受信する可能性があります。 これを回避するために、クライアント アプリはイベント ドリブン モードを使用して、生成識別子の更新について最終的に学習できるようにします。 クライアント アプリの現在の識別子を入力として受け取ることで、イベント ドリブン モードでは、呼び出し元が通知を見逃す可能性のある競合状態を回避できます。
次のコード例は、上記のアクションを実行して仮想マシン生成識別子を取得する方法を示しています。
手記
次のコードは、正しく機能するために管理者またはシステム特権で実行する必要があります。
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;
};
タイム シフト イベントが発生したかどうかを判断する
仮想マシン生成識別子を取得したら、後で使用するためにそれを格納する必要があります。 データベースへのコミットなど、時間の影響を受けやすい操作をアプリで実行する前に、生成識別子を再取得し、格納されている値と比較する必要があります。 識別子が変更された場合は、タイム シフト イベントが発生し、アプリが適切に動作する必要があることを意味します。