欢迎来到三一办公! | 帮助中心 三一办公31ppt.com(应用文档模板下载平台)
三一办公
全部分类
  • 办公文档>
  • PPT模板>
  • 建筑/施工/环境>
  • 毕业设计>
  • 工程图纸>
  • 教育教学>
  • 素材源码>
  • 生活休闲>
  • 临时分类>
  • ImageVerifierCode 换一换
    首页 三一办公 > 资源分类 > DOC文档下载  

    驱动程序设计课程大作业键盘过滤驱动程序设计.doc

    • 资源ID:2386205       资源大小:90.50KB        全文页数:11页
    • 资源格式: DOC        下载积分:8金币
    快捷下载 游客一键下载
    会员登录下载
    三方登录下载: 微信开放平台登录 QQ登录  
    下载资源需要8金币
    邮箱/手机:
    温馨提示:
    用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)
    支付方式: 支付宝    微信支付   
    验证码:   换一换

    加入VIP免费专享
     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    驱动程序设计课程大作业键盘过滤驱动程序设计.doc

    驱动程序设计课程大作业键盘过滤驱动程序设计班级:姓名: 学号:2009年2月11日一、主要设计思路目标:键盘过滤驱动。利用驱动分层机制,使用过滤驱动捕获键盘的扫描码并保存下来;应用程序定时访问驱动程序取回扫描码,转换成相应的按键名称并显示;通过应用程序设定按键映射,应用程序将指令传送给驱动程序,以实现将指定的按键消息转换成其他按键。应用程序驱动程序显示按键设置映射读扫描码设置映射捕获扫描码 键盘过滤驱动是工作在异步模式下的。系统为了得到一个按键操作,首先要发送一个IRP_MJ_READ消息到驱动的设备栈,驱动收到这个IRP后,会一直保持这个IRP为未确定(pending)态,因为当时并没有按键操作。直到一个键被真正的按下,驱动此时就会立刻完成这个IRP,并将刚按下的键的相关数据做为该IRP的返回值。在该IRP带着对应的数据返回后,操作系统将这些值传递给对应的事件系统来处理,然后系统紧接着又会立刻发送一个IRP_MJ_READ请求,等待下次的按键操作,重复以上的步骤。为了实现截获键盘消息,需要在过滤驱动程序中创建一个挂接到物理键盘设备上层的过滤驱动设备。系统发送的IRP_MJ_READ消息会首先到达过滤驱动设备,这样就可以有机会给IRP_MJ_READ设置指定的完成例程,然后将消息下传给物理键盘设备。当有按键动作发生时,IRP_MJ_READ消息在完成后就会调用指定的完成例程,这时就可以在完成例程中读出键盘动作的内容,或者修改这些信息,以实现按键的映射。键盘设备过滤设备IRP_MJ_READ设置完成例程完成读扫描码标准的按键扫描码和ASCII码没有直接的对应关系,大部分按键的扫描码为一个字节;部分功能键为两个字节,且都以0xE0为高字节。但实验中发现,IRP中返回的按键信息和标准的扫描码并不全等。在KEYBOARD_INPUT_DATA结构中,MakeCode字段仅包含了一个字节的编码,还要同时参照Flags字段的内容才能判断出按键的扫描码。下表是KEYBOARD_INPUT_DATA结构中两个字段的内容与其所代表的按键动作的对应关系。当Flags=0或1时,说明按下的按键是扫描码为一个字节的按键;若Flags=2或3,则说明按下的是扫描码为两个字节的按键,而MakeCode中只保留扫描码的低字节。FlagsMakeCode动作0等于扫描码按下1等于扫描码松开2扫描码的低字节按下3扫描码的低字节松开若使用指定的内容改写返回值中的KEYBOARD_INPUT_DATA结构,就可以改变按键的作用,实现按键映射的功能。除了IRP_MJ_READ以外,对于其他发送给键盘设备的消息,到达过滤驱动设备时就可以不做处理,直接下传给键盘设备,以保证系统的正常工作。在完成例程中将每次捕获得到的扫描码保存起来,应用程序每隔一定的时间(100ms)读取一次并将其清空,再根据扫描码查表得到相应按键的名称,这样就可以做到在应用程序中实时的显示键盘动作。用户在应用程序中设定好按键映射的对应关系后,可以通过IRP_MJ_DEVICE_CONTROL消息将映射关系发送给过滤驱动程序,还是在完成例程中实现按键的映射替代。二、 模块的划分、实现及说明具体实现分为驱动程序和应用程序两大部分。驱动程序用C和WindowsXP DDK实现,应用程序通过VC+ 6.0基于MFC实现。在调试和测试中使用了DriverMonitor和Dbgview等工具。下面分别介绍其中主要部分的实现:(一)驱动程序部分1. DEVICE_EXTENSION的定义/定义设备扩展对象typedef struct _DEVICE_EXTENSION PDRIVER_OBJECT pDriver;/对应的驱动PDEVICE_OBJECT pDevice;/驱动对应的设备对象PDEVICE_OBJECT pKBDevice;/挂接到的键盘设备UNICODE_STRING ustrDeviceName;/设备名称UNICODE_STRING ustrSymLinkName;/符号链接名ULONG IrpPendingCount;/运行中的IRP数量ULONG LastScanCode;/最近获得的键盘扫描码ULONG SetCode2;/按键映射规则:SetCode0->SetCode1 DEVICE_EXTENSION, *PDEVICE_EXTENSION;2. DriverEntry主要任务是填写MajorFunction数组、设置卸载例程,并调用CreateDevice函数。对所关心的一些消息分别设置回调函数,为其他消息设置通用处理函数。/通用事件处理例程for (i=0; i<=IRP_MJ_MAXIMUM_FUNCTION;i+)pDriverObject->MajorFunctioni = KeyFilter_DispatchGeneral;/指定卸载驱动例程pDriverObject->DriverUnload = KeyFilter_UnLoad;/捕获IRP_MJ_READ消息pDriverObject->MajorFunctionIRP_MJ_READ = KeyFilter_DispatchRead;/与应用程序通讯pDriverObject->MajorFunctionIRP_MJ_DEVICE_CONTROL = KeyFilter_DeviceIOControl;pDriverObject->MajorFunctionIRP_MJ_CREATE = KeyFilter_OnFileCreate;pDriverObject->MajorFunctionIRP_MJ_CLOSE = KeyFilter_OnClose;3. DispatchGeneral对于不关心的那些消息,返回成功值并传递给下一层的键盘设备,本层不作处理。/将消息传递到下一个单元IoCopyCurrentIrpStackLocationToNext(pIrp);/将IRP下发给键盘设备status = IoCallDriver(pDevExt->pKBDevice, pIrp);return status;4. CreateDevice建立设备对象,初始化DEVICE_EXTENSION结构,建立符号链接,将过滤驱动设备挂接到物理键盘设备之上。/设备名称RtlInitUnicodeString(&devName, L"DeviceKeyFilterDriver");/要挂接的设备RtlInitUnicodeString(&HookdevName, L"DeviceKeyboardClass0");/符号链接RtlInitUnicodeString(&symLinkName,L"?KeyFilterDriver");/建立键盘类设备status = IoCreateDevice(pDriverObject,/驱动程序对象sizeof(DEVICE_EXTENSION),/要求的设备扩展的大小&devName,/设备名称FILE_DEVICE_KEYBOARD,/设备的类型0,/指示可删除介质、只读等。FALSE,/非独占访问方式&HookpDeviceObject);/返回的设备对象if(!NT_SUCCESS(status)/创建设备失败,输出调试信息DbgPrint("KeyFilter: Keyboard hook failed to create device!n");return status;/对设备进行必要的初始化HookpDeviceObject->Flags |= DO_BUFFERED_IO;pDevExt = (PDEVICE_EXTENSION)HookpDeviceObject->DeviceExtension;pDevExt->pDevice = HookpDeviceObject;pDevExt->pDriver = pDriverObject;pDevExt->ustrDeviceName = devName;pDevExt->ustrSymLinkName = symLinkName;pDevExt->IrpPendingCount = 0;pDevExt->SetCode0 = pDevExt->SetCode1 = 0;/挂接过滤设备到DeviceKeyboardClass0设备的上层status = IoAttachDevice(HookpDeviceObject, &HookdevName, &kbdDevice);if(!NT_SUCCESS(status)/连接失败,输出调试信息DbgPrint("KeyFilter: Connect with keyboard failed!n");/删除设备IoDeleteDevice(HookpDeviceObject);return status;pDevExt->pKBDevice = kbdDevice;/创建符号链接status = IoCreateSymbolicLink(&symLinkName,&devName);if(!NT_SUCCESS(status)DbgPrint("KeyFilter: Create SymbolicLink failed!n");IoDeleteDevice(HookpDeviceObject);return status;5. DispatchRead在收到IRP_MJ_READ的IRP后,为其设置指定的完成例程ReadComplete,然后再将IRP发送给物理键盘设备。/获取当前IRP包堆栈指针currentIrpStack = IoGetCurrentIrpStackLocation(pIrp);/传递到下一个单元IoCopyCurrentIrpStackLocationToNext(pIrp);/设置完成例程IoSetCompletionRoutine(pIrp, KeyFilter_ReadComplete, pDeviceObject, TRUE, TRUE, TRUE);/将IRP下发给键盘设备status = IoCallDriver(pDevExt->pKBDevice, pIrp);6. ReadComplete在IRP完成时会调用ReadComplete例程,此时用KEYBOARD_INPUT_DATA结构读取IRP中的返回值,得到按键事件的扫描码并保存。同时若符合按键映射规则,则改写IRP中的返回值,实现按键的替换。/获取当前IRP包堆栈指针pIrpSp = IoGetCurrentIrpStackLocation(pIrp);if(NT_SUCCESS(pIrp->IoStatus.Status)/获得按键数据KeyData = pIrp->AssociatedIrp.SystemBuffer;DbgPrint("Flag: %d Code:0x%04Xn", KeyData->Flags,KeyData->MakeCode);/根据Flags和MakeCode字段生成扫描码switch(KeyData->Flags)case 0:case 1:KeyCode = KeyData->MakeCode;break;case 2:case 3:KeyCode = 0xE000 + KeyData->MakeCode;break;case 4:case 5:KeyCode = 0xE100 + KeyData->MakeCode;break;default:KeyCode = 0;break;/实现按键映射if(KeyCode = pDevExt->SetCode0)KeyCode = pDevExt->SetCode1;/写回IRPKeyData->MakeCode = (USHORT)KeyCode;/只记录键盘按下事件if(KeyData->Flags=0 | KeyData->Flags=2 | KeyData->Flags=4)pDevExt->LastScanCode = KeyCode;7. DeviceIOControl实现与应用程序之间的通信。当应用程序通过DeviceIoControl读扫描码时,利用IRP中的IoStatus.Information字段返回一个ULONG类型的扫描码。当应用程序设定按键映射规则时,同样使用DeviceIoControl通过系统内存缓冲区传递两个ULONG型的扫描码到pDevExt->SetCode数组中。/得到当前堆栈currentIrpStack = IoGetCurrentIrpStackLocation(pIrp);/得到控制码IOControlCode = currentIrpStack->Parameters.DeviceIoControl.IoControlCode;switch (IOControlCode)case READ_SCANCODE:/读扫描码if(pDevExt->LastScanCode)DbgPrint("READ_SCANCODE 0x%04Xn",pDevExt->LastScanCode);info = pDevExt->LastScanCode;pDevExt->LastScanCode = 0;break;case SET_CODE:/设定按键映射pBuffer = (ULONG *)pIrp->AssociatedIrp.SystemBuffer;pDevExt->SetCode0 = *pBuffer;pBuffer+;pDevExt->SetCode1 = *pBuffer;info = 1;break;/完成IRPstatus = STATUS_SUCCESS;pIrp->IoStatus.Status = status;pIrp->IoStatus.Information = info;IoCompleteRequest(pIrp, IO_NO_INCREMENT);return status;(二) 应用程序部分使用MFC实现应用界面,在程序启动时打开过滤驱动设备。/打开设备m_hDevice = CreateFile(".KeyFilterDriver",GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if (m_hDevice = INVALID_HANDLE_VALUE)CString str;str.Format("获得设备驱动句柄失败,错误代码:%d", GetLastError();MessageBox(str, "错误");exit(0);开辟新的工作线程,实现定时(100ms)读取捕获到的键盘扫描码,将扫描码翻译成按键名称,并按时间顺序将按键动作显示在ListBox中。while(1)DeviceIoControl(m_hDevice, READ_SCANCODE, NULL, 0, NULL, 0, &info, NULL);if(info)time=CTime:GetCurrentTime(); sprintf(mess,"%s Code:0x%04X %sn",time.Format("%H:%M:%S"),info,CodeName(info);pListBox1->AddString(mess);pListBox1->SetCurSel(pListBox1->GetCount()-1);Sleep(100);设定按键映射规则时,通过DeviceIoControl将两个DWORD类型的扫描码通过系统缓冲IO的方式传递给驱动程序。GetDlgItemText(IDC_EDIT1,mess,8);sscanf(mess,"0x%x",&Code0);GetDlgItemText(IDC_EDIT2,mess,8);sscanf(mess,"0x%x",&Code1);if(Code0>0xE000 | Code1>0xE000)MessageBox("请输入小于0xE000的编码!");return;DeviceIoControl(m_hDevice, SET_CODE, Code, sizeof(Code), NULL, 0, &info, NULL);三、 所遇到的问题及解决方法键盘过滤驱动的卸载问题。在使用常规的驱动卸载步骤时,会发生系统蓝屏重启的故障。通过分析和查阅相关资料,终于得出了解决的办法。由于IRM_MJ_READ是异步的,在给IRP_MJ_READ设置了完成例程的情况下,该IRP完成后会调用过滤驱动所指定的完成例程,使得有了处理返回数据的机会。但也正是因为这样,当动态御载了键盘过滤驱动,也就卸载掉了完成例程,而之后的再次按键动作在完成了这个IRP后还是会调用那个已经不存在了的完成例程,因而引发错误。同理可知,在安装过滤驱动时,就已经有一个IRP在键盘设备驱动中等待按键了,而该IRP并没有被设置完成例程。因此可知,在驱动运行之后的第一个按键动作是不会被截获的。在实验中的确证实了这个推想。动态卸载的实现,是在设备扩展对象中加入一个IrpPendingCount计数器,用来记录正在运行中的(pending状态)IRP数量。当收到一个IRM_MJ_READ时计数器加1,在完成一个IRP之后计数器减1。在驱动卸载例程中,先用IoDetachDevice解除过滤驱动的绑定,使得之后的IRP不会再被设置完成例程;然后要等待到计数器为0,即已被设置了完成例程的IRP都已完成之后才卸载设备和驱动对象。因此,在进入驱动卸载例程之后,还需要等待一次额外的按键操作才能正常的完成驱动程序的卸载。/ 解除过滤驱动,释放设备对象与物理驱动之间的绑定关系 IoDetachDevice(KeyDevice);/等待计数器为0while(pDevExt->IrpPendingCount)/删除过滤设备IoDeleteDevice(KeyFilterDevice);/删除符号链接pLinkName = pDevExt->ustrSymLinkName;IoDeleteSymbolicLink(&pLinkName);return;四、 程序运行结果及使用说明使用DriverMonitor打开和加载驱动程序:通过DeviceTree可以看到,设备对象KeyFilterDriver已被正确创建,并挂接到了键盘设备上:启动应用程序,就可以实时显示经过翻译的按键动作:输入按键映射规则,如要将按键A替换成B,则在映射码中输入A的扫描码:0x1E,在目标码中输入B的扫描码:0x30,点击设置,之后的按键A就会被替换成按键B。关闭应用程序后,在DriverMonitor中卸载驱动,驱动卸载正常(需要一次额外的按键操作)。五、 收获及意见对Windows驱动程序的基本结构有了大致的了解,对IRP的传递和操作方式有了一定的认识和理解,对内核模式和应用模式之间的数据交换有了初步的认识。我非常喜欢这门课程由浅入深,从最简单的程序结构开始,逐步增加内容,循序渐进的讲授安排,建议老师保留这样的讲课方式。作为学生,我觉得这样非常有利于课堂的理解和加深对知识的印象。

    注意事项

    本文(驱动程序设计课程大作业键盘过滤驱动程序设计.doc)为本站会员(laozhun)主动上传,三一办公仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知三一办公(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    备案号:宁ICP备20000045号-2

    经营许可证:宁B2-20210002

    宁公网安备 64010402000987号

    三一办公
    收起
    展开