NT驱动程序和WDM驱动程序的区别

1. Windows驱动程序分为两类,一类是不支持即插即用功能的NT式的驱动程序;另一类是支持即插即用功能的WDM式的驱动程序。


2. NT式的驱动程序要导入的头文件时NTDDK.H,而WDM式的驱动要导入的头文件为WDM.H.


3. DriverEntry需要放在INIT标志的内存中。INIT标志指明该函数只是在加载的时候需要载入内存,而当驱动程序加载成功后,该函数可以从内存中卸载掉。


4.C++编写驱动需要注意:

[cpp] view plaincopy
  1. #ifdef  __cplusplus    
  2. extern  “C”    
  3. {    
  4. #endif    
  5.   
  6. #include <NTDDK.H>    
  7.   
  8. #ifdef  __cplusplus    
  9. }    
  10. #endif    
  11.     
  12. #pragma  INITCODE  extern  “C”  NTSTATUS   DriverEntry(IN  PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING  pRegistryPath){}  


5. 来看一个简单的NT式驱动

Driver.h:

[cpp] view plaincopy
  1. #pragma once  
  2.   
  3.   
  4. #ifdef __cplusplus  
  5.   
  6. extern "C"  
  7.   
  8. {  
  9.   
  10. #endif  
  11.   
  12. #include <NTDDK.h>  
  13.   
  14. #ifdef __cplusplus  
  15.   
  16. }  
  17.   
  18. #endif   
  19.   
  20.   
  21.   
  22. #define PAGEDCODE code_seg("PAGE")  
  23.   
  24. #define LOCKEDCODE code_seg()  
  25.   
  26. #define INITCODE code_seg("INIT")  
  27.   
  28.   
  29.   
  30. #define PAGEDDATA data_seg("PAGE")  
  31.   
  32. #define LOCKEDDATA data_seg()  
  33.   
  34. #define INITDATA data_seg("INIT")  
  35.   
  36.   
  37.   
  38. #define arraysize(p) (sizeof(p)/sizeof((p)[0]))  
  39.   
  40.   
  41. typedef struct _DEVICE_EXTENSION {  
  42.   
  43.     PDEVICE_OBJECT pDevice;  
  44.   
  45.     UNICODE_STRING ustrDeviceName;    //设备名称  
  46.   
  47.     UNICODE_STRING ustrSymLinkName;    //符号链接名  
  48.   
  49. } DEVICE_EXTENSION, *PDEVICE_EXTENSION;  
  50.   
  51.   
  52.   
  53. // 驱动函数声明  
  54.   
  55. NTSTATUS CreateDevice (IN PDRIVER_OBJECT pDriverObject);  
  56.   
  57. VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject);  
  58.   
  59. NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,  
  60.   
  61.                                  IN PIRP pIrp);  


driver.cpp

