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)。

将驱动集成到Win98中2

将驱动集成到Win98中2

将驱动集成到Win98中即使有了“驱动精灵”,笔者仍觉得不太爽快,因为我们必须在重装系统后,首先去下载并安装“驱动精灵”这个程序,然后才能还原驱动程序。

假如由于某些原因本机以前备份的驱动程序被破坏,那就没办法了。

其实,我们可以利用Windows 98安装光盘中的“硬件信息安装程序(infinst.exe)”将相应设备的驱动程序集成到Windows安装文件中,这样就相当于手工更新了Windows 98安装程序中的硬件驱动程序,在安装操作系统时就可以自动安装这些设备的驱动程序,你自然也就不需要再去东翻西找驱动程序盘了。

1.复制Windows 98安装文件首先,我们需要将Windows98的所有安装文件全部复制到硬盘上的某个目录中,例如g:tempwin98文件夹。

不过,笔者这里要提醒的是,请朋友们将g:tempwin98文件夹下的所有文件、文件夹的只读属性去掉,也就是说用户必须拥有写入权限,否则将无法更新安装文件。

2.复制硬件设备驱动程序例如,我们将ASUS显卡的驱动程序集成到Windows 98安装文件中,那么请先将驱动程序全部复制到硬盘上,例如g:tempdrivers。

3.选择相应文件从Windows 98安装光盘的toolsreskitinfinst文件夹下找到infinst.exe文件,双击运行即可启动“Inf安装程序”,当然你也可以将它复制到硬盘上再运行。

运行infinst.exe后,点击“添加到Windows98安装程序的Inf”选项中的“浏览”按钮,选择g:\temp\drivers\Asusnv9x.inf文件;再点击“Windows 98 Setup.exe”小节中的“浏览”按钮,选择“g:\temp\win98\setup.exe”文件,注意这里都必须选择完整的绝对路径,否则会出错。

集成驱动程序现在,点击“添加Inf A ”按钮,即可将驱动程序集成到Windows 98的安装文件中。

windows驱动开发教程

windows驱动开发教程

windows驱动开发教程Windows驱动开发是指在Windows操作系统下编写、调试和部署驱动程序的过程。

驱动程序是操作系统的核心组成部分,它负责与硬件设备通信,使得操作系统能够正确地识别、管理和控制硬件设备。

在本教程中,我们将介绍Windows驱动开发的基本概念、工具和流程。

首先,为了进行Windows驱动开发,我们需要准备好相应的开发工具。

其中最重要的工具是Windows Driver Kit(WDK),它包含了用于驱动开发的各种工具和库文件。

我们可以从微软官方网站上下载并安装最新版本的WDK。

接下来,我们需要熟悉驱动程序的基本概念。

在Windows中,驱动程序可以分为内核驱动和用户模式驱动。

内核驱动运行在操作系统的内核空间,具有更高的权限和更广泛的硬件访问能力;而用户模式驱动则运行在用户空间,通过系统调用与内核驱动进行通信。

我们需要了解如何编写和编译这两种类型的驱动程序,并了解它们的工作原理和特点。

在编写驱动程序之前,我们还需要了解一些基本的Windows内核编程概念,例如驱动对象模型(Driver Object Model)、设备对象模型(Device Object Model)和驱动程序接口(Driver Interface)。

这些概念是驱动程序的基础,对于理解和设计驱动程序非常重要。

接下来,我们将介绍如何使用WDK的工具和库文件来编写驱动程序。

我们可以使用Visual Studio编写驱动程序的源代码,并使用WDK的编译工具将源代码编译成驱动程序二进制文件。

在编译过程中,我们需要配置驱动程序的环境和依赖项,并确保编译成功。

在编写和编译驱动程序之后,我们需要进行驱动程序的调试和部署。

对于驱动程序的调试,我们可以使用WDK提供的调试工具和技术,例如Kernel-Mode Debugging和WinDbg。

对于驱动程序的部署,我们需要将驱动程序二进制文件和相关的配置文件复制到操作系统的指定目录,并注册驱动程序的信息到操作系统的驱动程序数据库。

ndows98驱动,Windows982000驱动程序编写方法

ndows98驱动,Windows982000驱动程序编写方法

ndows98驱动,Windows982000驱动程序编写⽅法标签:Windows98Windows982000驱动程序编写⽅法Windows98Windows98/2000驱动程序编写⽅法1. 驱动程序的开发环境对于VxD的开发,需要的开发环境是:Visual C++ 5.0/6.0Windosw 95DDK 如果想加快开发步骤,建议使⽤第三⽅的VToolsD开发⼯具,它将DDK的东西全部封装成C++的类,可以直接⽤VisualC++编写程序,⽽⽆须使⽤汇编。

