返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >详解应用程序与驱动程序通信DeviceIoControl
  • 237
分享到

详解应用程序与驱动程序通信DeviceIoControl

2024-04-02 19:04:59 237人浏览 泡泡鱼
摘要

目录一、定义io控制码 二、定义驱动设备名,符号链接名三、将符号链接名与设备对象名称关联 ,等待IO控制码四、应用程序获取设备句柄,发送IO控制码五、总结Deviceioc

一、定义IO控制码 

其实可以看作是一种通信协议

看看CTL_CODE原型:


#define CTL_CODE( DeviceType, Function, Method, Access ) ( \
  ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
  )

可以看到,这个宏四个参数,自然是一个32位分成了4部分,高16位存储设备类型,14~15位访问权限,2~13位操作功能,最后0,1两位就是确定缓冲区是如何与I/O和文件系统数据缓冲区进行数据传递方式,最常见的就是METHOD_BUFFERED。

自定义CTL_CODE:

#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)

IOCTL_Device_Function:生成的IRP的MinorFunction

DeviceType:设备对象的类型。设备类型可参考:Http://blog.csdn.net/liyun123gx/article/details/38058965

Function :自定义的IO控制码。自己定义时取0x800到0xFFF,因为0x0到0x7FF是微软保留的。

Method :数据的操作模式。

METHOD_BUFFERED:缓冲区模式

METHOD_IN_DIRECT:直接写模式

METHOD_OUT_DIRECT:直接读模式

METHOD_NEITHER :Neither模式

Access:访问权限,可取值有:

FILE_ANY_ACCESS:表明用户拥有所有的权限

FILE_READ_DATA:表明权限为只读

FILE_WRITE_DATA:表明权限为可写

也可以 FILE_WRITE_DATA | FILE_READ_DATA:表明权限为可读可写,但还没达到FILE_ANY_ACCESS的权限。

继续介绍这个缓冲区数据传递方式Method:

Method表示Ring3/Ring0的通信中的内存访问方式,有四种方式:

#defineMETHOD_BUFFERED0

#defineMETHOD_IN_DIRECT1

#defineMETHOD_OUT_DIRECT2

#defineMETHOD_NEITHER3

(1)如果使用METHOD_BUFFERED,表示系统将用户的输入输出都经过pIrp->AssociatedIrp.SystemBuffer来缓冲,因此这种方式的通信比较安全

METHOD_BUFFERED方式相当于对Ring3的输入输出都进行了缓冲。

METHOD_BUFFERED方式:

(2)如果使用METHOD_IN_DIRECT或METHOD_OUT_DIRECT方式,表示系统会将输入缓冲在pIrp->AssociatedIrp.SystemBuffer中,并将输出缓冲区定,然后在内核模式下重新映射一段地址,这样也是比较安全的。

METHOD_IN_DIRECT和METHOD_OUT_DIRECT可称为"直接方式",是指系统依然对Ring3的输入缓冲区进行缓冲,但是对Ring3的输出缓冲区并没有缓冲,而是在内核中进行了锁定。这样Ring3输出缓冲区在驱动程序完成I/O请求之前,都是无法访问的,从一定程度上保障了安全性。

这两种方式,对于Ring3的输入缓冲区和METHOD_BUFFERED方式是一致的。对于Ring3的输出缓冲区,首先由系统锁定,并使用pIrp->MdlAddress来描述这段内存,驱动程序需要使用MmGetSystemAddressFORMdlSafe函数将这段内存映射到内核内存地址(OutputBuffer),然后可以直接写入OutputBuffer地址,最终在驱动派遣例程返回后,由系统解除这段内存的锁定。

METHOD_IN_DIRECT和METHOD_OUT_DIRECT方式的内存访问

METHOD_IN_DIRECT和METHOD_OUT_DIRECT方式的区别,仅在于打开设备的权限上,当以只读权限打开设备时,METHOD_IN_DIRECT方式的IoControl将会成功,而METHOD_OUT_DIRECT方式将会失败。如果以读写权限打开设备,两种方式都会成功。

METHOD_IN_DIRECT和METHOD_OUT_DIRECT方式:

(3)如果使用METHOD_NEITHER方式,"其他方式",虽然通信的效率提高了,但是不够安全。驱动的派遣函数中输入缓冲区可以通过I/O堆栈(IO_STACK_LOCATION)的stack->Parameters.DeviceIo Control.Type3InputBuffer得到。输出缓冲区可以通过pIrp->UserBuffer得到。由于驱动中的派遣函数不能保证传递进来的用户输入和输出地址,因此最好不要直接去读写这些地址的缓冲区。应该在读写前使用ProbeForRead和ProbeForWrite函数探测地址是否可读和可写。