[cpp] view plaincopy
  1. /************************************************************************ 
  2.  
  3. * 函数名称:DriverEntry 
  4.  
  5. * 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象 
  6.  
  7. * 参数列表: 
  8.  
  9.       pDriverObject:从I/O管理器中传进来的驱动对象 
  10.  
  11.       pRegistryPath:驱动程序在注册表的中的路径 
  12.  
  13. * 返回 值:返回初始化驱动状态 
  14.  
  15. *************************************************************************/  
  16.   
  17. #pragma INITCODE  
  18.   
  19. extern "C" NTSTATUS DriverEntry (  
  20.   
  21.             IN PDRIVER_OBJECT pDriverObject,  
  22.   
  23.             IN PUNICODE_STRING pRegistryPath    )   
  24.   
  25. {  
  26.   
  27.     NTSTATUS status;  
  28.   
  29.     KdPrint(("Enter DriverEntry\n"));  
  30.   
  31.   
  32.   
  33.     //注册其他驱动调用函数入口  
  34.   
  35.     pDriverObject->DriverUnload = HelloDDKUnload;  
  36.   
  37.     pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine;  
  38.   
  39.     pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine;  
  40.   
  41.     pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;  
  42.   
  43.     pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine;  
  44.   
  45.       
  46.   
  47.     //创建驱动设备对象  
  48.   
  49.     status = CreateDevice(pDriverObject);  
  50.   
  51.   
  52.   
  53.     KdPrint(("DriverEntry end\n"));  
  54.   
  55.     return status;  
  56.   
  57. }  
  58.   
  59.   
  60.   
  61. /************************************************************************ 
  62.  
  63. * 函数名称:CreateDevice 
  64.  
  65. * 功能描述:初始化设备对象 
  66.  
  67. * 参数列表: 
  68.  
  69.       pDriverObject:从I/O管理器中传进来的驱动对象 
  70.  
  71. * 返回 值:返回初始化状态 
  72.  
  73. *************************************************************************/  
  74.   
  75. #pragma INITCODE   //指明此函数加载到INIT内存区域(即只在加载的时候需要载入内存,加载成功后可以从内存中卸载掉)  
  76.   
  77. NTSTATUS CreateDevice (  
  78.   
  79.         IN PDRIVER_OBJECT    pDriverObject)   
  80.   
  81. {  
  82.   
  83.     NTSTATUS status;  
  84.   
  85.     PDEVICE_OBJECT pDevObj;  
  86.   
  87.     PDEVICE_EXTENSION pDevExt;  
  88.   
  89.       
  90.   
  91.     //创建设备名称  
  92.   
  93.     UNICODE_STRING devName;  
  94.   
  95.     RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDevice");  
  96.   
  97.       
  98.   
  99.     //创建设备  
  100.   
  101.     status = IoCreateDevice( pDriverObject,  
  102.   
  103.                         sizeof(DEVICE_EXTENSION),  
  104.   
  105.                         &(UNICODE_STRING)devName,  
  106.   
  107.                         FILE_DEVICE_UNKNOWN,//此种设备为独占设备  
  108.   
  109.                         0, TRUE,  
  110.   
  111.                         &pDevObj );  
  112.   
  113.     if (!NT_SUCCESS(status))  
  114.   
  115.         return status;  
  116.   
  117.   
  118.   
  119.     pDevObj->Flags |= DO_BUFFERED_IO;  
  120.   
  121.     pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;  
  122.   
  123.     pDevExt->pDevice = pDevObj;  
  124.   
  125.     pDevExt->ustrDeviceName = devName;  
  126.   
  127.     //创建符号链接  
  128.   
  129.     UNICODE_STRING symLinkName;  
  130.   
  131.     RtlInitUnicodeString(&symLinkName,L"\\??\\HelloDDK");  
  132.   
  133.     pDevExt->ustrSymLinkName = symLinkName;  
  134.   
  135.     status = IoCreateSymbolicLink( &symLinkName,&devName );  
  136.   
  137.     if (!NT_SUCCESS(status))   
  138.   
  139.     {  
  140.   
  141.         IoDeleteDevice( pDevObj );  
  142.   
  143.         return status;  
  144.   
  145.     }  
  146.   
  147.     return STATUS_SUCCESS;  
  148.   
  149. }  
  150.   
  151.   
  152.   
  153. /************************************************************************ 
  154.  
  155. * 函数名称:HelloDDKUnload 
  156.  
  157. * 功能描述:负责驱动程序的卸载操作 
  158.  
  159. * 参数列表: 
  160.  
  161.       pDriverObject:驱动对象 
  162.  
  163. * 返回 值:返回状态 
  164.  
  165. *************************************************************************/  
  166.   
  167. #pragma PAGEDCODE  
  168.   
  169. VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject)   
  170.   
  171. {//遍历系统中所有的此类设备对象,删除设备对象及其符号链接  
  172.   
  173.     PDEVICE_OBJECT    pNextObj;  
  174.   
  175.     KdPrint(("Enter DriverUnload\n"));  
  176.   
  177.     pNextObj = pDriverObject->DeviceObject;  
  178.   
  179.     while (pNextObj != NULL)   
  180.   
  181.     {  
  182.   
  183.         PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)  
  184.   
  185.             pNextObj->DeviceExtension;  
  186.   
  187.   
  188.   
  189.         //删除符号链接  
  190.   
  191.         UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;  
  192.   
  193.         IoDeleteSymbolicLink(&pLinkName);  
  194.   
  195.         pNextObj = pNextObj->NextDevice;  
  196.   
  197.         IoDeleteDevice( pDevExt->pDevice );  
  198.   
  199.     }  
  200.   
  201. }  
  202.   
  203.   
  204.   
  205. /************************************************************************ 
  206.  
  207. * 函数名称:HelloDDKDispatchRoutine 
  208.  
  209. * 功能描述:对读IRP进行处理 
  210.  
  211. * 参数列表: 
  212.  
  213.       pDevObj:功能设备对象 
  214.  
  215.       pIrp:从IO请求包 
  216.  
  217. * 返回 值:返回状态 
  218.  
  219. *************************************************************************/  
  220.   
  221. #pragma PAGEDCODE  
  222.   
  223. NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,  
  224.   
  225.                                  IN PIRP pIrp)   
  226.   
  227. {  
  228.   
  229.     KdPrint(("Enter HelloDDKDispatchRoutine\n"));  
  230.   
  231.     NTSTATUS status = STATUS_SUCCESS;  
  232.   
  233.     // 完成IRP  
  234.   
  235.     pIrp->IoStatus.Status = status;  
  236.   
  237.     pIrp->IoStatus.Information = 0;    // bytes xfered  
  238.   
  239.     IoCompleteRequest( pIrp, IO_NO_INCREMENT );  
  240.   
  241.     KdPrint(("Leave HelloDDKDispatchRoutine\n"));  
  242.   
  243.     return status;  
  244.   
  245. }  
