驱动程序在 Windows 2000 上不加载?VER_SET_CONDITION 可能是罪魁祸首
在 Microsoft Windows XP 和更高版本的 Windows 上,驱动程序可以调用 RtlVerifyVersionInfo 将指定的一系列操作系统版本要求与操作系统当前运行的版本的相应属性进行比较。VER_SET_CONDITION 宏被用来创建一个指定如何比较版本需求的条件掩码。条件掩码被用作 RtlVerifyVersionInfo 的一个输入参数。
不幸的是,VER_SET_CONDITION 会导致试图在 Windows 2000 系统上加载为 Windows XP 和更高版本的 Windows 构建的驱动程序时产生未解析的导入。为什么?因为 Ntddk.h 中定义的 VER_SET_CONDITION 宏调用 VerSetConditionMask 函数,在 Windows 2000 上这个函数未被 Ntoskrnl.exe 公开。
如果驱动程序使用 Windows XP 或更高版本的 Ntoskrnl.lib 构建,那么链接程序无法在链接时找到这个未解析的导入,因为 VerSetConditionMask 被该版本的库导出。相反,当驱动程序被加载到运行 Windows 2000 的系统上并且内存管理器试图解析驱动程序调用的导入时,它将无法在 Ntoskrnl.exe 中找到 VerSetConditionMask。驱动程序映像加载失败并且设备管理器报告错误码 31 (CM_PROB_FAILED_ADD)。
您应该做什么?
如果您正在构建要在 Windows 2000、Windows XP 和更高版本的 Windows 上运行的单个驱动程序,并且希望在您的驱动程序中使用 RtlVerifyVersionInfo,那么可通过以下方法避免由 VER_SET_CONDITION 导致的未解析导入:
| • | 使用 MmGetSystemRoutineAddress 来确定 RtlVerifyVersionInfo 函数在当前运行的 Windows 版本上是否可用。 |
| • | 如果 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