METHOD_ NEITHER方式是不进行缓冲的,在驱动中可以直接使用Ring3的输入输出内存地址,

驱动程序可以通过pIrpStack->Parameters.DeviceIoControl.Type3InputBuffer得到Ring3的输入缓冲区地址(其中pIrpStack是IoGetCurrentIrpStackLocation(pIrp)的返回);通过pIrp-> UserBuffer得到Ring3的输出缓冲区地址。

由于METHOD_NEITHER方式并不安全,因此最好对Type3InputBuffer读取之前使用ProbeForRead函数进行探测,对UserBuffer写入之前使用ProbeForWrite函数进行探测,当没有发生异常时,再进行读取和写入操作。

METHOD_NEITHER方式:

二、定义驱动设备名,符号链接名

定义好了IO控制码CTL_CODE,第二步驱动程序还要准备驱动设备名和符号链接名。

关于在Ring0层中要设置驱动设备名的同时还要设置符号链接名的原因,是因为只有符号链接名才可以被用户模式下的应用程序识别。

windows下的设备是以"\Device\[设备名]”形式命名的。

例如磁盘分区的c盘,d盘的设备名称就是"\Device\HarddiskVolume1”,"\Device\HarddiskVolume2”, 当然也可以不指定设备名称。

如果IoCreateDevice中没有指定设备名称,那么I/O管理器会自动分配一个数字作为设备的名称。

例如"\Device\00000001"。\Device\[设备名],不容易记忆,通常符号链接可以理解为设备的别名,更重要的是设备名,只能被内核模式下的其他驱动所识别,而别名可以被用户模式下的应用程序识别,例如c盘,就是名为"c:"的符号链接,其真正的设备对象是"\Device\HarddiskVolume1”,所以在写驱动时候,一般我们创建符号链接,即使驱动中没有用到,这也算是一个好的习惯吧。

驱动中符号链接名是这样写的

L"\\??\\HelloDDK" --->\??\HelloDDK

或者

L"\\DosDevices\\HelloDDK"--->\DosDevices\HelloDDK

在应用程序中,符号链接名:

L"\\\\.\\HelloDDK"-->\\.\HelloDDK

DosDevices的符号链接名就是??, 所以"\\DosDevices\\XXXX"其实就是\\??\\XXXX


#define DEVICE_OBJECT_NAME  L"\\Device\\BufferedIODeviceObjectName"
//设备与设备之间通信
#define DEVICE_LINK_NAME    L"\\DosDevices\\BufferedIODevcieLinkName"
//设备与Ring3之间通信

三、将符号链接名与设备对象名称关联 ,等待IO控制码

驱动程序要做的最后一步,先用IoCreateDevice函数创建设备对象,再用IoCreateSymbolicLink将符号链接名与设备对象名称关联,大功告成,等待IO控制码。


 //创建设备对象名称
RtlInitUnicodeString(&DeviceObjectName,DEVICE_OBJECT_NAME);
//创建设备对象
Status = IoCreateDevice(DriverObject,NULL,
    &DeviceObjectName,
    FILE_DEVICE_UNKNOWN,
    0, FALSE,
    &DeviceObject);
if (!NT_SUCCESS(Status))
{
    return Status;
}
 
//创建设备连接名称
RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);
//将设备连接名称与设备名称关联
Status = IoCreateSymbolicLink(&DeviceLinkName,&DeviceObjectName);
 
if (!NT_SUCCESS(Status))
{
    IoDeleteDevice(DeviceObject);
    return Status;
}       

四、应用程序获取设备句柄,发送IO控制码

驱动程序铺垫打理好之后,应用程序就可以由符号链接名通过CreateFile函数获取到设备句柄DeviceHandle,再用本场的主角,DeviceIoControl通过这个DeviceHandle发送控制码了。

先看看这两个函数:


