Windows NT 驱动程序的编译、安装、调试

这篇具有很好参考价值的文章主要介绍了Windows NT 驱动程序的编译、安装、调试。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


Windows NT 驱动介绍

Windows 驱动分为两类,一类是从 Windows NT 遗留下来的驱动模型称为传统的 Windows NT 驱动程序模型,另一类是 Windows 添加了电源管理后的 KMDF (WDM)驱动程序。本文这里首先以最简单的 Windows NT 驱动模型为例介绍 Windows 驱动的简单编写、编译、安装及调试。

NT 驱动代码分析

如果有学习过 Linux 驱动的同学,基本上会了解到驱动的编写其实都是内核规定好的 API 需要去声明和定义的,根据一定的驱动模型或者范例来进行编写即可实现自己定义的驱动功能。(可参考 Linux 驱动:Linux驱动开发——(Linux内核GPIO操作库函数)gpio(1))。

在 Linux 驱动中内核规定要添加驱动,需要自己实现驱动的入口函数和退出函数,然后用如下的宏定义进行声明:

module_init(led_init);	//驱动入口函数声明
module_exit(led_exit);	//驱动退出函数声明
MODULE_LICENSE("GPL");	//驱动遵循的开源规则声明

而在 Windows 驱动中也有类似的模块入口函数和模块退出函数。区别是在 Windows 驱动中函数入口都统一由一个固定的函数名称 DriverEntry,你只需要在自己的驱动代码中实现这个固定的函数入口(Windows 内核会在加载驱动后主动调用这个入口函数进行驱动的初始化操作),该函数的具体声明如下:

NTSTATUS DriverEntry(
  _In_ PDRIVER_OBJECT  DriverObject,
  _In_ PUNICODE_STRING RegistryPath
);
  • 参数

    • DriverObject [in]:指向 DRIVER_OBJECT 结构的指针,该结构表示驱动程序的 WDM 驱动程序对象。
    • RegistryPath [in]:指向 UNICODE_STRING 结构的指针,该结构指定注册表中驱动程序 的 Parameters 键 的路径。
  • 返回值
    如果例程成功,则必须返回STATUS_SUCCESS。 否则,它必须返回 ntstatus.h 中定义的错误状态值之一。

Windows NT 模型驱动和 WDM 模型驱动都是由 DriverEntry 入口函数开始加载驱动和完成驱动最初的初始化操作的(这也是 Windows 内核固定会调用的驱动入口函数)。所以我们编写 Windows 驱动其实也是从最初的 DriverEntry 入口函数开始。

这里首先编写一个最简单的 Windows NT 驱动作为模型例程进行讲解,整个驱动源文件有两个文件组成:驱动源文件(helloNt.cpp)和驱动头文件(helloNt.h)。

这里最好参考《Windows 驱动开发环境搭建》这篇文章的内容已经搭建好了 Windows 驱动开发环境,并且使用 VS2019 创建 KMDF 驱动解决方案项目,如下图:

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发

使用 VS2019 创建完成解决方案后会自动创建如下文件,这里由于我们想要创建一个最简单的 Windows NT 驱动,所以删除不需要的文件即可(Device.c、Device.h、Queue.c、Queue.h、helloNt.inf)。

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
删除完成后只保留 Driver.c 和 Driver.h

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发

这里需要注意,将项目属性进行一些设置(平台参数 x64、编译将警告视为错误设置为否、链接将警告视为错误设置为否、取消 WPP trace 运行)

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发

驱动模块具体代码内容如下:

Driver.c

#include "driver.h"


#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, CreateDevice)
#pragma alloc_text (PAGE, HelloDDKDispatchRoutine)
#pragma alloc_text (PAGE, HelloDDKUnload)
#endif

NTSTATUS
DriverEntry(
    _In_ PDRIVER_OBJECT  DriverObject,
    _In_ PUNICODE_STRING RegistryPath
    )