驱动虽然有了设备名称,但是这种设备名只能在内核态可见,而对于应用程序时不可见的。因此,驱动需要暴露一个符号链接,该链接指向真正的设备名称。
编译好生成的sys文件,我们可以使用 DriverMonitor 加载,加载->启动->停止->卸载。


6.再来看看WDM式驱动有什么不同。头文件就直接忽略了。
DriverEntry入口函数:

[cpp] view plaincopy
  1. /************************************************************************ 
  2. * 函数名称:DriverEntry 
  3. * 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象 
  4. * 参数列表: 
  5.       pDriverObject:从I/O管理器中传进来的驱动对象 
  6.       pRegistryPath:驱动程序在注册表的中的路径 
  7. * 返回 值:返回初始化驱动状态 
  8. *************************************************************************/  
  9. #pragma INITCODE   
  10. extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,  
  11.                                 IN PUNICODE_STRING pRegistryPath)  
  12. {  
  13.     KdPrint(("Enter DriverEntry\n"));  
  14.   
  15.     pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;  
  16.     pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;  
  17.     pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =   
  18.     pDriverObject->MajorFunction[IRP_MJ_CREATE] =   
  19.     pDriverObject->MajorFunction[IRP_MJ_READ] =   
  20.     pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMDispatchRoutine;  
  21.     pDriverObject->DriverUnload = HelloWDMUnload;  
  22.   
  23.     KdPrint(("Leave DriverEntry\n"));  
  24.     return STATUS_SUCCESS;  
  25. }  
这里增加了一个设置AddDevice回调函数,此回调函数的作用是创建设备对象并由PNP(即插即用)管理器调用。并设置对IRP_MJ_PNP的IRP的回调函数。这都是NT和WDM驱动最大的不同点。而且在WDM驱动中,大部分卸载工作都不是由DriverUnload来处理,而是放在对IRP_MN_REMOVE_DEVICE的IRP的处理函数中处理。


下面是AddDevice回调函数的处理。