BOOL WINapi DeviceIoControl(
  _In_         HANDLE hDevice,       //CreateFile函数打开的设备句柄
  _In_         DWord dwIoControlCode,//自定义的控制码
  _In_opt_     LPVOID lpInBuffer,    //输入缓冲区
  _In_         DWORD nInBufferSize,  //输入缓冲区的大小
  _Out_opt_    LPVOID lpOutBuffer,   //输出缓冲区
  _In_         DWORD nOutBufferSize, //输出缓冲区的大小
  _Out_opt_    LPDWORD lpBytesReturned, //实际返回的字节数,对应驱动程序中pIrp->iOStatus.Information。
  _Inout_opt_  LPOVERLAPPED lpOverlapped //重叠操作结构指针。同步设为NULL,DeviceIoControl将进行阻塞调用;否则,应在编程时按异步操作设计
);
HANDLE CreateFile(
  LPCTSTR lpFileName,                         //打开的文件名
  DWORD dwDesiredAccess,                    //访问权限
  DWORD dwShareMode,                      //共享模式
  LPSECURITY_ATTRIBUTES lpSecurityAttributes,   //安全属性
  DWORD dwCreationDisposition,               //文件存在与不存在时的文件创建模式
  DWORD dwFlagsAndAttributes,                //文件属性设定(隐藏、只读、压缩、指定为系统文件等)
  HANDLE hTemplateFile                       //文件副本句柄
);

五、总结DeviceIoControl的通信流程

1.驱动程序和应用程序自定义好IO控制码 (CTL_CODE宏 四个参数,32位,4部分,存储设备类型,访问权限,操作功能,缓冲区数据传递方式(四种))

2.驱动程序定义驱动设备名,符号链接名, 将符号链接名与设备对象名称关联 ,等待IO控制码(IoCreateDevice,IoCreateSymbolicLink)

3.应用程序由符号链接名通过CreateFile函数获取到设备句柄DeviceHandle,再用本场的主角,DeviceIoControl通过这个设备句柄发送控制码给派遣函数。

六、源代码

BufferedIO.h


#pragma once
#include <ntifs.h>

#define CTL_SYS \
    CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)

#define DEVICE_OBJECT_NAME  L"\\Device\\BufferedIODeviceObjectName"
//设备与设备之间通信
#define DEVICE_LINK_NAME    L"\\DosDevices\\BufferedIODevcieLinkName"
//设备与Ring3之间通信
VOID DriverUnload(PDRIVER_OBJECT DriverObject);
NTSTATUS PassThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp);
NTSTATUS ControlThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp);

BufferedIO.c


#include "BufferedIO.h"

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING ReGISterPath)
{
    NTSTATUS Status = STATUS_SUCCESS;
    PDEVICE_OBJECT  DeviceObject = NULL;
    UNICODE_STRING  DeviceObjectName;
    UNICODE_STRING  DeviceLinkName;
    ULONG           i;
    //   栈
    //   堆
    //   全局(global Static Const)
    DriverObject->DriverUnload = DriverUnload;
 
    //创建设备对象名称
    RtlInitUnicodeString(&DeviceObjectName,DEVICE_OBJECT_NAME);
 
    //创建设备对象
    Status = IoCreateDevice(DriverObject,NULL,
        &DeviceObjectName,
        FILE_DEVICE_UNKNOWN,
        0, FALSE,
        &DeviceObject);
    if (!NT_SUCCESS(Status))
    {
        return Status;
    }
    //创建设备连接名称
    RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);
 
    //将设备连接名称与设备名称关联
    Status = IoCreateSymbolicLink(&DeviceLinkName,&DeviceObjectName);
 
    if (!NT_SUCCESS(Status))
    {
        IoDeleteDevice(DeviceObject);
        return Status;
    }
    //设计符合我们代码的派遣历程
    for (i=0;i<IRP_MJ_MAXIMUM_FUNCTION;i++)
    {
        DriverObject->MajorFunction[i] = PassThroughDispatch;   //函数指针
    }
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ControlThroughDispatch;
    return Status;
}
//派遣历程
NTSTATUS PassThroughDispatch(PDEVICE_OBJECT  DeviceObject,PIRP Irp)
{
    Irp->IoStatus.Status = STATUS_SUCCESS;     //LastError()
    Irp->IoStatus.Information = 0;             //ReturnLength
    IoCompleteRequest(Irp, IO_NO_INCREMENT);   //将Irp返回给Io管理器
    return STATUS_SUCCESS;
}
NTSTATUS ControlThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp)
{
    NTSTATUS Status;
    ULONG_PTR Informaiton = 0;
    PVOID InputData = NULL;
    ULONG InputDataLength = 0;
    PVOID OutputData = NULL;
    ULONG OutputDataLength = 0;
    ULONG IoControlCode = 0;
    PIO_STACK_LOCATION  IoStackLocation = IoGetCurrentIrpStackLocation(Irp);  //Irp堆栈  
    IoControlCode = IoStackLocation->Parameters.DeviceIoControl.IoControlCode;
    InputData  = Irp->AssociatedIrp.SystemBuffer;
    OutputData = Irp->AssociatedIrp.SystemBuffer;
    InputDataLength  = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;
    OutputDataLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
    switch (IoControlCode)
    {
    case CTL_SYS:
    {
        if (InputData != NULL&&InputDataLength > 0)
        {
            DbgPrint("%s\r\n", InputData);
        }
        if (OutputData != NULL&&OutputDataLength >= strlen("Ring0->Ring3") + 1)
        {
            memcpy(OutputData, "Ring0->Ring3", strlen("Ring0->Ring3") + 1);
            Status = STATUS_SUCCESS;
            Informaiton = strlen("Ring0->Ring3") + 1;
        }
        else
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;   //内存不够
            Informaiton = 0;
        }
        break;
    }
    default:
        break;
    }
    Irp->IoStatus.Status = Status;             //Ring3 GetLastError();
    Irp->IoStatus.Information = Informaiton;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);  //将Irp返回给Io管理器
    return Status;                            //Ring3 DeviceIoControl()返回值
}
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
    UNICODE_STRING  DeviceLinkName;
    PDEVICE_OBJECT  v1 = NULL;
    PDEVICE_OBJECT  DeleteDeviceObject = NULL;
     
    RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);
    IoDeleteSymbolicLink(&DeviceLinkName);
 
    DeleteDeviceObject = DriverObject->DeviceObject;
    while (DeleteDeviceObject != NULL)
    {
        v1 = DeleteDeviceObject->NextDevice;
        IoDeleteDevice(DeleteDeviceObject);
        DeleteDeviceObject = v1;
    }
}