/*++

Routine Description:
    DriverEntry initializes the driver and is the first routine called by the
    system after the driver is loaded. DriverEntry specifies the other entry
    points in the function driver, such as EvtDevice and DriverUnload.

Parameters Description:

    DriverObject - represents the instance of the function driver that is loaded
    into memory. DriverEntry must initialize members of DriverObject before it
    returns to the caller. DriverObject is allocated by the system before the
    driver is loaded, and it is released by the system after the system unloads
    the function driver from memory.

    RegistryPath - represents the driver specific path in the Registry.
    The function driver can use the path to store driver related data between
    reboots. The path does not store hardware instance specific data.

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise.

--*/
{
    NTSTATUS status;
    KdPrint(("Enter DriverEntry\n"));

    //注册其他驱动调用函数入口
    DriverObject->DriverUnload = HelloDDKUnload;
    DriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine;
    DriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;
    DriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine;

    //创建驱动设备对象
    status = CreateDevice(DriverObject);

    KdPrint(("DriverEntry end\n"));
    return status;
}




/************************************************************************
* 函数名称:CreateDevice
* 功能描述:初始化设备对象
* 参数列表:
      pDriverObject:从I/O管理器中传进来的驱动对象
* 返回 值:返回初始化状态
*************************************************************************/
#pragma INITCODE
NTSTATUS CreateDevice(
    IN PDRIVER_OBJECT	pDriverObject)
{
    NTSTATUS status;
    PDEVICE_OBJECT pDevObj;
    PDEVICE_EXTENSION pDevExt;

    //创建设备名称
    UNICODE_STRING devName;
    RtlInitUnicodeString(&devName, L"\\Device\\MyDDKDevice");

    //创建设备
    status = IoCreateDevice(pDriverObject,
        sizeof(DEVICE_EXTENSION),
        &devName,
        FILE_DEVICE_UNKNOWN,
        0, TRUE,
        &pDevObj);
    if (!NT_SUCCESS(status))
        return status;

    pDevObj->Flags |= DO_BUFFERED_IO;
    pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
    pDevExt->pDevice = pDevObj;
    pDevExt->ustrDeviceName = devName;
    //创建符号链接
    UNICODE_STRING symLinkName;
    RtlInitUnicodeString(&symLinkName, L"\\??\\HelloDDK");
    pDevExt->ustrSymLinkName = symLinkName;
    status = IoCreateSymbolicLink(&symLinkName, &devName);
    if (!NT_SUCCESS(status))
    {
        IoDeleteDevice(pDevObj);
        return status;
    }
    return STATUS_SUCCESS;
}

/************************************************************************
* 函数名称:HelloDDKUnload
* 功能描述:负责驱动程序的卸载操作
* 参数列表:
      pDriverObject:驱动对象
* 返回 值:返回状态
*************************************************************************/
#pragma PAGEDCODE
VOID HelloDDKUnload(IN PDRIVER_OBJECT pDriverObject)
{
    PDEVICE_OBJECT	pNextObj;
    KdPrint(("Enter DriverUnload\n"));
    pNextObj = pDriverObject->DeviceObject;
    while (pNextObj != NULL)
    {
        PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
            pNextObj->DeviceExtension;

        //删除符号链接
        UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
        IoDeleteSymbolicLink(&pLinkName);
        pNextObj = pNextObj->NextDevice;
        IoDeleteDevice(pDevExt->pDevice);
    }
}