[cpp] view plaincopy
  1. /************************************************************************ 
  2. * 函数名称:HelloWDMAddDevice 
  3. * 功能描述:添加新设备 
  4. * 参数列表: 
  5.       DriverObject:从I/O管理器中传进来的驱动对象 
  6.       PhysicalDeviceObject:从I/O管理器中传进来的物理设备对象 
  7. * 返回 值:返回添加新设备状态 
  8. *************************************************************************/  
  9. #pragma PAGEDCODE  
  10. NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,  
  11.                            IN PDEVICE_OBJECT PhysicalDeviceObject)  
  12. {   
  13.     PAGED_CODE();  
  14.     KdPrint(("Enter HelloWDMAddDevice\n"));  
  15.   
  16.     NTSTATUS status;  
  17.     PDEVICE_OBJECT fdo;  
  18.     UNICODE_STRING devName;  
  19.     RtlInitUnicodeString(&devName,L"\\Device\\MyWDMDevice");  
  20.     status = IoCreateDevice(  
  21.         DriverObject,  
  22.         sizeof(DEVICE_EXTENSION),  
  23.         &(UNICODE_STRING)devName,  
  24.         FILE_DEVICE_UNKNOWN,  
  25.         0,  
  26.         FALSE,  
  27.         &fdo);  
  28.     if( !NT_SUCCESS(status))  
  29.         return status;  
  30.     PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;  
  31.     pdx->fdo = fdo;  
  32.     pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);  
  33.     UNICODE_STRING symLinkName;  
  34.     RtlInitUnicodeString(&symLinkName,L"\\DosDevices\\HelloWDM");  
  35.   
  36.     pdx->ustrDeviceName = devName;  
  37.     pdx->ustrSymLinkName = symLinkName;  
  38.     status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName,&(UNICODE_STRING)devName);  
  39.   
  40.     if( !NT_SUCCESS(status))  
  41.     {  
  42.         IoDeleteSymbolicLink(&pdx->ustrSymLinkName);  
  43.         status = IoCreateSymbolicLink(&symLinkName,&devName);  
  44.         if( !NT_SUCCESS(status))  
  45.         {  
  46.             return status;  
  47.         }  
  48.     }  
  49.   
  50.     fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;  
  51.     fdo->Flags &= ~DO_DEVICE_INITIALIZING;  
  52.   
  53.     KdPrint(("Leave HelloWDMAddDevice\n"));  
  54.     return STATUS_SUCCESS;  
  55. }  
其中PAGED_CODE是一个DDK提供的宏,只在check版中有效。当此例程所在的中断请求及超过APC_LEVEL时,会产生一个断言。


本例对IRP_MN_REMOVE_DEVICE的处理。

[cpp] view plaincopy
  1. /************************************************************************ 
  2. * 函数名称:HandleRemoveDevice 
  3. * 功能描述:对IRP_MN_REMOVE_DEVICE IRP进行处理 
  4. * 参数列表: 
  5.       fdo:功能设备对象 
  6.       Irp:从IO请求包 
  7. * 返回 值:返回状态 
  8. *************************************************************************/  
  9. #pragma PAGEDCODE  
  10. NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp)  
  11. {  
  12.     PAGED_CODE();  
  13.     KdPrint(("Enter HandleRemoveDevice\n"));  
  14.   
  15.     Irp->IoStatus.Status = STATUS_SUCCESS;  
  16.     NTSTATUS status = DefaultPnpHandler(pdx, Irp);  
  17.     IoDeleteSymbolicLink(&(UNICODE_STRING)pdx->ustrSymLinkName);  
  18.   
  19.     //调用IoDetachDevice()把fdo从设备栈中脱开:  
  20.     if (pdx->NextStackDevice)  
  21.         IoDetachDevice(pdx->NextStackDevice);  
  22.       
  23.     //删除fdo:  
  24.     IoDeleteDevice(pdx->fdo);  
  25.     KdPrint(("Leave HandleRemoveDevice\n"));  
  26.     return status;  
  27. }  
而对卸载例程的处理可以什么都不用做。


其他PNP的IRP如果不需要做处理,那么直接传递到底层驱动,并将底层驱动的结果返回。

