ドライバが Windows 2000 に読み込まれない場合、VER_SET_CONDITION が原因かもしれません

Microsoft Windows XP およびそれ以降のバージョンの Windows では、ドライバは、RtlVerifyVersionInfo を呼び出して、オペレーティング システムのバージョン要件の指定されたセットを、現在実行中のバージョンのオペレーティング システムの対応する属性と比較できます。VER_SET_CONDITION マクロは、バージョン要件を比較する方法を指定する条件マスクの作成に使用されます。条件マスクは、RtlVerifyVersionInfo への入力パラメータとして使用されます。

残念なことに、Windows XP およびそれ以降のバージョン用に構築されたドライバを Windows 2000 システムに読み込もうとした場合、VER_SET_CONDITION により、未解決のインポートが生じます。なぜでしょうか?それは、Ntddk.h で定義された VER_SET_CONDITION マクロが、VerSetConditionMask 関数を呼び出し、この関数が Windows 2000 上で Ntoskrnl.exe によって公開されていないからです。

ドライバが Windows XP またはそれ以降のバージョン用の Ntoskrnl.lib と共にビルドされた場合、リンカは、この未解決のインポートをリンク時に見つけることができません。なぜなら、VerSetConditionMask は、ライブラリのそのバージョンによってエクスポートされるからです。代わりに、ドライバが Windows 2000 を実行しているシステムに読み込まれ、ドライバによって呼び出されたインポートをメモリ マネージャが解決しようとすると、メモリ マネージャは、Ntoskrnl.exe で VerSetConditionMask を見つけられません。ドライバ イメージは読み込みに Fail し、デバイス マネージャは、エラー コード 31 (CM_PROB_FAILED_ADD) を報告します。

どうすればよいでしょうか。

Windows 2000、Windows XP、およびそれ以降のバージョンの Windows で実行する単一のドライバ バイナリを構築しており、自分のドライバで RtlVerifyVersionInfo を使用する場合、VER_SET_CONDITION によって生じる未解決のインポートを避ける方法は次のとおりです。

MmGetSystemRoutineAddress を使用して、現在実行中のバージョンの Windows で RtlVerifyVersionInfo 関数が利用可能かどうか判定します。

RtlVerifyVersionInfo が利用可能でない場合、Windows 2000 で実行中と仮定し、PsGetVersion を使用して、OS の現在のバージョンに関する情報を入手します。(Windows XP およびそれ以降のバージョンの Windows では、PsGetVersion が削除されていることに注意してください。)

下記のコード サンプルは、MmGetSystemRoutineAddress を使用して RtlVerifyVersionInfo の利用可能性をテストする方法を示します。

typedef
ULONGLONG
(*PFN_VER_SET_CONDITION_MASK)(
		IN  ULONGLONG   ConditionMask,
		IN  ULONG   TypeMask,
		IN  UCHAR   Condition
		);

typedef
NTSTATUS
(*PFN_RTL_VERIFY_VERSION_INFO)(
		IN PRTL_OSVERSIONINFOEXW VersionInfo,
		IN ULONG TypeMask,
		IN ULONGLONG  ConditionMask
		);

VOID
MyDriverVerifyVersion(
		VOID
		)
{
		UNICODE_STRING funcName;
		RTL_OSVERSIONINFOEXW info;
		PFN_RTL_VERIFY_VERSION_INFO pRtlVerifyVersionInfo;
		PFN_VER_SET_CONDITION_MASK pVerSetConditionMask;
		ULONGLONG condition;
		NTSTATUS status;

		RtlInitUnicodeString(&funcName, L"RtlVerifyVersionInfo");
		pRtlVerifyVersionInfo = (PFN_RTL_VERIFY_VERSION_INFO)
				MmGetSystemRoutineAddress(&funcName);

		if (pRtlVerifyVersionInfo == NULL) {
			// We are on Win2K-- guaranteed not to be XP or later.
			// Do win2k specific checks here
			// To check the specific SP number, either parse the
			// CSDVersion returned by PsGetVersion or use a user mode
			// application to report the service pack number to 
			// the driver. See PsGetVersion in the DDK documentation 
			// for more information.

			return; 
		}

		// If RtlVerifyVersionInfo is present, 
		// assume VerSetConditionMask is present as well
		RtlInitUnicodeString(&funcName, L"VerSetConditionMask");
		pVerSetConditionMask = (PFN_VER_SET_CONDITION_MASK)
				MmGetSystemRoutineAddress(&funcName);

		// This code checks for XP Gold or XP SP1

		// Check for XP
		condition = 0;
		RtlZeroMemory(&info, sizeof(info));
		info.dwOSVersionInfoSize = sizeof(info);

		info.dwMajorVersion = 5;
		info.dwMinorVersion = 1;
		condition = pVerSetConditionMask(condition, VER_MAJORVERSION, VER_EQUAL);
		condition = pVerSetConditionMask(condition, VER_MINORVERSION, VER_EQUAL);

		// Check for XP gold and SP1 by checking for less than SP2
		info.wServicePackMajor = 2;
		condition = pVerSetConditionMask(condition, VER_SERVICEPACKMAJOR, VER_LESS);

		status = pRtlVerifyVersionInfo(&info,
							VER_MAJORVERSION |
							VER_MINORVERSION |
							VER_SERVICEPACKMAJOR,
							condition);

	if (NT_SUCCESS(status)) {
		// this is XP Gold or SP1
	}
	else {
		// this is XP SP2 or later OR
		// this is another OS released after XP 
		// such as Windows Server 2003
	}

詳細情報 :

Windows DDK
RtlVerifyVersionInfo
MmGetSystemRoutineAddress
PSGetVersion



この情報はお役に立ちましたか?