⽽且它提供的 QuickVxd能够⽅便快速地建⽴VxD程序的框架。

对于WDM的开发,⼜分⼏种情况:对于Windows 98系统Visual C++ 5.0Windows 98DDK2)对于Windows Me/2000Visual C++ 6.0Windows 2000DDK3)对于Windows XPVisual C++6.0/.netWindows XPDDK 同样,为了⽅便起见,也可以使⽤第三⽅的开发⼯具DriverWorks,它也是将DDK的内容封装成类,⽽且提供⼀个快速⽅便地⽣成驱动框架的⼯具。

2. 驱动程序开发⼯具包DriverStudio2.1 DriverStudio 2.7所包含的⼯具 VToolsD VToolsD 是⼀个⽤来开发针对Win9X (Windows 95 和 Windows 98)操作系统下设备驱动程序(VxD)的⼯具。

VToolsD 中包括⽣成驱动程序源代码的⼯具,run-time 和 interface 库,以及⼀些驱动程序样本,可以⽤来作为各种类型的设备驱动程序的基础部分。

DriverWorks DriverWorks对于Windows NT下和 Windows 98 与 Windows 2000共同⽀持的Win32驱动模型(WDM)设备驱动程序的开发提供完全的⽀持。

DriverWorks中包含⼀个⾮常完善的源代码⽣成⼯具(DriverWizard) 以及相应的类库和驱动程序样本,它提供了在C++下进⾏设备驱动程序开发的⽀持。

wdm驱动

wdm驱动

wdm驱动
WDM驱动是指Windows Driver Model(Windows驱动模型)驱动,它是一种用于开发Windows操作系统设备驱动程序的软件框架。

WDM驱动是为Windows 98、Windows Me和Windows 2000引入的,它提供了一种标准化的方式来编写设备驱动程序。

相比于传统的驱动开发方式,WDM驱动具有更丰富的功能和更高的灵活性。

WDM驱动在设备驱动程序的开发中提供了许多优点,包括:
1. 简化了驱动程序的开发:WDM驱动使用一套统一的API,使得驱动程序的开发更加简单和一致。

2. 可移植性:WDM驱动可以在不同的Windows操作系统版本上运行,而无需进行大量的修改。

3. 兼容性:WDM驱动允许旧的驱动程序与新的设备完全兼容,而无需进行修改。

4. 性能:WDM驱动通过使用更高级的设备驱动模型和硬件访问机制,提高了设备驱动程序的性能。

总之,WDM驱动是一种用于开发Windows设备驱动程序的软件框架,它提供了许多优势和便利,使开发者能够更轻松地开发高性能和兼容性强的驱动程序。

研华设备驱动程序编程使用介绍

研华设备驱动程序编程使用介绍

研华设备驱动程序编程使用介绍(本文件内容适用于所有数字量和模拟量采集卡)这一部分包括了创建DA&C应用的基本信息,介绍了设备驱动程序文件的特点,讲解了基本的使用方法。

•Visual C++ 5.0或更高版本•Visual Basic5.0或更高版本•Delphi4.0或更高版本•Borland C++ 5.0或C++ builder 1.0或更高版本如果您正在使用的开发工具不是上面所述的几种,请仔细查阅您所使用的工具的文档,了解该工具如何调用动态连接库来创建应用程序。

Windows 95/98/NT/2000的32位DLL的函数库是动态连接,这就是说动态连接库并不直接编译到应用程序的可执行文件中,而只是将动态连接库的路径信息保存在可执行文件中。

设备驱动程序只是在可执行程序执行过程中动态连接库被调用的时候才被连接到应用程序。

导入库(*.LIB)包含了它们它们的动态连接库定义的外部函数。

它们隐含了动态连接库的位置。

如何为您的应用程序引入动态连接库的路径信息,例如:是通过导入函数库还是通过函数声明的方法,取决于您所使用的编程工具。

使用函数原型是良好的编程习惯,这也是为什么设备驱动程序总是被打包成函数原型形式。

安装工具为您选择的的开发工具拷贝正确的的原型文件。

如果你不打算使用研华驱动程序支持的开发工具,那么您需要创建您自己的函数原型。

按照下面的步骤开始对研华的硬件设备编程应用:这一部分我们推荐您采用研华的设备驱动程序结合微软的可视化平台来开发您的系统!研华设备驱动程序支持Visual C++ 5.0及其以上的版本。

要使用DA&C函数,需要按照下面的步骤使用研华驱动程序提供的DLL(动态连接库,Dynamic Linked Library)1.象编写一般的Windows应用程序一样创建您的应用程序代码,在这些代码中可以象典型的函数调用一样调用研华驱动程序中提供的动态连接库中(DLL)的函数。