/************************************************************************
* 函数名称:HelloDDKDispatchRoutine
* 功能描述:对读IRP进行处理
* 参数列表:
      pDevObj:功能设备对象
      pIrp:从IO请求包
* 返回 值:返回状态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,
    IN PIRP pIrp)
{
    KdPrint(("Enter HelloDDKDispatchRoutine\n"));
    NTSTATUS status = STATUS_SUCCESS;
    // 完成IRP
    pIrp->IoStatus.Status = status;
    pIrp->IoStatus.Information = 0;	// bytes xfered
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    KdPrint(("Leave HelloDDKDispatchRoutine\n"));
    return status;
}

Driver.h

#pragma once

#ifdef __cplusplus
extern "C"
{
#endif

#include <ntddk.h>
#include <NTDDK.h>

#ifdef __cplusplus
}
#endif 

#define arraysize(p) (sizeof(p)/sizeof((p)[0]))

typedef struct _DEVICE_EXTENSION {
	PDEVICE_OBJECT pDevice;
	UNICODE_STRING ustrDeviceName;
	UNICODE_STRING ustrSymLinkName;
} DEVICE_EXTENSION, * PDEVICE_EXTENSION;

NTSTATUS CreateDevice(IN PDRIVER_OBJECT pDriverObject);
VOID HelloDDKUnload(IN PDRIVER_OBJECT pDriverObject);
NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,
	IN PIRP pIrp);


编译

这里在编译前,首先已经根据文章《Windows 驱动开发环境搭建》完成了基本的编译环境搭建。所以我们直接在 VS 2019 中将解决方案进行构建即可。

前面我们已经对项目的属性进行了设置,这里只要选取我们设置的平台 debug-X64,然后点击“生成”——“生成解决方案”/“重新生成解决方案”即可生成我们需要的驱动文件。

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发

安装

对于我们自己编写的驱动程序,为了防止驱动程序的加载使用导致我们的操作系统崩溃(Windows 驱动程序加载后就会运行在 RING-0 内核空间中,这个空间中的程序崩溃会直接导致 Windows 操作系统崩溃,最常见的现象就是蓝屏)。所以我们需要搭建 Windows 虚拟机环境,然后在虚拟机中进行新驱动的安装调试。

由于 Windows NT 驱动程序实际上就是 Windows 服务驱动程序,我们这里编写的并没有真正与设备进行相关,所以你可以理解它是一个运行在 Windows 内核空间的服务程序。对于 Windows NT 驱动程序的安装,这里可以给出两种方式:

  • 通过工具 DriverMonitor 进行打开加载安装
  • 修改注册表,添加驱动服务,重启计算机使用 net start helloNt 命令进行加载启动

修改注册表进行安装

使用快捷键 Win+R 打开对话框后输入 regedit 运行后打开系统的注册表。在注册表中我们找到服务驱动程序加载的注册位置。

计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\helloNt

在其中添加一个新的服务程序项目,然后加入一下内容进行描述,注意其中的 ImagePath 描述内容为加载的驱动程序文件(helloNt.sys)。

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
其中涉及到驱动文件的路径,输入内容如下:
Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
\??\C:\Tools\helloNt\helloNt\helloNt.sys

驱动文件存放位置:C:\Tools\helloNt\helloNt
Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发

注册表内容都添加配置完成后,我们重启计算机(重启才会使得注册表生效)。
重启完成后,使用快捷键 Win+X,选择 Windows powershell(管理员)打开终端对话框,使用命令 net start helloNt 启动加载驱动程序。

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
这时候驱动程序就已经加载启动了。我们可以打开“系统信息”工具来查看这个服务的状态。

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
可以看到这里已经启动了这个驱动程序。

使用工具 DriverMonitor 进行打开加载安装

本来这个 DriverMonitor 工具是 DriverStudio 工具集的一部分,但是这个 DriverStudio 工具集也已经被淘汰了,所以目前其实这个并不好找。我在 github 上找到一个仓库中存放有这部分的工具,所以可以从这里使用 git clone 进行下载使用。(https://github.com/HotIce0/windows_kernel_driver_learn_note.git)

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发

将这个工具下载下来解压后,打开可以看到一个简单的界面:

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
选择“File”,“Open Driver” 打开驱动文件。

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发

然后选择“File”-“Start Driver”,即可完成驱动的加载和运行。

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发

调试

这里的例程其实是从书籍《Windows 驱动开发技术详解》(张帆)中得到的,驱动加载的时候都是正常的,但是我们如果使用手动卸载启动时就会出现问题,使用 net 命令卸载驱动:

net stop helloNt

结果发现系统蓝屏了(Windows 崩溃了)

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
这时候就需要对驱动进行简单调试来找到出问题的地方。虽然我们在驱动中添加了 log 打印(KdPrint),但是当系统崩溃后,我们并不能及时看到 log 的打印输出(已经崩溃了还看个锤子,只能看到蓝屏)。针对这种情况就需要我们使用 windbg 双机调试来进行了。(双机调试环境请参考文章《windbg 双机调试环境搭建(虚拟机)》)

在宿主机上启动 windbg 后,与虚拟机建立连接。执行 windbg 传统调试步骤:

  1. 加载符号表(尤其是自己编译的驱动程序的符号表)
  2. 设置驱动模块入口断点,或者直接操作使系统崩溃(崩溃会自动停止在断点位置)
  3. 查看当前调用堆栈,参考源代码推断崩溃原因
  4. 修改源码,重新编译,加载验证

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
这里首先 break 下来,然后通过命令加载符号表,或者使用 GUI 操作加载符号表。

.srcpath+ "C:\Tools\helloNt\*.pdb"
Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
由于是卸载操作引起的崩溃,所以我们在卸载函数这里添加断点:

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
执行 net stop helloNt 命令后,windbg 会自动跳转到断点(记载了符号表)也会显示对应的代码。

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发

单步执行,然后发现崩溃点的调用栈:
Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
这里发现是在调用 Unload() 中的 DeleteSymbolicLink 的时候出现的问题。回顾我们这里的代码:

VOID HelloDDKUnload(IN PDRIVER_OBJECT pDriverObject)
{
    PDEVICE_OBJECT	pNextObj;
    KdPrint(("Enter DriverUnload\n"));
    pNextObj = pDriverObject->DeviceObject;
    while (pNextObj != NULL)
    {
        PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
            pNextObj->DeviceExtension;
        UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
        IoDeleteSymbolicLink(&pLinkName);
        pNextObj = pNextObj->NextDevice;
        IoDeleteDevice(pDevExt->pDevice);
    }
}

发现这里调用了 IoDeleteSymbolicLink(&pLinkName);,然后我们在这里打印 pLinkName 的地址看一下这个地址空间是否正常可访问。

也可以使用 !analyze -v 进行分析(这个分析结果可供参考)
Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发

发现结果和我们预测的位置基本一致。那问题就出现在这个链接变量这里了。为什么我们卸载链接变量会崩溃呢?

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
通过 Unload 局部变量中的数据可以看到这里的 pDevExt->ustrSymLinkName; 内存是不可以访问的,也就是说在这里的时候已经出现问题了。回到源代码中查看初始化部分发现这个字符串变量是在 CreateDevice 的时候放进去的:

	//创建设备名称
	UNICODE_STRING devName;
	RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDevice");
	
	//创建设备
	status = IoCreateDevice( pDriverObject,
						sizeof(DEVICE_EXTENSION),
						&(UNICODE_STRING)devName,
						FILE_DEVICE_UNKNOWN,
						0, TRUE,
						&pDevObj );
	if (!NT_SUCCESS(status))
		return status;

	pDevObj->Flags |= DO_BUFFERED_IO;
	pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
	pDevExt->pDevice = pDevObj;
	pDevExt->ustrDeviceName = devName;

这里创建了一个字符串变量 devName,然后将这个变量放到了 pDevExt 中。但是这里发现这个 devName 是这个函数的局部变量,这里放进去的只是字符串指针,而在后面的函数调用中取出了已经释放空间的指针进行使用肯定会有问题。

找到问题后,我们修改一下,把这里的局部变量修改成全局变量或者静态变量试一下:


UNICODE_STRING gdevName;
UNICODE_STRING gsymLinkName;

/************************************************************************
* 函数名称:CreateDevice
* 功能描述:初始化设备对象
* 参数列表:
      pDriverObject:从I/O管理器中传进来的驱动对象
* 返回 值:返回初始化状态
*************************************************************************/
#pragma INITCODE
NTSTATUS CreateDevice(
    IN PDRIVER_OBJECT	pDriverObject)
{
    NTSTATUS status;
    PDEVICE_OBJECT pDevObj;
    PDEVICE_EXTENSION pDevExt;

    //创建设备名称
    //UNICODE_STRING devName;
    RtlInitUnicodeString(&gdevName, L"\\Device\\MyDDKDevice");

    //创建设备
    status = IoCreateDevice(pDriverObject,
        sizeof(DEVICE_EXTENSION),
        &gdevName,
        FILE_DEVICE_UNKNOWN,
        0, TRUE,
        &pDevObj);
    if (!NT_SUCCESS(status))
        return status;

    pDevObj->Flags |= DO_BUFFERED_IO;
    pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
    pDevExt->pDevice = pDevObj;
    pDevExt->ustrDeviceName = gdevName;
    //创建符号链接
    //UNICODE_STRING symLinkName;
    RtlInitUnicodeString(&gsymLinkName, L"\\??\\HelloDDK");
    pDevExt->ustrSymLinkName = gsymLinkName;
    status = IoCreateSymbolicLink(&gsymLinkName, &gdevName);
    if (!NT_SUCCESS(status))
    {
        IoDeleteDevice(pDevObj);
        return status;
    }
    return STATUS_SUCCESS;
}

