创建驱动程序项目时,请指定最低目标操作系统,该操作系统是运行驱动程序的 Windows 的最低版本。 例如,可以指定 Windows 7 为最低目标操作系统。 在这种情况下,驱动程序会在 Windows 7 和更高版本的 Windows 上运行。

如果为特定的最低版本的 Windows 开发驱动程序且希望驱动程序运行在更高版本的 Windows 上,则不得使用任何未记录的函数,也不得以文档中介绍的方式之外的任何其他方式使用记录的函数。 否则,驱动程序可能会无法运行在更高版本的 Windows 上。 即使你已经很小心地仅使用记录的函数,也应在每次发布新版本的 Windows 时在其上对驱动程序进行测试。

编写仅使用共同功能的多版本驱动程序

设计将在多个版本的 Windows 上运行的驱动程序时,最简单的方法是允许驱动程序仅使用运行该驱动程序的所有版本的 Windows 共同的 DDI 函数和结构。 在此情形下,将最低目标操作系统设置为驱动程序将支持的最低 Windows 版本。

例如,若要支持从 Windows 7 开始的所有版本的 Windows,应执行以下操作:

  • 设计并实现驱动程序,以便该驱动程序仅使用 Windows 7 中提供的那些功能。

  • 生成驱动程序时,将 Windows 7 指定为最低目标操作系统。

    虽然此过程很简单,但它可能会限制驱动程序仅使用更高版本的 Windows 上提供的功能子集。 在许多情况下,你会希望在更新的操作系统功能可用时使用它,以提高安全性、提高可靠性或启用更新的功能。

    编写使用版本相关功能的多版本驱动程序

    内核模式驱动程序可以动态确定操作系统提供的 API 是否可用,或者驱动程序正在运行于哪个版本的Windows 上,并选择使用该运行时环境中可用的功能。 例如,必须支持从 Windows 7 开始的所有 Windows 版本的驱动程序可以在运行时确定它在哪个 Windows 版本上运行。 如果驱动程序在 Windows 7 上运行,则它只能使用 Windows 7 支持的 DDI 函数。 但是,同一个驱动程序可以使用 Windows 8 独有的其他 DDI 函数,例如,当其运行时检查确定这些 API 当前存在,或者确定它在 Windows 8 上运行时。

    建议尽可能检查功能或 API 可用性,而不是尝试检查驱动程序是否在特定操作系统版本或更高版本上运行。

    有条件地调用与 Windows 版本相关的函数

    内核模式驱动程序可以使用 MmGetSystemRoutineAddress MmGetSystemRoutineAddressEx 函数动态检查它想要使用的特定 API 是否在当前运行时环境中可用,并获取要用于调用该 API 的函数指针。

    若要帮助保留键入检查和防止出现无意识的错误,应创建映射原始函数类型的 typedef。

    示例:确定 API 可用性并有条件地调用 API

    typedef
    NTSTATUS
    (*PFN_IoOpenDriverRegistryKey)(
        PDRIVER_OBJECT     DriverObject,
        DRIVER_REGKEY_TYPE RegKeyType,
        ACCESS_MASK        DesiredAccess,
        ULONG              Flags,
        PHANDLE            DriverRegKey
    VOID ExampleFunction(VOID) {
        NTSTATUS status = STATUS_UNSUCCESSFUL;
        HANDLE persistentStateKey = NULL;
        PFN_IoOpenDriverRegistryKey pfnIoOpenDriverRegistryKey = NULL;
        UNICODE_STRING functionName = {0};
        RtlInitUnicodeString(&functionName, L"IoOpenDriverRegistryKey");
        pfnIoOpenDriverRegistryKey = (PFN_IoOpenDriverRegistryKey)MmGetSystemRoutineAddress(&functionName);
        if (pfnIoOpenDriverRegistryKey != NULL) {
            // Open a key to where state can be stored under the driver service
            status = pfnIoOpenDriverRegistryKey(g_GlobalStructure.DriverObject,
                                                DriverRegKeyPersistentState,
                                                KEY_WRITE,
                                                &persistentStateKey);
        } else {
            // Fall back to opening up a different location to store state in
        // Use the opened registry key
    

    确定 Windows 版本

    RtlIsNtDdiVersionAvailable 是一个函数,可供驱动程序在运行时用来确定特定版本的 Windows 提供的功能是否可用。 该函数的原型如下所示:

    BOOLEAN RtlIsNtDdiVersionAvailable(IN ULONG Version)
    

    在此原型中,Version 是一个值,指示 Windows DDI 的所需版本。 此值必须为 DDI 版本常量之一,在 sdkddkver.h 中定义,如 NTDDI_WIN8 or NTDDI_WIN7。

    当调用程序运行在与 Version 指定的 Windows 版本相同或更高的 Windows 版本上时,RtlIsNtDdiVersionAvailable 会返回 TRUE。

    驱动程序还可以通过调用 RtlIsServicePackVersionInstalled 函数来检查是否安装了特定的 Service Pack。 该函数的原型如下所示:

    BOOLEAN RtlIsServicePackVersionInstalled(IN ULONG Version)
    

    在此原型中,Version 为一个值,指示所需的 Windows 版本和 Service Pack。 此值必须为 DDI 版本常量之一,在 sdkddkver.h 中定义,如 NTDDI_WS08SP3。

    请注意,仅当操作系统版本与指定版本完全匹配时,RtlIsServicePackVersionInstalled 才会返回 TRUE。 因此,如果驱动程序不是在 Windows Server 2008 SP4 上运行,则将 Version 设置为 NTDDI_WS08SP3 的 RtlIsServicePackVersionInstalled 调用会失败。

    示例:确定 Windows 版本并有条件地调用与版本相关的函数

    此代码示例(来自驱动程序的头文件)将 PAISQSL 类型定义为指向 KeAcquireInStackQueuedSpinLock 函数的指针。 然后,此示例声明该类型的 AcquireInStackQueuedSpinLock 变量。

    // Pointer to the ordered spin lock function. This function is only // available on Windows 7 and later systems typedef (* PAISQSL) (KeAcquireInStackQueuedSpinLock); PAISQSL AcquireInStackQueued = NULL;

    此代码示例(来自驱动程序的初始化代码)确定驱动程序是否在 Windows 7 或更高版本操作系统上运行。 如果是,代码会检索指向 KeAcquireInStackQueuedSpinLock 的指针。

    // Are we running on Windows 7 or later? if (RtlIsNtDdiVersionAvailable(NTDDI_WIN7)) { // Yes... Windows 7 or later it is! RtlInitUnicodeString(&funcName, L"KeAcquireInStackQueuedSpinLock"); // Get a pointer to Windows implementation of KeAcquireInStackQueuedSpinLock // into our variable "AcquireInStackQueued" AcquireInStackQueued = (PAISQSL) MmGetSystemRoutineAddress(&funcName); // Acquire a spin lock. if (NULL != AcquireInStackQueued) { (AcquireInStackQueued)(&SpinLock, &lockHandle); } else { KeAcquireSpinLock(&SpinLock);

    在此示例中,驱动程序调用 RtlIsNtDdiVersionAvailable 以确定驱动程序是否在 Windows 7 或更高版本上运行。 如果版本为 Windows 7 或更高版本,驱动程序将会调用 MmGetSystemRoutineAddress 来获取指向 KeAcquireInStackQueuedSpinLock 函数的指针并将此指针存储在名为 AcquireInStackQueued 的变量(该变量声明为 PAISQSL 类型)中。

    随后,当驱动程序必须获取旋转锁时,它会检查是否收到了指向 KeAcquireInStackQueuedSpinLock 函数的指针。 如果驱动程序已收到此指针,则驱动程序会使用该指针调用 KeAcquireInStackQueuedSpinLock。 如果指向 KeAcquireInStackQueuedSpinLock 的指针为 Null,则驱动程序会使用 KeAcquireSpinLock 来获取旋转锁。

  •