手工打造自己的PNP无盘Windows98

手工打造自己的PNP无盘Windows98

手工打造自己的PNP无盘Windows98现在以名智为代表无盘集成系统在国内流行很广,很多网吧、学校都在应用它们系统,只需要ghost 服务器的硬盘,工作站使用相同的网卡,完全不同配置的机器都能即插即用启动无盘win98,自动寻找适用驱动,并且内置很多的游戏与应用软件,自从有了此类的系统,好多当年的老朋友都不再去继续研究手工无盘的安装,也很少有普通用户还会自己去从头到尾一步步安装了无盘网了,但曾起何时我们用NT+win95(netsetup) 做RPLwin95无盘时几多麻烦,2000年pxe 技术初现国内时论坛里大家多么的兴奋,现在想起来还是让我回味那份激情。

其实,以名智为代表这些集成化系统,虽然使用非常方便,但是公司出于商业利益很自然对其系统做过很多加密限制,工作站系统中也多处都被加入了强制性广告信息,以及应用软件更新添加的繁锁,也给用户的使用带来了诸多的不便。

特别是他们现在推出的新系统需要使用网上在线注册,破解后系统稳定性大大折扣。

这些正是目前存在的问题。

如果你是一个无盘爱好者,是否想过自己安装一套无盘系统呢,就像名智一样可以即插即用,实际做到这点并不难,本站从2000年开办至今,介绍有关无盘rpl /pxe方面的文章不下十几篇,除了使用传统的安装方法外,我们还可以借助国内一家专用软件如:启明星、华教、等纯手工订制安装自己的PNP无盘网。

应网友们的要求,本人将以前分散资料与各大论坛上精贴进行了重新的整理结合自己几年来的经验,近日从头到尾的安装了一个小型网络无盘,将每个过程界面截图保存下来,写成安装详解提供给广大的无盘爱好者,整个安装过程均没有使用现有的国内专用软件与自编程序,所涉及软件均为PXE 与RPL 中原厂自带的,可以方便广大朋友们自己安装一套“知根知底”系统。

本文将分星成6篇在一个星期内连载完成,但原是目前为止最完整、最公开的。

文章所涉的一批批处理文件与注册表键值文件,我们都提供免费下载。

学会编写自己的电脑驱动程序

学会编写自己的电脑驱动程序

学会编写自己的电脑驱动程序在当今数字化和信息技术高速发展的时代,电脑已经成为人们生活中不可或缺的一部分。

无论是个人用户还是企业组织,都离不开电脑和各种软件应用。

而作为电脑的核心组件之一的驱动程序,更是保证了电脑正常运行的重要角色。

学会编写自己的电脑驱动程序,不仅可以增加技术实力,还能满足个性化需求。

本文将介绍学习编写自己的电脑驱动程序的基本步骤和方法。

一、了解电脑驱动程序的基础知识在开始编写电脑驱动程序之前,我们需要了解一些基础知识。

首先,了解驱动程序的定义和作用,它是一种软件,用于控制硬件设备与操作系统之间的通信。

其次,熟悉不同操作系统的体系结构和相关编程语言,如Windows、Linux和macOS等。

这些操作系统都提供了开发驱动程序的工具和接口,我们可以根据自己的需求选择相应的平台进行学习和开发。

二、选择合适的开发工具和环境针对不同的操作系统和硬件设备,选择合适的开发工具和环境是必要的。

对于Windows系统,我们可以使用微软提供的Windows Driver Kit(WDK)来进行驱动程序开发。

它提供了丰富的开发工具和文档,可以帮助我们编写出高质量的驱动程序。

而对于Linux和macOS系统,其内核源代码是公开的,我们可以直接在操作系统的开发环境中进行驱动程序的编写。

三、学习驱动程序开发的基本原理和技术在进入实际编写驱动程序之前,我们需要学习一些基本的原理和技术。

首先,了解驱动程序的工作原理和运行机制,掌握操作系统提供的相关API函数和概念。

其次,学习设备驱动的特性和编程模型,了解设备驱动程序如何与硬件设备进行通信和控制。

此外,学会使用调试工具和技术,以便在开发过程中及时发现和修复错误。

四、编写和调试自己的电脑驱动程序在前面的准备工作完成后,我们可以开始编写自己的电脑驱动程序了。

首先,根据自己的需求和目标选择合适的驱动程序类型,如文件系统驱动、网络驱动、USB驱动等。

其次,在选定的开发环境中创建项目,包括设备驱动源代码、配置文件和相关的测试数据。