IO.cpp


// 缓冲区IO.cpp : 定义控制台应用程序的入口点。
//
 
#include "stdafx.h"
#include <windows.h>
#define DEVICE_LINK_NAME    L"\\\\.\\BufferedIODevcieLinkName"
 
#define CTL_SYS \
    CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)
int main()
{
    HANDLE DeviceHandle = CreateFile(DEVICE_LINK_NAME,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
    if (DeviceHandle==INVALID_HANDLE_VALUE)
    {
        return 0;
    }
    char BufferData = NULL;
    DWORD ReturnLength = 0;
    BOOL IsOk = DeviceIoControl(DeviceHandle, CTL_SYS,
        "Ring3->Ring0",
        strlen("Ring3->Ring0")+1,
        (LPVOID)BufferData,
        0,
        &ReturnLength,
        NULL);
    if (IsOk == FALSE)
    {
        int LastError = GetLastError();
 
        if (LastError == ERROR_NO_SYSTEM_RESOURCES)
        {
            char BufferData[MAX_PATH] = { 0 };
            IsOk = DeviceIoControl(DeviceHandle, CTL_SYS,
                "Ring3->Ring0",
                strlen("Ring3->Ring0") + 1,
                (LPVOID)BufferData,
                MAX_PATH,
                &ReturnLength,
                NULL);
 
            if (IsOk == TRUE)
            {
                printf("%s\r\n", BufferData);
            }
        }
    }
    if (DeviceHandle != NULL)
    {
        CloseHandle(DeviceHandle);
        DeviceHandle = NULL;
    }
    printf("Input AnyKey To Exit\r\n");
 
    getchar();
    return 0;
}

以上就是详解应用程序与驱动程序通信DeviceIoControl的详细内容,更多关于应用程序 驱动程序通信 DeviceIoControl的资料请关注编程网其它相关文章!

--结束END--

本文标题: 详解应用程序与驱动程序通信DeviceIoControl

本文链接: https://lsjlt.com/news/128008.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 详解应用程序与驱动程序通信DeviceIoControl
    目录一、定义IO控制码 二、定义驱动设备名,符号链接名三、将符号链接名与设备对象名称关联 ,等待IO控制码四、应用程序获取设备句柄,发送IO控制码五、总结DeviceIoC...
    99+
    2024-04-02
  • 应用程序与驱动程序通信DeviceIoControl的示例分析
    这篇文章主要介绍了应用程序与驱动程序通信DeviceIoControl的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、定义IO控制码 其实可以看作是一种...
    99+
    2023-06-15
  • 如何在应用程序中使用Cassandra的驱动程序与Cassandra集群交互
    要在应用程序中使用Cassandra的驱动程序与Cassandra集群交互,首先需要选择适合你的编程语言的Cassandra驱动程序...
    99+
    2024-04-02
  • 微信小程序单选框组应用详解
    本文实例为大家分享了微信小程序单选框组应用的具体代码,供大家参考,具体内容如下 需求概述 有一个核选项数组,里面存放着核选项名称、内容、ID、选择状态。选择状态有未选择、符合、不符合...
    99+
    2024-04-02
  • python启动应用程序和终止应用程序
    1. 目的 每天上班,工作需要,电脑上需要每天开机启动一些软件,下班时候,需要关掉一些软件。一个一个打开和关闭貌似是很繁琐的,于是乎,这个脚本产生了。 2. 环境 系统环境: - win7-32位 - python 2.7...
    99+
    2023-01-31
    应用程序 python
  • nvidia驱动程序与windows不兼容怎么解决
    这篇“nvidia驱动程序与windows不兼容怎么解决”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“nvidia驱动程序与...
    99+
    2023-07-02
  • windows驱动程序错误如何解决
    今天小编给大家分享一下windows驱动程序错误如何解决的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。驱动程序错误解决方法:...
    99+
    2023-06-30
  • windows驱动程序无法使用如何解决
    今天小编给大家分享一下windows驱动程序无法使用如何解决的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。解决方法:方法一:...
    99+
    2023-06-30
  • win10驱动程序无法使用怎么解决
    本文小编为大家详细介绍“win10驱动程序无法使用怎么解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“win10驱动程序无法使用怎么解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。win10驱动程序无法使用...
    99+
    2023-06-30
  • 微信小程序bindtap与catchtap的区别详解
    目录1、什么是事件2、如何使用事件3、bindtap和catchtap的区别4、小程序中事件分为冒泡事件和非冒泡事件。事件之target&&currentTarget...
    99+
    2024-04-02
  • 微信小程序怎么与服务器端通信
    微信小程序可以通过调用微信提供的API,与服务器端进行通信。以下是一般的步骤: 在微信小程序中使用wx.request()方法发送...
    99+
    2024-04-03
    微信小程序 服务器
  • Node.js WebSocket 与移动应用程序的集成
    Node.js 与 WebSocket 相结合,为移动应用程序提供了一个强大的实时通信平台。本文将深入探究 WebSocket 的原理,并提供分步指南,展示如何在 Node.js 服务器和移动应用程序之间建立 WebSocket 连接。 ...
    99+
    2024-03-01
    Node.js、WebSocket、移动应用程序、实时通信
  • 在所有 4 种 JDBC 驱动程序类型中,什么时候使用哪种驱动程序
    在选择使用哪种 JDBC 驱动程序时,可以考虑以下几个因素:1. 类型1驱动程序(JDBC-ODBC桥):适用于访问需要使用ODBC...
    99+
    2023-10-10
    JDBC
  • 在所有 4 种 JDBC 驱动程序类型中,什么时候使用哪种驱动程序?
    如果您正在访问一种类型的数据库,例如 Oracle、Sybase 或 IBM,则首选驱动程序类型为 4。如果您的 Java 应用程序同时访问多种类型的数据库,类型 3 是首选驱动程序。类型 2 驱动程序在类型 3 或类型 4 驱动程序尚不适...
    99+
    2023-10-22
  • win7怎么禁用驱动程序签名
    要禁用Windows 7的驱动程序签名,您可以按照以下步骤操作:1. 打开“开始”菜单,并点击“控制面板”。2. 在控制面板中,选择...
    99+
    2023-09-08
    win7
  • 详解微信小程序应用和页面生命周期
    目录什么是生命周期生命周期的分类小程序的页面生命周期函数什么是生命周期 生命周期(Life Cycle)是指一个对象从创建→>运行>销毁的整个阶段,强调的是一个...
    99+
    2022-11-13
    小程序应用 微信小程序页面生命周期
  • win7找不到驱动程序如何解决
    在Windows 7中找不到驱动程序的问题,可以尝试以下解决方法:1. Windows更新:打开控制面板,点击“系统与安全”,选择“...
    99+
    2023-09-18
    win7
  • cad缺少驱动程序aceredist怎么解决
    这篇文章主要介绍“cad缺少驱动程序aceredist怎么解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“cad缺少驱动程序aceredist怎么解决”文章能帮助大家解决问题。cad缺少驱动程序a...
    99+
    2023-07-02
  • win7驱动程序代码28如何解决
    这篇“win7驱动程序代码28如何解决”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“win7驱动程序代码28如何解决”文章吧...
    99+
    2023-06-30
  • windows amd驱动程序超时怎么解决
    本篇内容主要讲解“windows amd驱动程序超时怎么解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“windows amd驱动程序超时怎么解决”吧!解决方法:方法一:进入amd官网,点击“...
    99+
    2023-06-30
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作