[cpp] view plaincopy
  1. /************************************************************************ 
  2. * 函数名称:DefaultPnpHandler 
  3. * 功能描述:对PNP IRP进行缺省处理 
  4. * 参数列表: 
  5.       pdx:设备对象的扩展 
  6.       Irp:从IO请求包 
  7. * 返回 值:返回状态 
  8. *************************************************************************/   
  9. #pragma PAGEDCODE  
  10. NTSTATUS DefaultPnpHandler(PDEVICE_EXTENSION pdx, PIRP Irp)  
  11. {  
  12.     PAGED_CODE();  
  13.     KdPrint(("Enter DefaultPnpHandler\n"));  
  14.     IoSkipCurrentIrpStackLocation(Irp);  
  15.     KdPrint(("Leave DefaultPnpHandler\n"));  
  16.     return IoCallDriver(pdx->NextStackDevice, Irp);  
  17. }  
安装WDM式驱动需要一个inf文件。inf文件描述了WDM驱动程序的操作硬件设备的信息和驱动程序的一些信息。
可以直接右击这个inf文件进行安装即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/509342.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java调用类中的静态变量时类中静态代码块什么情况会执行以及类的初始化问题?

类从被加载到虚拟机内存中开始&#xff0c;到卸载出内存为止&#xff0c;它的整个生命周期包括&#xff1a;加载、验证、准备、解析、初始化、使用和卸载七个阶段。 类初始化是类加载过程的最后一个阶段&#xff0c;到初始化阶段&#xff0c;才真正开始执行类中的Java程序代码…

Windows驱动程序的加载

NT式驱动程序的加载 1. 用DriverMonitor工具加载NT式驱动 2. 在注册表中填写相应的字段,Windows对NT式驱动程序的加载&#xff0c;是基于服务的方式加载的&#xff0c;类似于Windows服务程序的加载。设备驱动程序的动态加载主要是基于服务控制程序&#xff08;Service Contro…

北京市(朝阳区)(西城区)(海定区)正则表达式(代码保存)