发现修改成全局变量后,结果还是一样的,这里的 memory 还是无法正常 read。

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发

比较发现这里是由于这部分的 buffer 空间 read error,修改代码,将这部分的字符串初始化从这个 #pragma INITCODE 声明的 CreateDevice 函数中移到 DriverEntry 中:


NTSTATUS
DriverEntry(
    _In_ PDRIVER_OBJECT  DriverObject,
    _In_ PUNICODE_STRING RegistryPath
    )
{
    NTSTATUS status;
    KdPrint(("Enter DriverEntry\n"));

    UNICODE_STRING devName;
    UNICODE_STRING symLinkName;
    RtlInitUnicodeString(&devName, L"\\Device\\MyDDKDevice");
    RtlInitUnicodeString(&symLinkName, L"\\??\\HelloDDK");

    //注册其他驱动调用函数入口
    DriverObject->DriverUnload = HelloDDKUnload;
    DriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine;
    DriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;
    DriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine;

    //创建驱动设备对象
    status = CreateDevice(DriverObject, devName, symLinkName);

    KdPrint(("DriverEntry end\n"));
    return status;
}

/************************************************************************
* 函数名称:CreateDevice
* 功能描述:初始化设备对象
* 参数列表:
      pDriverObject:从I/O管理器中传进来的驱动对象
* 返回 值:返回初始化状态
*************************************************************************/
#pragma INITCODE
NTSTATUS CreateDevice(
    IN PDRIVER_OBJECT	pDriverObject,
    IN UNICODE_STRING  devName,
    IN UNICODE_STRING  symLinkName)
{
    NTSTATUS status;
    PDEVICE_OBJECT pDevObj;
    PDEVICE_EXTENSION pDevExt;

    //创建设备名称
    //UNICODE_STRING devName;
    //RtlInitUnicodeString(&devName, L"\\Device\\MyDDKDevice");

    //创建设备
    status = IoCreateDevice(pDriverObject,
        sizeof(DEVICE_EXTENSION),
        &devName,
        FILE_DEVICE_UNKNOWN,
        0, TRUE,
        &pDevObj);
    if (!NT_SUCCESS(status))
        return status;

    pDevObj->Flags |= DO_BUFFERED_IO;
    pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
    pDevExt->pDevice = pDevObj;
    pDevExt->ustrDeviceName = devName;
    //创建符号链接
    //UNICODE_STRING symLinkName;
    //RtlInitUnicodeString(&symLinkName, L"\\??\\HelloDDK");
    pDevExt->ustrSymLinkName = symLinkName;
    status = IoCreateSymbolicLink(&symLinkName, &devName);
    if (!NT_SUCCESS(status))
    {
        IoDeleteDevice(pDevObj);
        return status;
    }
    return STATUS_SUCCESS;
}

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发
并且可以看到加载和卸载驱动操作已经正常了。说明我们的判断是正确的。

