驱动程序设计课程大作业--键盘过滤驱动程序设计.doc_第1页
驱动程序设计课程大作业--键盘过滤驱动程序设计.doc_第2页
驱动程序设计课程大作业--键盘过滤驱动程序设计.doc_第3页
驱动程序设计课程大作业--键盘过滤驱动程序设计.doc_第4页
驱动程序设计课程大作业--键盘过滤驱动程序设计.doc_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

驱动程序设计 课程大作业驱动程序设计课程大作业键盘过滤驱动程序设计班级:姓名: 学号: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消息将映射关系发送给过滤驱动程序,还是在完成例程中实现按键的映射替代。2、 模块的划分、实现及说明具体实现分为驱动程序和应用程序两大部分。驱动程序用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; imajorfunctioni = 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, ldevicekeyfilterdriver);/要挂接的设备rtlinitunicodestring(&hookdevname, ldevicekeyboardclass0);/符号链接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;(2) 应用程序部分使用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(code00xe000 | code10xe000)messagebox(请输入小于0xe000的编码!);return;deviceiocontrol(m_hdevice, set_code, code, sizeof(code), null, 0, &info, null);3、 所遇到的问题及解决方法键盘过滤驱动的卸载问题。在使用常规的驱动卸载步骤时,会发生系统蓝屏重启的故障。通过分析和查阅相关资料,终于得出了解决的办法。由于irm_mj_read是异步的,在给irp_mj_read设置了完成例程的情况下,该irp完成后会调用过滤驱动所指定的完成例程,使得有了处理返回数据的机会。但也正是因为这样,当动态御载了键盘过滤驱动,也就卸载掉了完成例程,而之后的再次按键动作在完成了这个irp后还是会调用那个已经不存在了的完成例程,因而引发错误。同理可知,在安装过滤驱动时,就已经有一个irp在键盘设备驱动中等待按键了,而该irp并没有被设置完成例程。因此可知,在驱动运行之后的第一个按键动作是不会被截获的。在实验中的确证实了这个推想。动态卸载的实现,是在设备扩展对象中加入一个irppendingcount计数器,用来记录正在运行中的(pending状态)irp数量。当收到一个irm_mj_read时计数器加

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论