Windows982000驱动程序编写方法

合集下载

Win2000驱动程序设计4

Win2000驱动程序设计4

1第十四章.系统线程驱动程序执行的工作不可能总是响应请求﹐至少不是在请求的时间动作﹒一些工作必须与调用者同步﹐可能与其它的驱动程序活动在不同的优先级﹒WIN2000允许单独的执行线程的创建﹐每个允许一个独立的代码路径﹒它们被适当的事件触发运行﹒本章介绍创建内核模式线程的过程﹒也介绍不同线程间的同步问题﹒定义和使用系统线程线程是执行的单位﹒每个线程保持一个独立的程序计数器和包含一个私有的CPU寄存器设置的上下文环境﹒每个线程有一个优先权﹐这个优先权决定何时线程得到系统处理器的控制权﹒通常﹐优先权越高的线程﹐越有可能得到控制权﹒线程可以在用户模式或者内核模式操作﹒系统线程运行在专用的内核模式﹒它没有用户模式上下文﹐也不能访问用户地址空间﹒像Win32线程﹐系统线程运行在或者低于APC_LEVEL IRQL﹐根据调度优先权竞争CPU的使用权﹒何时使用线程有几个使用线程的原因﹕1. 设备很慢﹐且很少访问﹒2. 设备转换状态需要很长时间(多于50毫秒)﹐且驱动程序必须等待转换发生﹒3. 设备为了一个简单的操作需要转换多个状态且驱动程序必须查询设备一段时间﹒4. 设备的一些状态变化不产生中断且驱动程序必须查询设备一段时间﹒应该使用一个CustomTimerDpc例程管理这样的设备﹒根据设备的活动量﹐这个方法可能充满DPC队列﹐使其它驱动程序变得缓慢﹒另一方面﹐运行在PASSIVE_LEVEL水平上不会影响到DPC例程﹒幸运的是﹐大多数现代的硬件不会像上面一样运行﹒遗留下来的硬件的状态变化需要驱动程序查询和重试﹒一个重要的例子是软驱和其它与软盘控制器连系的设备﹒另一个需要线程的是执行过多时间初始化的设备﹒驱动程序必须侦测初始化过程﹐查询状态的变化﹒因为服务控制管理器仅仅给驱动程序30秒来执行DriverEntry例程﹒否则服务控制管理器强制卸载驱动程序﹒解决办法是放置一个长时间运行的设备开始代码在一个单独的线程中﹐迅速使用STATUS_SUCCESS从DriverEntry例程中返回﹒最后﹐可能有一些只能运行在PASSIVE_LEVEL IRQL的操作﹒例如﹐如果驱动程序必须访问注册表或者执行文件操作﹐就必须考虑多线程了﹒创建和结束系统线程驱动程序使用PsCreateSystemThread函数来创建系统线程﹒因为只能在PASSIVE_LEVEL IRQL上调用这个函数﹐所以通常线程在DriverEntry或者AddDevice例程创建﹒当一个驱动程序被卸载﹐必须保证所有的它创建的线程都被结束﹒PsTerminateSystemThread函数就是系统线程结束自己的方法﹒不像Win32用户模式线程﹐系统线程不能强制结束﹒这就是说﹐需要建立一个信号机制﹐来让线程知道它应该结束﹒表14.1. NTSTATUS PsCreateSystemThread函数原型表14.2. NTSTATUS PsTerminateSystemThread函数原型管理线程优先权通常﹐系统线程应该运行在实时范围的低端﹒VOID ThreadStartRoutine( PVOID pContext ){ ...KeSetPriorityThread( KeGetCurrentThread(), LOW_REALTIME_PRIORITY );... }实时线程没有定量超时时间﹒所以只有当线程自愿进入等待状态的时候才会放弃CPU﹐或者被另一个优先权更高的线程抢占﹒由此﹐驱动程序不能依赖时间片轮转﹒系统工作者线程有些时候﹐运行在PASSIVE_LEVEL IRQL上的线程的创建和结束不是非常有效的﹒一种替代的方法是﹐使用一个回调机制来代表驱动程序工作﹒使用系统工作者线程是容易的﹒先为一个WORK_QUEUE_ITEM结构分配地址空间﹒系统使用这个块来保持工作请求的跟踪﹒再使用WORK_QUEUE_ITEM参数调用ExInitializeWorkItem函数联系一个回调函数﹒稍后﹐当一个系统线程需要执行回调函数的时候﹐调用ExQueueWorkItem函数来插入请求块到一个系统工作请求中﹒这个请求可以工作在实时优先权的系统线程或者一个可变优先权的线程﹒要知道所有的驱动程序分享同一组系统工作者线程﹒请求一个长时间的执行可能延迟来自其它驱动程序请求的执行﹒执行长时间的操作应该使用一个私有的线程﹐不要使用系统工作者线程﹒线程同步像Win32应用程序一样﹐系统线程也需要悬挂自己直到一些条件满足﹒这个部分描述系统线程可以使用的同步技术﹒时间同步最簡單的同步是停止線程的執行直到指定的時間間隔過去﹒內核提供KeDelayExecutionThread函數來方便的使用定時器對象﹒一般的同步系统线程可以通过等待派遣对象来同步它们的活动﹒派遣对象有两个状态﹐发信号状态和静止状态﹒当线程等待一个静止状态的派遣对象﹐线程就会停止直到对象变成发信号状态﹒有两个函数用来等待一个派遣对象﹒KeWaitForSingleObject函数这个函数使线程成为等待状态直到一个特殊的派遣对象被设置为发信号状态﹒timeout值应该被指定为如果派遣对象是静止状态是时﹐线程的唤醒时间﹒如果这个值设置为NULL﹐等待是不确定的﹒表14.3. NTSTATUS KeDelayExecutionThread函数原型表14.4. NTSTATUS KeWaitForSingleObject函数原型KeWaitForMultipleObjects函数这个函数将调用者线程变为等待状态直到任何或者所有的派遣对象被设置为发信号状态﹒表14.5. NTSTATUS KeWaitForMultipleObjects函数原型要知道﹐一次线程可以等待的对象数目是有限的﹒每个线程有一个内嵌的用作当前等待操作的等待块数组﹒线程可以使用这个数组等待最多THREAD_WAIT_OBJECTS个对象﹒如果THREAD_WAIT_OBJECTS表示的数目不够了﹐驱动程序提供的另外的等待块数组必须被包含在调用KeWaitForMultipleObjects函数中﹒然而﹐等待的对象数目不能超过MAXIMUM_WAIT_OBJECTS个﹒KeWaitForXxx函数可能从PASSIVE_ LEVEL或者DISPATCH_LEVEL IRQL被调用﹐然而﹐对于DISPATCH_LEVEL IRQL﹐必须指定timeout值为0(0与NULL是不同的)﹐在DISPATCH_LEVEL IRQL﹐这个调用对于单个对象用作查询是很有效的﹒使用派遣对象除了线程对象自己之外﹐驱动程序必须为任何可能用到的派遣对象分配存储空间﹒派遣对象必须是永久存储﹐所以﹐通常分配到设备或者控制器的Extension中﹒且必须在非分页的存储空间中﹒派遣对象必须在使用之前使用合适的KeInitializeXxx函数初始化一次﹒因为初始化函数只能在PASSIVE_LEVEL IRQL上被调用﹐所以﹐派遣对象常常在DriverEntry或者AddDevice例程中被初始化﹒下面部分详细描述派遣对象的各个方面﹒事件对象事件是必须被明确的设置为静止或者发信号状态的派遣对象﹒事件类似于一个二进制标志﹐允许一个线程通过设置为发信号状态来通知另一个线程﹒这些对象只有两种行为:通知事件和同步事件﹒在对象初始化的时候选择类型﹐这两个事件类型在被设置为发信号状态后的行为是不同的﹒只要一个通知事件保持发信号状态﹐所有的等待这个事件的线程就退出等待状态﹒通知事件必须被明确的设置为静止状态﹒图14.1. 事件对象同步系统线程当一个同步事件被设置为发信号状态﹐它保持发信号状态直到另一个线程调用KeWaitForXxx函数的时候﹐才自动的设置自己为静止状态﹒使用一个事件对象之前﹐要分配一个KEVENT数据类型的存储空间﹒两个使事件对象变为静止状态的函数是不同的﹒KeResetEvent函数返回事件变为静止状态之前的状态﹐KeClearEvent的速度比较快﹐因为它没有返回值﹐所以它用在之前的状态已知的情况下﹒本章的驱动程序提供一个使用系统线程的例子﹐它有一个需要暂停直到一个中断到达的工作者线程﹐因此这个线程等待一个事件对象﹒驱动程序的DpcForIsr例程设置事件为发信号状态﹐唤醒工作者线程﹒表14.6. 事件对象操作函数多驱动程序共享事件使用KeInitializeEvent函数创建的事件对象是很难被两个不相干的驱动程序共享﹒事件对象只能被指针引用﹐没有直接的协议(例如﹐内部的IOCTL)﹐没有一个简单的方法将一个驱动程序的指针传递到另一个驱动程序﹒尽管这样﹐当另一个驱动程序使用对象的时候﹐驱动程序要保证创建的这个对象没有卸出的问题﹒IoCreateSynchronizationEvent和IoCreateNotificationEvent函数允许创建命名的事件对象﹐只要两个驱动程序使用相同的事件名字﹐它们每个都可以获得指向同一个事件对象的指针﹒这两个函数的行为像WIN32的CreateEvent函数一样﹒换句话说﹐第一个驱动程序使用事件名调用函数﹐这将创建一个事件对象﹒后来的尝试创建一个事件对象的复制品的调用将仅仅返回一个存在的事件对象句柄﹒IoCreateXxxEvent函数有两个值得注意的性质:第一﹐KEVENT对象的存储器不是被驱动程序分配﹐是系统提供的存储空间﹒当最后一个用户将它释放的时候﹐系统自动删除对象﹒第二﹐IoCreateXxxEvent函数返回事件对象的句柄﹐不是存储空间指针﹒必须执行下列步骤来将句柄转变成为指针:1. 调用ObReferenceObjectByHandle函数﹒这个函数获得事件对象的指针﹐且将对象的指针引用计数增加1﹒2. 当句柄不再需要的时候﹐调用ZwClose函数将它释放﹒这个函数减小对象的句柄参考计数﹐在指针参考计数加1之后﹐再调用这个函数﹐否则对象将被删除﹒3. 当事件对象不再需要的时候﹐调用ObDereferenceObject函数减少指针参考计数﹐且可能删除事件对象﹒这些函数仅仅在PASSIVE_LEVEL IRQL被调用﹐它限制了驱动程序使用它们的范围﹒Mutex 对象互斥对象(Mutex)在同一时间只能被一个线程拥有的派遣对象﹒当一个线程拥有对象的时候﹐它变得静止﹐当它变得可以被线程利用的时候﹐它在发信号状态﹒互斥对象提供一个简单的机制来让多个线程互斥的访问共享的资源﹐例如﹐存储器﹒图14.2表示了线程B﹐C和D等待线程A拥有的互斥对象﹒当A释放了互斥对象﹐等待线程的其中之一获得互斥对象的拥有权﹒图14.2. 互斥对象同步系统线程使用互斥对象﹐必须在非分页的存储空间中保留一个KMUTEX类型的空间﹒要知道当一个互斥对象的初始化﹐总是设置它为发信号状态﹒如果一个线程在已经拥有互斥对象的情况下调用KeWaitForXxx函数﹐线程不会等待﹒而互斥对象增加引用计数器﹐事实上它是递归的拥有请求﹒当线程想要释放互斥对象﹐要调用KeReleaseMutex函数的次数和请求拥有的次数相等﹒这样互斥对象才会进入发信号状态﹒这个行为和Win32应用程序的互斥对象是相同的﹒在控制由驱动程序转变到用户模式之前驱动程序释放任何的互斥对象是非常重要的﹒内核将确认是否驱动程序线程在拥有互斥对象的情况下尝试返回控制给I/O管理器﹒例如﹐DriverEntry或者派遣例程是不允许请求一个可能被其它派遣例程或者系统线程释放的互斥对象表14.7. 互斥对象的操作函数信号量对象信号量(Semaphore)是一个保持一个计数的派遣对象﹒这个对象在它的引用计数器大于零的情况下保持发信号状态﹐当它的引用计数器为零的时候为静止状态﹒换句话说﹐信号量是一个计数互斥对象﹒图14.3显示了信号量的操作﹒线程B﹐C和D等待一个当前计数为零的信号量﹒当线程A调用两次KeReleaseSemaphore函数﹐计数增加到2﹐有两个线程被允许恢复执行﹒唤醒两个线程也导致信号量的引用计数器减小到0﹒图14.3. 信号量对象同步系统线程本章的后面将提供一个简单的驱动程序实例﹐在每次增加一个IRP到工作队列它的派遣例程就增加一个信号量计数﹐当线程从队列中移除IRP﹐它将减小计数﹒当队列为空的时候就进入等待状态﹒使用信号量之前﹐必须分配一个KSEMAPHORE结构的存储空间定时器对象定时器(Timer)是一个有超时值的派遣对象﹒当一个定时器被打开﹐它进入静止状态直到时间到﹐这时它转变为发信号状态﹐在前面介绍的定时器对象是用来强制一个CustomTimerDpc例程执行﹐因为它们仅仅是内核派遣对象﹐它们也可以用来在KeWaitForXxx调用中使用﹒图14.4描述了定时器对象的动作﹒线程A开始定时器然后调用KeWaitForSingleObject函数﹐线程阻塞直到定时时间到﹒那时﹐定时器进入发信号状态﹐线程被唤醒﹒表14.8. 操作信号量对象的函数图14.4. 定时器对象同步系统线程定时器对象有两种﹕通知定时器和同步定时器﹒在对象被初始化的时候决定它的类型﹐虽然两种类型的定时器都是在超时时间到的情况下转变为发信号状态﹐但是﹐保持发信号状态的时期是不同的﹒当一个通知定时器到期﹐它保持发信号状态直到它被明确的复位﹒在定时器变为发信号状态的时候﹐所有的等待定时器的线程都被唤醒﹒当一个同步定时器到期﹐它仅仅保持发信号状态到满足一个单个的KeWaitForXxx请求为止的一段时间﹐在这之后﹐定时器对象自动变为静止状态﹒在使用定时器对象之前﹐必须为它分配一个KTIMER结构的存储空间﹒表14.9. 操作定时器对象的函数线程对象系统线程也是有信号状态的派遣对象﹒当一个系统线程终止时﹐它的线程对象从静止状态转变为发信号状态﹐这允许驱动程序通过等待线程对象来同步它的cleanup操作﹒当PsCreateSystemThread函数被调用﹐它返回一个线程对象的句柄﹒调用KeWaitForXxx函数来使用线程对象﹐函数需要一个线程对象的指针而不是它的句柄﹐为了将句柄转变成为指针﹐必须执行下列步骤﹕1. 调用ObReferenceObjectByHandle函数﹒这个函数获得事件对象的指针﹐且将对象的指针引用计数增加1﹒2. 当句柄不再需要的时候﹐调用ZwClose函数将它释放﹒这个函数减小对象的句柄参考计数﹐在指针参考计数加1之后﹐再调用这个函数﹐否则对象将被删除﹒3. 当事件对象不再需要的时候﹐调用ObDereferenceObject函数减少指针参考计数﹐且可能删除事件对象﹒这些函数仅仅在PASSIVE_LEVEL IRQL被调用﹐它限制了驱动程序使用它们的范围﹒互斥对象的变化WIN2000执行部件支持两种互斥对象﹒下面将简要的介绍它们﹒通常使用使用这些对象代替内核互斥对象可以使驱动程序更好的执行﹒快速互斥对象快速互斥对象(Fast Mutex)是同步对象﹐它与内核互斥对象相似﹐只是不允许递归拥有﹒通过移除这个性质来提高它的执行速度﹒快速互斥对象是一个FAST_MUTEX类型的对象﹐它联系一个或者多个需要保护的数据项﹒任何访问这个数据项必须先获得相应的FAST_MUTEX的拥有权﹒注意﹐这些对象有自己的请求拥有权的函数﹒以前的KeWaitForXxx函数不能用来请求快速互斥对象﹒表14.10. 操作快速互斥对象的函数决策资源决策资源(Executive Resource)是一个非常像内核互斥对象的同步对象﹒主要的不同是决策资源可以被一个线程独占或者由多个线程读访问共享﹒因为它可以公共的被多个读操作同时访问一个资源﹐决策资源对象比标准的内核互斥对象提供更大的吞吐量﹒决策资源仅仅是一个ERESOURCE结构类型的对象﹒任何需要访问它保护的数据的操作必须先请求相应的ERESOURCE的拥有权﹒注意这些对象有自己的函数﹒表14.11. 操作决策对象的函数同步死锁死锁可能在任何多个线程同时竞争多个资源的时候产生﹒图14.5. 死锁图解1. 线程A请求资源X﹒2. 线程B请求资源Y﹒3. 线程A请求资源Y﹐所以进入等待线程B释放Y的状态﹒4. 线程B请求资源X﹐所以进入等待线程A释放X的状态﹐结果就是死锁﹒在使用事件﹐互斥对象或者信号量时都可能引起死锁﹒甚至是线程对象也可能引起死锁﹒有两种方法解决死锁问题:1. 使用超时参数的KeWaitForXxx函数来限制等待时间﹒因为这个方法可能有助于发现一个死锁﹐但它不能解决根本的问题﹒2. 强制所有的线程使用固定的顺序获得拥有权﹒如果线程A和B都先访问资源X后访问资源Y﹐就不会产生死锁了﹒互斥对象使用级数﹐提供一些对死锁的防护﹒当一个互斥对象被初始化﹐一个级数就被分配了﹒当一个线程尝试获得互斥对象的时候﹐如果线程拥有任何低的级数的互斥对象的话﹐内核不会给于拥有权﹒通过强制执行这个策略﹐内核避免了涉及多个互斥对象的死锁﹒基于线程的驱动实例这个部分提供基于包的辅助DMA驱动程序的修改版本﹒这个驱动程序的不同是使用一个系统线程来完成大多数的I/O处理﹒结果﹐它使用非常少的时间在DISPATCH_LEVEL IRQL﹐且对其它系统组件没有多少干扰﹒驱动程序怎样工作注意﹐驱动程序没有Start I/O例程﹒当用户模式I/O请求到达的时候﹐驱动程序的一个派遣例程简单的添加与设备对象相关联的IRP到工作队列中﹐然后这个派遣例程调用KeReleaseSemaphore函数增加一个跟踪工作队列中IRPs数目的信号量对象﹐一个非零的信号量计数表示工作队列中有IRP需要处理﹒图14.6. 基于线程的DMA驱动程序构架每个设备对象有自己的处理这些I/O请求的系统线程﹐这个线程在一个KeWaitForSingleObject函数为开始的无限循环中﹐如果信号量对象是一个非零值﹐线程从工作队列中移出IRP和执行这个I/O操作﹐也减小一个计数﹐另一方面﹐如果计数是零﹐线程进入等待状态直到派遣例程插入另一个IRP到队列中﹒当线程需要执行数据传输﹐它开始设备﹐然后使用KeWaitForSingleObject函数来等待事件对象﹐驱动程序的DpcForIsr例程在一个中断到达之后设置事件为发信号状态﹒事件对象有效地使用出列IRP的工作者线程同步了中断服务代码(实际上是DPC)﹒当驱动程序的RemoveDevice例程需要取消系统线程的时候﹐它设置设备Extension中的一个标记﹐并且增加信号量对象的计数﹐它就唤醒和终止线程﹒如果线程在执行I/O操作的过程中﹐只有在完成当前的IRP之后再终止线程﹒DEVICE_EXTENSION结构这个文件包含所有的常用的驱动过程定义的数据结构﹒下面的片断显示管理系统线程和它的工作队列的代码﹒typedef struct _DEVICE_EXTENSION{ ...PETHREAD pThreadObj; // 指向工作者线程对象的指针BOOLEAN bThreadShouldStop; // 这是一个当它被设置为TRUE的时候线程退出的标志KEVENT evAdapterObjectIsAcquired; // 发信号给Adapter对象已经拥有的事件KEVENT evDeviceOperationComplete; // 发最后的操作完成的信号的事件KSEMAPHORE semIrpQueue; // 信号量和自旋锁管理的IRP队列KSPIN_LOCK lkIrpQueue;LIST_ENTRY IrpQueueListHead;} DEVICE_EXTENSION, *PDEVICE_EXTENSION;AddDevice例程这段程序演示了线程对象﹐工作队列和各种各样的用来处理I/O请求的同步对象的初始化代码﹒记得对于每个设备对象﹐AddDevice例程仅仅调用一次﹒NTSTATUS AddDevice( IN PDRIVER_OBJECT pDriverObj, IN PDEVICE_OBJECT pdo ){ ... // 初始化工作队列自旋锁KeInitializeSpinLock( &pDevExt->lkIrpQueue );//初始化工作队列InitializeListHead( &pDevExt->IrpQueueListHead );// 初始化工作队列信号量KeInitializeSemaphore( &pDevExt->semIrpQueue, 0, MAXLONG);// 为Adapter对象初始化时间KeInitializeEvent( &pDevExt-> evAdapterObjectIsAcquired,SynchronizationEvent, FALSE );// 为操作完成初始化事件KeInitializeEvent( &pDevExt->evDeviceOperationComplete,SynchronizationEvent, FALSE );// 初始化工作者线程pDevExt->bThreadShouldStop = FALSE;HANDLE hThread = NULL; // 开始工作者线程status = PsCreateSystemThread( &hThread, (ACCESS_MASK)0, NULL,(HANDLE)0, NULL, WorkerThreadMain, pDevExt );if (!NT_SUCCESS(status)) { IoDeleteSymbolicLink( &linkName );IoDeleteDevice( pfdo ); return status; }// 获得真正的指向线程对象的指针ObReferenceObjectByHandle( hThread, THREAD_ALL_ACCESS,NULL, KernelMode, (PVOID*)&pDevExt->pThreadObj, NULL );ZwClose( hThread ); // 不需要处理... }DispatchReadWrite例程这个例程响应用户的读写设备的请求﹒在检测是否是一个零长度的数据传输之后﹐放置IRP为悬挂状态﹐然后插入到工作队列的信号量对象中﹒注意没有调用IoStartPakcet函数﹐因为没有Start I/O例程﹒NTSTATUS DispatchReadWrite( IN PDEVICE_OBJECT pDO, IN PIRP pIrp ){ PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation( pIrp );PDEVICE_EXTENSION pDE = pDO->DeviceExtension;// 检查是否零字节传输if( pIrpStack->Parameters.Read.Length == 0 ){ pIrp->IoStatus.Status = STATUS_SUCCESS;pIrp->rmation = 0;IoCompleteRequest( pIrp, IO_NO_INCREMENT );return STATUS_SUCCESS; }IoMarkIrpPending( pIrp ); // 开始设备操作// 添加IRP到线程的工作队列ExInterlockedInsertTailList( &pDE->IrpQueueListHead,&pIrp->Tail.Overlay.ListEntry, &pDE->lkIrpQueue );KeReleaseSemaphore( &pDE->semIrpQueue,0, // 没有优先权推进1, //将信号量计数增加1Increment semaphore by 1FALSE ); // 在这个函数后没有WaitForXxxreturn STATUS_PENDING; }Thread.cpp这个模块包含主要的线程函数和需要的管理线程的例程﹒WorkerThreadMain这是IRP的处理引擎﹐它的工作是从设备Extension的工作队列中取出I/O请求﹐并且执行数据传输操作﹐这个函数继续等待新的IRP直到RemoveDevice例程告诉它关闭﹒VOID WorkerThreadMain( IN PVOID pContext ) {PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) pContext;PDEVICE_OBJECT pDeviceObj = pDevExt->pDevice;PLIST_ENTRY ListEntry;PIRP pIrp;CCHAR PriorityBoost;// 工作者线程运行在比用户线程高的优先级KeSetPriorityThread( KeGetCurrentThread(), LOW_REALTIME_PRIORITY );while( TRUE ) // 进入主要的IRP处理循环{ // 等待一个IRP出现在工作队列中或者RemoveDevice例程停止线程KeWaitForSingleObject( &pDevExt->semIrpQueue, Executive,KernelMode, FALSE, NULL );// 检查是否因为设备被移除而导致线程唤醒if( pDevExt->bThreadShouldStop )sTerminateSystemThread(STATUS_SUCCESS);// 它一定是一个真正的请求﹐得到IRPListEntry = ExInterlockedRemoveHeadList(&pDevExt->IrpQueueListHead, &pDevExt->lkIrpQueue);pIrp = CONTAINING_RECORD( ListEntry,IRP, Tail.Overlay.ListEntry );// 处理IRP﹐因为这是一个同步操作﹐所以这个函数没有返回直到移除IRP的时间到PriorityBoost = PerformDataTransfer( pDeviceObj, pIrp );// 释放IRP和回到循环的顶部﹐看是否有另一个正在等待的请求IoCompleteRequest( pIrp, PriorityBoost ); }} // 结束循环KillThread这个函数通知与一个特殊的设备对象相关联的线程该结束了﹐为了简单化﹐这个函数停止和等待直到目标线程完成﹐因此﹐这个函数只能运行在PASSIVE_LEVEL IRQL﹒VOID KillThread( IN PDEVICE_EXTENSION pDE ) {pDE->bThreadShouldStop = TRUE; //设置停止标志// 确定线程被唤醒KeReleaseSemaphore( &pDE->semIrpQueue,0, // 没有优先级推进1, // 将信号量计算增加1TRUE ); // 在本函数之后有WaitForXxx函数调用// 等待线程停止KeWaitForSingleObject(&pDE->pThreadObj, Executive, KernelMode,FALSE, NULL );ObDereferenceObject( &pDE->pThreadObj ); }Transfer.C这个部分包含执行I/O操作的支持例程﹒这部分代码是从基于包的辅助DMA驱动程序继承来的﹐所以只介绍差别大的代码﹒值得注意的是非常具体的Adapter Control或者DpcForIsr例程的工作﹒这些函数仅仅设置事件对象来发线程的数据传输例程可以继续进行的信号﹒PerformDataTransfer这个函数移动整个数据缓冲区到或者从设备﹒这可能包含在没有足够的映射寄存器的情况下将整个传输分成几个独立的设备操作﹒这个例程运行在PASSIVE_LEVEL IRQL而且不返回控制给调用者直到所有的事情做完﹒CCHAR PerformDataTransfer( IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp ){ PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation( pIrp );PDEVICE_EXTENSION pDE = (PDEVICE_EXTENSION) pDevObj->DeviceExtension;PMDL pMdl = pIrp->MdlAddress;ULONG MapRegsNeeded; NTSTATUS status;// 设置I/O方向标志if( pIrpStack->MajorFunction == IRP_MJ_WRITE )pDE->bWriteToDevice = TRUE;else pDE->bWriteToDevice = FALSE;// 设置纪录信息pDE->bytesRequested = MmGetMdlByteCount( pMdl );pDE->bytesRemaining = pDE->bytesRequested;pDE->transferVA = (PCHAR) MmGetMdlVirtualAddress( pMdl );// 清理CPU高速缓冲存储器﹐如果需要KeFlushIoBuffers( pIrp->MdlAddress, !pDE->bWriteToDevice, TRUE );// 计算传输的第一部分的尺寸pDE->transferSize = pDE->bytesRemaining;MapRegsNeeded = ADDRESS_AND_SIZE_TO_SPAN_PAGES(pDE->transferVA, pDE->transferSize ); if( MapRegsNeeded > pDE->mapRegisterCount ){ MapRegsNeeded = pDE->mapRegisterCount;pDE->transferSize = MapRegsNeeded * PAGE_SIZE- MmGetMdlByteOffset( pMdl );}// 获得adapter对象status = AcquireAdapterObject( pDE, MapRegsNeeded );if( !NT_SUCCESS( status )) {pIrp->IoStatus.Status = status;pIrp->rmation = 0;return IO_NO_INCREMENT; }// 尝试执行第一部分的数据传输status = PerformSynchronousTransfer( pDevObj, pIrp ); if( !NT_SUCCESS( status )) {pDE->pDmaAdapter->DmaOperations->FreeAdapterChannel ( pDE->pDmaAdapter );pIrp->IoStatus.Status = status;pIrp->rmation = 0;return IO_NO_INCREMENT; }// 处理完成﹐刷新纪录信息pDE->transferVA += pDE->transferSize;pDE->bytesRemaining -= pDE->transferSize;// 循环完成请求中所有的部分传输操作while( pDE->bytesRemaining >0 ){ // 尝试传输所有的数据pDE->transferSize = pDE->bytesRemaining;MapRegsNeeded = ADDRESS_AND_SIZE_TO_SPAN_PAGES(pDE->transferVA, pDE->transferSize );// 如果一次不能传输所有的数据﹐减小这次传输的数据量if (MapRegsNeeded > pDE->mapRegisterCount) {MapRegsNeeded = pDE->mapRegisterCount;pDE->transferSize = MapRegsNeeded * PAGE_SIZE -BYTE_OFFSET(pDE->TransferVA); }// 尝试执行设备操作status = PerformSynchronousTransfer( pDevObj, pIrp );if( !NT_SUCCESS( status )) break;// 完成﹐为下一次传输刷新纪录信息pDE->transferVA += pDE->transferSize;pDE->bytesRemaining -= pDE->transferSize; }// 在所有的数据传输完成之后﹐释放DMA Adapter对象pDE->pDmaAdapter->DmaOperations->FreeAdapterChannel ( pDE->pDmaAdapter ); // 发送IRP返回给调用者﹐它的最后的状态是最后一次传输操作的状态pIrp->IoStatus.Status = status;pIrp->rmation = pDE->bytesRequested- pDE->bytesRemaining;// 因为至少有一个I/O操作﹐所以﹐给IRP一个优先级推进return IO_DISK_INCREMENT; }AcquireAdapterObject和AdapterControl这两个函数一起给线程获得adapter对象的拥有权设置一个同步机制﹐AcquireAdapterObject函数运行在系统线程的上下文环境中﹐所以它可以停止和等待一个非零的时间间隔﹒static NTSTATUS AcquireAdapterObject( IN PDEVICE_EXTENSION pDE,IN ULONG MapRegsNeeded ){ KIRQL OldIrql; NTSTATUS status;// 为了请求Adapter对象﹐必须运行在DISPATCH_LEVELKeRaiseIrql( DISPATCH_LEVEL, &OldIrql );pDE->pDmaAdapter->DmaOperations->AllocateAdapterChannel ( pDE->pDmaAdapter, pDE->pDevice,MapRegsNeeded, AdapterControl, pDE );KeLowerIrql( OldIrql );// 如果调用失败﹐是因为没有足够的映射寄存器if( !NT_SUCCESS( status )) return status;// 停止和等待Adapter Control例程设置事件对象//这是我们得到Adapter对象的信号KeWaitForSingleObject( &pDE->evAdapterObjectIsAcquired,Executive, KernelMode, FALSE, NULL );return STATUS_SUCCESS; }IO_ALLOCATION_ACTION AdapterControl(IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp, IN PVOID MapRegisterBase, IN PVOID pContext ){ PDEVICE_EXTENSION pDE = (PDEVICE_EXTENSION) pContext;// 存储映射寄存器的句柄﹐线程需要它设置数据传输pDE->mapRegisterBase = MapRegisterBase;// 让线程知道它可以使用Adapter对象。

Windows2000设备驱动程序

Windows2000设备驱动程序

Windows2000设备驱动程序引言:因为工作关系,我经常涉及PC机与外围设备接口的工作,从PC机这方面要做的工作看来,主要是通过接口处理外围设备的中断,通过I/O 端口或内存地址与外设互相传递数据。

从计算机原理的角度看,所要达到的目的很简单,那么如何编写程序完成上述功能呢?当前国内流行的PC操作系统有三种:DOS,Win95/98系列,WindowsNT。

DOS是单用户、单任务操作系统,因为PC机硬件处理速度持续提升,基于单用户、单任务的操作系统越来越不能充分发挥硬件的功能,现在只应用于一些老式PC及其它个别场合,有逐渐被淘汰的趋势;Win95/98系列和WindowsNT属于多任务操作系统,不论从其原理还是界面上看,这两种操作系统都比DOS有着无可比拟的优越性,这两种操作系统虽然在界面和操作上及其相似,但其内部实现的诸多方面有很多区别,有些区别是本质上的。

Win95/98设计目标是针对一般家庭用户,安全性及可靠性存有很多薄弱环节,就可靠性来说,Win95/98系列不能很好的防止多任务环境中某个进程的非法操作导致系统中其它程序甚至整个系统的崩溃,而WindowsNT在这方面及其它诸多方面设计的相当严谨。

这两种操作系统是Microsoft公司同一时期的产品,但针对不同的使用群,所以在一些重要场合及生产实践中应该选择WindowsNT作为计算机的操作系统,此外,从发展趋势来看,WindowsNT已经成为定型产品,具有相对稳定性。

在不同操作系统下编写驱动程序是有很大区别的,在DOS平台上,应用程序和设备驱动程序之间没有标准的接口,它们在外部表现为一个扩展名为EXE的文件,驱动程序的作用被柔和在应用程序中,这样,应用程序为了使用不同厂商的同一类设备,必须了解这些设备在接口上具体的硬件实现,同时,对于一个特定型号的硬件产品,所有支持它的应用软件中对于控制整个设备动作的这部分代码,可能被多次重写。

这种情况不适合硬件及应用软件的飞速发展。

Windows2000设备驱动程序的设计与开发①

Windows2000设备驱动程序的设计与开发①

2004 年 12 月 Dec. 2004
Windo ws 2000 设备驱动程序的设计与开发 ①
梁列全 , 王随平
中南大学 信息科学与工程学院 , 湖南 长沙 410083
摘要 : 在分析 Windows 设备驱动模型 ( WDM) 的基本结构 、设计和开发等基本问题的基础上 , 采用 WDM 技术设计
软件平台为 : Windows 2000 , Visual C + + 61 0 , Window s 2000 DD K 以及调试软件 Sof t ICE 等. 利用 Visual C + + 来编写驱动程序的源代码 , 使用 DD K 的编译环境来对所编代码进行编译发布 , 运 用 Soft ICE 对驱动程序进行分步测试.
① 收稿日期 : 2003 11 04 作者简介 : 梁列全 (1974 ) , 男 , 江苏金湖人 , 工程师 , 硕士研究生 , 主要从事计算机应用技术的研究.
946
西南师范大学学报 (自然科学版) 第 29 卷
(1) I/ O 端口的读写. I/ O 模块中的控制字和状态字的读写都是通过 I/ O 操作完成. 虽然 Window s
STAR T , SERV ICE_ ERROR_NO RMAL , tchDriver Pat h , N ULL , NULL , NULL , NULL , N ULL) ; …
m_ hDevice = : : Create File ( S TR _ COM PD EVNAM E , GEN ERIC _ R EAD | GEN ERIC _
1 Windows 2000 设备驱动程序的基本原理
WDM 是一种模块化 、分层次类型的驱动模型[1 - 3] . 如 图 1 所示 , 其中左边是设备对象栈 (设备对象是系统为帮助 软件管理硬件而创建的数据结构) , 右边是驱动程序的分层 结构. WDM 驱动模型中 , 每个硬件设备至少由两个驱动程 序 (功能驱动程序和总线驱动程序) 组成. 总线驱动程序负 责管理硬件与计算机的连接 , 为总线上的每个设备创建物 理设备对象 PDO ; 功能驱动程序负责初始化 I/ O 设备操作 , 处理 I/ O 设备操作完成时所产生的中断 , 为用户提供一种 适当的的设备控制方式 , 创建自己的功能设备对象 FDO.

Windows2000启动光盘制作-电脑资料

Windows2000启动光盘制作-电脑资料

Windows2000启动光盘制作-电脑资料windows 2000的原版启动光盘制作有两种方法,启动功能是一样的,。

第一种:是用cdrwin来做,它可以做得和原版一模一样,没有多出一个文件(指在WINDOWS下看得见的),但它不可以加入其他引导,也不能做多重引导,详细请看用 CDRWIN制作WINDOWS2000启动光盘。

第二种:是可以做成多重引导,但在光盘上看得到扇区文件。

本文介绍第二种制作方法有关说明:1、本文所说的Windows2000启动功能,是象正版Windows2000光盘那样,开机后按任意键从CD-ROM引导;不按任何键,5秒后从硬盘引导。

2、本文内容来自 Bart Lagerweij 的"example4",由于原文是英文版,读起来不太方便。

特此经简化步骤,稍做修改,写出来让大家更轻松的使用。

3、本光盘制作,不需要准备任何其他刻录软件,在example4中带有刻录软件。

引导特点:1、开机后按任意键从CD-ROM安装,进入Windows2000的安装引导。

系统要求:1、可以在所有的WINDOWS系统下制作,最好在WINDOWS 98下制作。

2、保证C盘有1.3G以上的剩余空间,用来存放要刻录的文件和ISO文件。

如果在其他分区有剩余空间,需自行修改有关文件路径指向。

软件准备:example5制作过程:1、将下载的example5.zip解压后放在C盘根目录下(c:\\example5),example5 的文件结构如下:2、将要刻录的WINDOWS 2000 Professional(服务器版用Cdrom_is.5,高级服务器版用Cdrom_ia.5)及其他需刻录的文件放在disk1目录下(c:\\example5\\disk1\\ 目录将成为光盘的根目录),放好文件后,disk1目录下应增加以下文件:c:\\example5\\disk1\\i386 (windows 2000 的安装目录,i386放在根目录下)……3、双击 findcode.bat,将会出现类似于以下的信息,电脑资料《Windows2000启动光盘制作》(https://www.)。

Windows 2000下PCI设备驱动程序开发

Windows 2000下PCI设备驱动程序开发

用, 微软公 司推 出了全新的 WD M设备驱 动模 式。详细阐述 了借 助工具软件 D i r rs用 Vsa C+ . r eWo , i l +6 0开发 P I v k u C 总线数据采集 卡的 WD 设备驱动程序的过程。 M 关键词 WD M驱 动程序 D i r rs 发工具 r eWok 开 v
Wid w 0 0下 P I 备 驱 动 程 序 开 发 no s 0 2 C 设
钱 宇 红
( 解放军电子工程学 院 安徽 合肥 2 03 ) 30 7


在 Widw no s系统下开发 的硬件 必须编 写相应 的设 备驱 动程序才能正常工作。随着 Widw 00 X no s 0 / P操作系统的广 泛运 2
THE DEVELoP E M NT CIBUS DEⅥ CE DRI oF P VER UNDER I W NDoW S 2 0 0 0
Q a u o g i Y h n n
( l t ncE gneigIstt, e i 3 0 7A hiC i ) Ee r i n i r tueHf 03 ,n u,hn co e n ni e2 a
件工作 的所 有细节 , 始初始化 IO操作 , / 处理 IO操作完成时所 /
境 下对 所开发 的硬件设 备进行控制与访 问的问题 。以往在 D S O
环境 下解 决这些问题 比较简单 , 但是在 Wi o s n w 环境下 , d 为了确
保 系统的安全 ,P C U运行 于保护模 式 , 统一管 理硬 件资源 , 因此 应用程序 代码 不能直接访 问硬件 , 而是要 通过调 用属于 内核 的 设备驱动程序 提供 的各 种服务间接地对硬件资源进行访 问。也 就是说 , Widw 环境下开 发设 备驱动程 序是 目前计算 机硬 在 nos 件设 备开发的人员必须面临 的问题 。 由于设 备驱动程序需要 与操作 系统低层 进行 交互 , 因此不 同的操作 系统 底层 结构有着不同 的设备驱动程序模 型。微软公

Windows 2000下打印机驱动程序的开发

Windows 2000下打印机驱动程序的开发

Windows 2000下打印机驱动程序的开发
田玉敏;燕红锁
【期刊名称】《计算机工程》
【年(卷),期】2002(028)003
【摘要】介绍Windows 2000系统下打印机驱动程序的编写,对Windows打印体系结构与功能、驱动程序组件进行了分析,同时对设备驱动程序开发包DDK,具体编程工具MDT及其使用进行了阐述.
【总页数】3页(P214-216)
【作者】田玉敏;燕红锁
【作者单位】西安电子科技大学计算机外部设备研究所,西安,710071;西安电子科技大学计算机外部设备研究所,西安,710071
【正文语种】中文
【中图分类】TP311.52
【相关文献】
1.坐标变换技术在Windows打印机驱动程序中的应用 [J], 朱长政
2.Windows NT4.0非标准打印机Mini驱动程序的实现方法 [J], 刘斌;王沛;王箭;潘金贵
3.Windows2000/XP下的打印机驱动程序设计 [J], 沈疆海;沈利香
4.从Windows98安装光盘中提取Epson LQ1900打印机驱动程序一例 [J], bigboy
5.WindowsServer2008R2中的打印机驱动程序隔离是什么? [J],
因版权原因,仅展示原文概要,查看原文内容请购买。

Windows98/Me设备驱动程序的编写

Windows98/Me设备驱动程序的编写
Ja i ngBo
( CAEPIsi t fT c n lg , in a gS c u n 6 0 n tu eo e h oo y M a g n ih a , 21 0) t y 9
Ab ta t yas e i ce a l,hsp p r h wsh w s eNu e aVtos 30t r ga i u l e iedi e Vx sr c:B p cf x mp e t i a e o o t u et M g o lD . po r vr a d vc rv r( D) i s o h o m t
# n l d v o lc h i c u e< t o s pl> # e n VI AS d f eDE CE CL i S Vmb e De ie e p vc
VC + ., 它 提 供 了 3 个 工 具 , 即 QucVx 、 +60 i k d
・ 作者简介:蒋 波 (9 3 ) 16 一 ,男,讲师 。0 /1 2 51/ 0
图 1 i V D 启 动 图 c x Qu k

9 — 3
维普资讯
关键词: Wid w 8Me 虚拟设备驱动程序 V D N MeaV o l . no s / 9 x u g tos 3 D 0 中图分类号:T 3 6 P 1 文献标识码 :B
Th o r m m i g o i d ws9 / eDe ieDrv r ePr g a n f W n o 8M v c i e
维普资讯
四川工程职业技术学院 学报 [ 季刊 ]
20 铹 06

Wid ws8Me 备驱 动程序 的编 写 n o 9/ 设
蒋 波
( 中国工程物 理研 究院工学 院 四JI 绵阳 l 6 10 ) 2 9 0

Windows2000内核模式驱动程序设计

Windows2000内核模式驱动程序设计

Windows2000内核模式驱动程序设计!李平,张云麟(重庆邮电学院,重庆400065)摘要:介绍了Windows2000驱动程序模型的基本结构、设计和开发的基本问题;并以PCI接口的ATM信令接口卡开发的驱动程序开发的方法和步骤,介绍了驱动程序开发环境的设置及编译方法。

关键词:Windows2000驱动程序模型;设计;开发中图法分类号:TP391文献标识码:A文章编号:1001-3695(2003)01-0122-03Design for Windows2000Drive Program ModuieLI Ping,ZHANG Yun-iin(Chongging Uniuersity of Posts&Telecommunications,Chongging400065,China)Abstract:In this paper,basic principie,design and expioitation of WDM are discussed,the step of design and the debug of drive program are presented in the end.Aiso giving some of the code of an exampie drive program.Key words:WDM;Design;Debug1引言设备驱动程序是直接同硬件打交道的软件模块。

在Windows2000中,微软公司在Windows NT4.0的驱动程序结构基础上,同时引入了Windows9x的即插即入特性,推出了新的驱动程序结构模式(WDM)。

WDM通过提供一种灵活的方式来简化驱动程序的开发,在实现对新硬件支持的基础上减少并降低所必须开发的驱动程序的数量和复杂性。

在Windows2000中的驱动程序可以分为两个大类:用户模式驱动程序和内核模式驱动程序。

用户模式驱动程序是与子系统特定相关的,它包含了Win32多媒体驱动程序、支持MS-DOS应用程序的虚拟设备驱动程序VDD(Virtuai Device Driver)。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
41
微计算机系统
微计算机系统 void doWrite(int n) // 向驱动程序中写数据 { char *buf; ULONG nWritten; int i, j; buf = (char *) malloc(n); if (buf == NULL) { printf("Failed to allocate buffer for write"); Exit(1); } // start with the mod26 letter of the number of bytes to write j = (n % 26); // load buffer with dummy data (abcdefg...) for (i=0; i<n; i++, j=(j + 1)%26) { buf[i] = 'a' + j; } 42
微计算机系统
驱动程序装载 器,可动态调 用驱动程序
28
微计算机系统
驱动程序监视器界面
29
微计算机系统
驱动程序装载器界面
30
微计算机系统
31
微计算机系统
32
微计算机系统
33
微计算机系统
34
微计算机系统
35
微计算机系统
36
2)完成应用程序和驱动程序之间的信息交换
微计算机系统
下面我们来修改有关代码,以便增加驱动程序和应 用程序之间相互通信的内容。需要增加的内容包括: a. 使用Read和Write方式分别从驱动程序读入字符和 向驱动程序写字符。 b. 使用IO控制代码方式分别从驱动程序读入字符和 向驱动程序写字符。 c. 使用IO控制代码方式向驱动程序写字符串再从驱动 程序中读出该字符串,并返回反馈串信息。 注意:程序中暗红色显示的部分是我们添加或修改 过的语句,其他是DriverWorks自动生成的。语句中 “t<< xxxxx”这样的语句是向调试软件输出信息,该 信息可以再DriverMonitor或其他调试监视器中看到。
public:
DEVMEMBER_DISPATCHERS virtual NTSTATUS OnStartDevice(KIrp I); virtual NTSTATUS OnStopDevice(KIrp I); virtual NTSTATUS OnRemoveDevice(KIrp I); virtual NTSTATUS DefaultPnp(KIrp I); virtual NTSTATUS DefaultPower(KIrp I); virtual NTSTATUS OnDevicePowerUp(KIrp I); virtual NTSTATUS OnDeviceSleep(KIrp I); void SerialRead(KIrp I); void SerialWrite(KIrp I);
23
下面我们讲解编译、执行和调试这个驱动程序。
先编译驱动程 序工程
微计算机系统
在VC的集成环境中
24
微计算机系统
25
微计算机系统
再编译测试应 用程序工程
26
微计算机系统
27
下面使用DriverStudio带的工具加载驱动程序和查看 调试信息。 驱动程序监视,可实 时看到驱动程序发出 的调试输出语句
40
printf("%d bytes read from device (%d requested).\n", nRead, nRead); // Print what was read while(i < nRead) { 这几句删除 // j = min((i+26),n); // for(; i < j; i++) // { // printf("%c, ", buf[i]); // } // printf("\n"); printf("%c, ",buf[i++]); } printf("\n"); free(buf); }
17
微计算机系统
驱动类 设备类
18
微计算机系统
驱动类文件 设备类文件
驱动安装 指导文件 测试用的控制 台程序文件
19
微计算机系统
此时已经具备了一个驱动程序以及做测试 用的应用程序的基本框架,我们可以在VC集 成环境下修改有关程序,增加相关的具体操 作代码,然后就可以编译和调试了。
20
微计算机系统
该驱动程序框架包含了几个最基本的类,这些类是:
class Sample : public KDriver // 驱动程序类,用于初始化驱动程序 { SAFE_DESTRUCTORS public: // 以下成员函数注意和WDM中有关例程联系起来看 virtual NTSTATUS DriverEntry(PUNICODE_STRING RegistryPath); virtual NTSTATUS AddDevice(PDEVICE_OBJECT Pdo); void LoadRegistryParameters(KRegistryKey &Params); int m_Unit;
6
微计算机系统
驱动类 名称 驱动类 文件名
7
微计算机系统
选择需要 处理的消 息句柄
8
微计算机系统
9
微计算机系统
10
微计算机系统
11
微计算机系统
添加和应用程 序之间通信的 控制代码
12
微计算机系统
13
微计算机系统
14
微计算机系统
测试用应用 程序名称
15
微计算机系统
16
微计算机系统
Test_Sample.cpp
微计算机系统
void doRead(int n) // 从驱动程序中读数据 { char *buf; ULONG nRead; int i, j; buf = (char *) malloc(n); if (buf == NULL) { printf("Failed to allocate buffer for read"); Exit(1); } // Read data from driver printf("Reading from device - "); ReadFile(hDevice, buf, n, &nRead, NULL); // 参数分别是设备句柄、输入缓冲地址、缓冲大小(字 节数)、实际读的数据字节数、覆盖结构指针。
22
微计算机系统 NTSTATUS SAMPLE_IOCTL_Read_Handler(KIrp I); NTSTATUS SAMPLE_IOCTL_Write_Handler(KIrp I); NTSTATUS SAMPLE_IOCTL_ReadWrite_Handler(KIrp I); #ifdef _COMMENT_ONLY virtual NTSTATUS Create(KIrp I); virtual NTSTATUS Close(KIrp I); virtual NTSTATUS DeviceControl(KIrp I); virtual NTSTATUS SystemControl(KIrp I); virtual NTSTATUS Read(KIrp I); virtual NTSTATUS Write(KIrp I); #endif // Member Data protected: // Unit number for this device (0-9) ULONG m_Unit; KPnpLowerDevice m_Lower; SampleDevice_DriverManagedQueue m_DriverManagedQueue; // TODO: Create additional driver managed queues. These might be // of the same class (SampleDevice_DriverManagedQueue), // or you might choose to derive another class. };
37
a. 使用Read和Write方式分别读写 SampleDevice.cpp
微计算机系统
void SampleDevice::SerialRead(KIrp I) { t << "Entering SampleDevice::SerialRead, " << I << EOL; NTSTATUS status = STATUS_SUCCESS; PUCHAR pBuffer = (PUCHAR) I.BufferedReadDest();//取得返回数据 BUFF的指针 ULONG dwTotalSize = I.ReadSize(CURRENT); // Requested read size char buff[512]; int n =512, j = (n % 26); for (int i=0; i<n; i++, j=(j + 1)%26) { buff[i] = 'a' + j; } buff[dwTotalSize]=‘\0’; //指定串尾 strcpy((char *)pBuffer,buff); // 把给应用程序的数据拷贝给返回BUFF t << “The string you will read is \”“ << buff << ”\“” << EOL; // 输出调试信息 ULONG dwBytesRead = strlen(buff); // Count of bytes read rmation() = dwBytesRead; // 返回给应用程序的信息的字节个数 I.Status() = status; m_DriverManagedQueue.PnpNextIrp(I); }
相关文档
最新文档