Windows NT 驱动程序的编译、安装、调试,Windows驱动,windows,驱动开发文章来源地址https://www.toymoban.com/news/detail-576813.html

到了这里,关于Windows NT 驱动程序的编译、安装、调试的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 在Windows 10(Win10)下安装“NVIDIA图形驱动程序-显卡驱动”、“NVIDIA控制面板”、CUDA Toolkit、cuDNN Archive的详细过程记录

    目标:安装CUDA Toolkit和cuDNN Archive 安装CUDA Toolkit首先要知道自己的CUDA版本号,通过控制面板打开“NVIDIA 控制面板”查看自己的CUDA版本。 按照别人的经验应该有下图中的“NVIDIA 控制面板”, 但是我的没有,如下图所示: 原因是没有完整安装NVIDIA的显卡驱动。下载一个驱动精

    2024年02月03日
    浏览(73)
  • [ 云计算相关 ] KVM虚拟化平台windows虚拟机迁移到openstack虚拟化平台(KVM虚拟化环境中Windows虚拟机安装Virtio驱动程序)

    👨‍🎓 博主介绍:大家好,我是 _PowerShell ,很高兴认识大家~ ✨主攻领域:【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 🎉点赞➕评论➕收藏 == 养成习惯(一键三连)😋 🎉欢迎关注💗一起学习👍一起讨论⭐️一起进步📝文末有彩蛋 🙏作者水平有

    2024年02月02日
    浏览(58)
  • 【Windows 10 驱动程序 已解决】重装 Win 10 系统后 电脑无法识别外接屏幕。投影:你的电脑不能投影到其他屏幕。请尝试重新安装驱动程序或使用其他视频卡。

          我的电脑是笔记本 联想拯救者 R720、 系统是 Windows 10 专业版 22H2、显示器接口是 HDMI   如果你在安装好系统后 联网 并且没有动系统 服务 : Windows Update ,那么在电脑识别到有可用的外接屏幕时(插入 HDMI 线后将听到一声类似插入 U 盘的声音),将会自动更新驱动

    2024年02月06日
    浏览(209)
  • windows驱动开发7:应用程序和驱动程序的通信

    一、基础介绍 1.1 设备与驱动的关系 设备由驱动去创建,访问一个设备,是首先得访问驱动。如果驱动在卸载的时候没有删除符号,r3下也是不能去访问设备的。 驱动程序和系统其他组件之间的交互是通过给设备发送或者接受发给设备的请求来交互的。换句话说,一个没有任

    2023年04月08日
    浏览(51)
  • Windows 驱动程序签名教程(一) --- 开发测试篇

    1.使用微软WHQL签名 2.使用2013-2015年签发的驱动签名,已过期未吊销未拉黑(不知道什么时候打个补丁会修复). 1.使用微软WHQL签名 2.使用过期签名的sha1 sha256都可以正常跑 1.使用微软WHQL签名,SHA256的签名需要打补丁支持(KB3033929). 2.SHA1签名 1.Windows 10 可以直接 输入命令: 2.如果还不

    2024年02月15日
    浏览(36)
  • 微软开源 windows-drivers-rs,用 Rust 开发 Windows 驱动程序

    导读 Microsoft Azure 首席技术官兼著名 Windows 软件开发人员 Mark Russinovich 在社交平台上宣布,启动了一个名为 windows-drivers-rs 的新开源项目。 该项目可帮助开发人员使用 Rust 开发 Windows 驱动程序,旨在支持 WDM (Windows Driver Model) 和 WDF (Windows Driver Framework) 驱动程序开发模型。 当前

    2024年02月08日
    浏览(49)
  • 微软开源 windows-drivers-rs, 用 Rust 开发 Windows 驱动程序

    Microsoft Azure 首席技术官兼著名 Windows 软件开发人员 Mark Russinovich 在社交平台上宣布, 启动了一个名为 windows-drivers-rs 的新开源项目。 该项目可帮助开发人员使用 Rust 开发 Windows 驱动程序, 旨在支持 WDM (Windows Driver Model) 和 WDF (Windows Driver Framework) 驱动程序开发模型。 当前版本的

    2024年02月08日
    浏览(43)
  • 如何检查 ODBC SQL Server 驱动程序版本 (Windows)

    您的计算机可能包含来自 Microsoft 和其他公司的多种 ODBC 驱动程序。使用 Windows  ODBC 数据源管理器 可以查看已安装的驱动程序的版本。 检查 ODBC SQL Server 驱动程序版本(32 位 ODBC) 在  ODBC 数据源管理器 中,单击**“驱动程序”**选项卡。 有关 Microsoft SQL Server 项的信息显示在

    2024年02月09日
    浏览(39)
  • 在windows通过VS Code开发Linux内核驱动程序

    最近在看Linux设备驱动程序第三版,为了在windows系统上练手操作,先是下载VMware Workstation安装了Linux系统虚拟机。然后在vscode上编写简单的示例程序,通过ftp把源文件发送到Linux虚拟机后,再在虚拟机上make编译测试内核驱动程序。这样即使是在内核日志中打印个简单的hello w

    2024年02月06日
    浏览(55)
  • windows 更新可能已经自动替换了您的AMD图形驱动程序

    AMD Radeon Software是用于Advanced Micro Devices的显卡和APU的设备驱动程序和实用程序软件包。它的图形用户界面是用Electron构建的,并且与 64 位 Windows 和 Linux 发行版兼容。 功能 Radeon Software 包括以下功能集:游戏资料管理、超频和欠压、性能监控、录制和流式传输、捕获的视频和屏

    2024年02月12日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包