“ String str “北京市(朝阳区)(西城区)(海定区)”; String ptr “.*?(?\()”; Pattern p Pattern.compile(ptr); Matcher matcher p.matcher(str); if(matcher.find()){ System.out.println(matcher.group()); } “

上升子序列

SubmitStatisticProblem Description 一个只包含非负整数的序列bi&#xff0c;当b1 < b2 < ... < bS的时候&#xff0c;我们称这个序列是上升的。对于给定的一个序列{a1, a2, ...,aN}&#xff0c;我们可以得到一些上升的子序列{ai1, ai2, ..., aiK}&#xff0c;这里1 …

设备对象

设备对象的结构体中有3个域 &#xff08;1&#xff09;DriverObject。这个比较好理解。就是这个设备对象所属的驱动对象。毕竟设备对象是由驱动对象创建的。 &#xff08;2&#xff09;NextDevice。指向下一个设备对象。这里的指向&#xff1a;指的是由同一个驱动对象的创建的设…

Resources.getResourceAsStream用法

Resources&#xff08;com.ibatis.common.resource.*&#xff09; Resources 类为从类路径中加载资源&#xff0c;提供了易于使用的方法。处理 ClassLoader 是一项 富于挑战的工作&#xff0c;尤其是应用服务器/容器的情况下。 Resources 类试图简化这些工作&#xff0c;Res…

阿里面试回来,想和Java程序员谈一谈

版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 目录(?)[-] 引言第一个问题阿里面试都问什么社招面试如何准备对于Java程序猿学习的建议结语 引言 其实本来真的没打算写这篇文章&#xff0c;主要是LZ得记忆力不是很好&#xff0c;不像一些记忆力强…

jquery添加div实现消息聊天框

上代码 <% page language"java" contentType"text/html; charsetUTF-8"pageEncoding"UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> &l…

数据结构实验之链表一:顺序建立链表

Problem Description 输入N个整数&#xff0c;按照输入的顺序建立单链表存储&#xff0c;并遍历所建立的单链表&#xff0c;输出这些数据。Input 第一行输入整数的个数N&#xff1b; 第二行依次输入每个整数。Output 输出这组整数。Example Input 8 12 56 4 6 55 15 33 62 Exam…

VS调试启动编辑并继续功能

在VS调试的过程中&#xff0c;可能需要进行源代码的修改&#xff0c;一般的做法是先停止当前的调试&#xff0c;然后重新编译再进行调试&#xff0c;哪怕做的一点点小的改动&#xff0c;这样做很浪费时间&#xff0c;尤其是当项目比较大&#xff0c;程序的启动和初始化需要较长…

IRP和IO_STACK_LOCATION

当一个应用程序调用函数去操作某个设备时&#xff0c;比如调用createFile,deviceIOControl,等等时&#xff0c;I/O管理器为此函数创建一个IRP数据结构对象和一个IRP_STACK_LOCATION数据结构对象数组。 &#xff08;数组个数等于驱动程序堆栈上驱动的个数&#xff09;。IRP对象中…

IRQL

转自&#xff1a;http://blog.csdn.net/changsha2011/article/details/6895529 最近开始入门windows 驱动开发&#xff0c;遇到一个IRQL。不知道何解&#xff0c;于是找了些资料&#xff0c;顺带记录下。下面的东西可能有不准确&#xff0c;如发现错误之处请指正&#xff0c;以…

#pragma code_seg(INIT)/code_seg(PAGE)

转自&#xff1a;http://blog.chinaunix.net/uid-24504987-id-161192.html Windows规定有些虚拟内存可以交换到文件中&#xff0c;这类内存被称为分页内存 有些虚拟内存 永远不会交换到文件中&#xff0c;这些内存叫非分页内存 #define PAGEDCODE code_seg(“PAGE”);//分页内…

JSON.stringify() 方法

JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串&#xff0c;如果指定了replacer是一个函数&#xff0c;则可以替换值&#xff0c;或者如果指定了replacer是一个数组&#xff0c;可选的仅包括指定的属性。 语法 JSON.stringify(value[, repla…

师--链表的结点插入

Problem Description 给出一个只有头指针的链表和 n 次操作&#xff0c;每次操作为在链表的第 m 个元素后面插入一个新元素x。若m 大于链表的元素总数则将x放在链表的最后。Input 多组输入。每组数据首先输入一个整数n(n∈[1,100])&#xff0c;代表有n次操作。接下来的n行&…

BeanUtils工具的使用(转载)

原文章地址&#xff1a;https://www.cnblogs.com/vmax-tam/p/4159985.html BeanUtils工具 什么是BeanUtils工具 BeanUtils工具是一种方便我们对JavaBean进行操作的工具&#xff0c;是Apache组织下的产品。 BeanUtils工具一般可以方便javaBean的哪些操作&#xff1f; 1&…

线程可警告状态以及APC队列

转自&#xff1a;http://blog.csdn.net/qq_22423659/article/details/53425828 一、线程可警告状态的真实含义 1、通过另一些方法让线程"暂停"(非SuspendThread方法&#xff0c;比如SleepEx&#xff0c;wait函数族等)&#xff0c;并可以进入一种称之为 Alterable的状…

数据结构实验之链表三:链表的逆置

Problem Description 输入多个整数&#xff0c;以-1作为结束标志&#xff0c;顺序建立一个带头结点的单链表&#xff0c;之后对该单链表的数据进行逆置&#xff0c;并输出逆置后的单链表数据。Input 输入多个整数&#xff0c;以-1作为结束标志。Output 输出逆置后的单链表数据。…

DBUtils使用详细示例(转载)

https://www.cnblogs.com/smyhvae/p/4085684.html

APC异步过程调用

转自&#xff1a;http://blog.sina.com.cn/s/blog_c2ef450f010192hx.html APC 异步过程调用 记得第一次看见这个名词的时候&#xff0c;觉得云里雾罩&#xff0c;然而上网看了下它的介绍&#xff0c;感觉说的都是没头没脑的 然而今天&#xff0c;我再次接触了它&#xff0c;我将…