【驱动笔记1】第一个驱动程序

【驱动笔记1】第一个驱动程序

【驱动笔记1】第一个驱动程序学习各种高级外挂制作技术,马上去百度搜索"魔鬼作坊",点击第一个站进入,快速成为做挂达人。

驱动程序的开发,向来是令人感到有所畏惧的,可能很多人像我一样,看了很久却还是一头雾水,不得其门而入。

我们今天就通过一个简单的程序来使读者学会初步的驱动程序开发。

在开发Windows驱动程序之前,我们需要首先安装DDK,Win98及其以前的VxD我们就不要再考虑了;Windows2000DDK也比较老了点,很少有人使用了,微软最新的WDK貌似都不支持2000了;在本文中假设我们已经安装了Win XP DDK或2003DDK(2003会包括XP)。

驱动开发通常有两种环境,一种是使用任意文本编辑器来编写代码,然后通过编写makefile 和sources文件在WinDDK的命令行环境下使用build命令编译;另一种方法是使用各种各样的方法以图可以利用IDE的环境来搭建驱动框架,比如使用驱动向导文件或一些小工具,我经常使用的是EasySys这个小工具。

废话不多说了,假设我们已经通过EasySys生成了一个驱动框架,现在我们就向里面添加代码。

下面我们编写了一个函数,它的作用是根据形参来创建一个文件,形参中给出了将要被创建的文件完整路径。

(注意,以下程序有许多错误,具体请看后面的讲解)BOOL CreateFileTest(IN PUNICODE_STRING FileName){BOOL bRet=FALSE;HANDLE hFile=NULL;NTSTATUS status;IO_STATUS_BLOCK Io_Status_Block;//初始化文件路径OBJECT_ATTRIBUTES obj_attrib;InitializeObjectAttributes(&obj_attrib,&FileName,OBJ_CASE_INSENSITIVE| OBJ_KERNEL_HANDLE,NULL,NULL);//创建文件status= ZwCreateFile(hFile,GENERIC_ALL,NULL,Io_Status_Block,NULL,FILE_ATTRIBUTE_NORMA L,FILE_SHARE_READ,FILE_CREATE,FILE_NON_DIRECTORY_FILE| FILE_SYNCHRONOUS_IO_NONALERT,NULL,0);if(Io_Status_rmation!=FILE_CREATED){bRet=FALSE;}else{bRet=TRUE;}if(hFile){ZwClose(hFile);}return bRet;}当我们激动地编译上述程序时,却很郁闷地发现编译器报出了十几个错误,不要急,这是大多数新手都必须要跨过的一个坎儿。

Windows驱动编程入门

Windows驱动编程入门

Windows驱动编程入门1前言我经常在网上遇到心如火燎的提问者。

他们碰到很多工作中的技术问题,是关于驱动开发的。

其实绝大部分他们碰到的―巨大困难‖是被老牛们看成初级得不能再初级的问题。

比如经常有人定义一个空的UNICODE_STRING,然后往里面拷贝字符串。

结果无论如何都是蓝屏。

也有人在堆栈中定义一个局部SPIN_LOCK,作为下面的同步用——这样用显然没有任何意义。

我无法一一回答这些问题:因为往往要耐心的看他们的代码,才能很不容易的发现这些错误。

而且我又不是总是空闲的,可以无休止的去帮网友阅读代码和查找初级错误。

但是归根结底,这些问题的出现,是因为现在写驱动的同行越来越多,但是做驱动开发又没有比较基础的,容易读懂的资料。

为此我决定从今天开始连载一篇超级入门级的教程,来解决那些最基本的开发问题。

老牛们就请无视这篇教程,一笑而过了。

Windows驱动编程基础教程(1.1-1.3)1.1 使用字符串结构常常使用传统C语言的程序员比较喜欢用如下的方法定义和使用字符串:char *str = { ―my first string‖ };// ansi字符串wchar_t *wstr = { L‖my first string‖ };// unicode字符串size_t len = strlen(str); // ansi字符串求长度size_t wlen = wcslen(wstr); // unicode字符串求长度printf(―%s %ws %d %d‖,str,wstr,len,wlen);// 打印两种字符串但是实际上这种字符串相当的不安全。

很容易导致缓冲溢出漏洞。

这是因为没有任何地方确切的表明一个字符串的长度。

仅仅用一个‘\0‘字符来标明这个字符串的结束。

一旦碰到根本就没有空结束的字符串(可能是攻击者恶意的输入、或者是编程错误导致的意外),程序就可能陷入崩溃。

使用高级C++特性的编码者则容易忽略这个问题。

  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); }
相关文档
最新文档