怎样在驱动层和应用层建立准消息机制(转)

本文介绍了一种在驱动层和应用层间建立准消息机制的方法,解决了驱动程序通知应用程序的难题。利用OVERLAPPED结构的异步调用特性,通过IOCTL实现双向通讯,有效提升了程序的可读性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

怎样在驱动层和应用层建立准消息机制    
  作者:TigerZD    
  怎样在驱动层和应用层建立准消息机制    
  TigerZD    
  驱动程序与应用程序运行与不同的环境又紧密合作,但是应用程序通知驱动程序易(IOCTL等),驱动程序通知应用程序却不易。一般的方法是单纯通过EVENT来进行,但是这种方法有其缺点:    
  1、EVENT只有信号态和非信号态两种区别,不能有附带的参数,因此一个EVENT只能对应一种事件,同时很多时候EVENT并不是在信号态,此时就需要应用程序在线程中等待,一旦事件较多那么线程就会较多,不但线程同步困难,程序也不易读写。    
  2、Windows   98对EVENT操作没有完全支持,像IoCreateXxxEvent并不被支持,因此要获得应用程序创建的事件句柄并不简单,这样驱动程序的通用性也被破坏了。    
  基于以上原因,单纯使用EVENT并不好,那么应该怎么做呢?经过实践,我总结出了一个较好的方法,原理是利用OVERLAPPED的异步调用、同时它又可以带参数的特性。具体做法如下:    
  1、在驱动程序的设备扩展中定义一个IRP变量,并在适当时候(调用IoCreateDevice初始化设备扩展后)初始化其为NULL。如    
  PIRP   UserMessageIrp;    
  2、定义一个IOCTL,如:    
  #define   IOCTL_DRIVER_USERMESSAGE   CTL_CODE(FILE_DEVICE_UNKNOWN,   /    
  0x801,/    
  METHOD_BUFFERED,   /    
  FILE_ANY_ACCESS)    
  3、应用程序在启动或要监控驱动程序的状态时发送该IOCTL到驱动程序,注意应用程序在用CreateFile打开设备连接时一定要带FILE_FLAG_OVERLAPPED参数。    
   
  HANDLE   FileIOWaiter   =   CreateEvent(   NULL,   TRUE,   FALSE,   NULL);    
  if(   FileIOWaiter==NULL)    
  return   GetLastError();    
  OVERLAPPED   ol;    
  ol.Offset   =   0;    
  ol.OffsetHigh   =   0;    
  ol.hEvent   =   FileIOWaiter;    
   
  ULONG   PnpMessage,nBytes;    
  if(!DeviceIoControl(hDevice,//设备句柄    
  IOCTL_DRIVER_USERMESSAGE    
  NULL,    
  0,    
  &PnpMessage,    
  sizeof(PnpMessage),    
  &nBytes,    
  &ol))    
  {    
  if(GetLastError()==ERROR_IO_PENDING)    
  {    
  while(WaitForSingleObject(FileIOWaiter,   100)==WAIT_TIMEOUT)    
  {//驱动程序没有消息传过来,循环等待    
  if(bInstanceisExit   ==   TRUE)//应用程序退出标志    
  {    
  CancelIo(hDevice);//见5    
  }    
  }    
  GetOverlappedResult(hDevice,   &ol,   &nBytes,   FALSE);//    
  //驱动程序有消息传过来,见6,得到数据。    
  }    
  }    
  //处理得到的数据,接7。    
   
  4、驱动程序在接到此IOCTL时如下处理:    
  ioControlCode   =   irpStack->Parameters.DeviceIoControl.IoControlCode;    
  switch(   ioControlCode)    
  {    
  ...    
  case   IOCTL_DRIVER_USERMESSAGE    
  ntStatus   =    
  Irp->IoStatus.Status   =   STATUS_PENDING;    
  Irp->IoStatus.Information   =   0;    
  IoMarkIrpPending(Irp);    
  IoSetCancelRoutine(Irp,DriverUserMessageCancelIrp);//见5    
  deviceExtension->UserMessageIrp   =   Irp;    
  return   STATUS_PENDING;    
  ...    
  }    
   
  5、定义IRP的Cancel例程,这是在应用程序要退出而驱动程序并没有完成该IRP时调用。    
  VOID   DriverUserMessageCancelIrp(    
  IN   PDEVICE_OBJECT   DeviceObject,    
  IN   PIRP   Irp)    
  {    
  PDEVICE_EXTENSION   deviceExtension;    
   
  deviceExtension   =   (PDEVICE_EXTENSION)    
  DeviceObject->DeviceExtension;    
   
  IoReleaseCancelSpinLock(Irp->CancelIrql);    
   
  //   If   this   is   our   queued   read,   then   unqueue   it    
  if(   Irp==deviceExtension->UserMessageIrp)    
  {    
  deviceExtension->UserMessageIrp   =   NULL;    
  }    
  //   Whatever   Irp   it   is,   just   cancel   it    
  Irp->IoStatus.Status   =   STATUS_CANCELLED;    
  Irp->IoStatus.Information   =   0;    
  IoCompleteRequest(Irp,IO_NO_INCREMENT);    
  }    
  6、在驱动程序需要通知应用程序时,举个例子,设备拔出时在处理IRP_MN_REMOVE_DEVICE时,在调用IoDetachDevice之前:    
  (#define   PNP_REMOVE_DEVICE   0   //驱动程序和应用程序共同定义的消息类型)    
  ...    
  ntStatus   =   DriverProcessUserMessageIrp(DeviceObject,PNP_REMOVE_DEVICE);    
  ...    
  //DriverProcessUserPnpIrp的定义如下:    
  NTSTATUS    
  DriverProcessUserMessageIrp(    
  IN   PDEVICE_OBJECT   DeviceObject,    
  ULONG   ProcessReason)    
  {    
  NTSTATUS   ntStatus;    
  PVOID   ioBuffer;    
  ULONG   outputBufferLength;    
  PIO_STACK_LOCATION   irpStack;    
  PIRP   Irp;    
  PDEVICE_EXTENSION   deviceExtension;    
   
  deviceExtension   =   (PDEVICE_EXTENSION)    
  DeviceObject->DeviceExtension;    
   
  Irp   =   deviceExtension->UserMessageIrp;    
  if(Irp   ==   NULL)    
  return   STATUS_SUCCESS;//这种情况是在设备启动后,应用程序并没有发送    
  //过该IRP,设备又要卸载时出现。    
   
  irpStack   =   IoGetCurrentIrpStackLocation   (Irp);    
   
  //   get   pointers   and   lengths   of   the   caller's   (user's)   IO   buffer    
  ioBuffer   =   Irp->AssociatedIrp.SystemBuffer;    
  outputBufferLength   =   irpStack->Parameters.DeviceIoControl.OutputBufferLength;    
   
  if(ioBuffer!=NULL   &&   sizeof(ProcessReason)<=outputBufferLength)    
  {    
  RtlCopyMemory(ioBuffer,    
  &ProcessReason,    
  sizeof(ProcessReason));    
  }    
  Irp->IoStatus.Status   =   STATUS_SUCCESS;    
  Irp->IoStatus.Information   =   sizeof(ProcessReason);    
   
  IoSetCancelRoutine(Irp,NULL);//取消Cancel例程。    
  IoCompleteRequest(Irp,IO_NO_INCREMENT);    
  return   STATUS_SUCCESS;    
  }    
  7、此时应用程序的OVERLAPPED的EVENT会置位,WaitForSingleObject结束等待,应用程序应如此处理:    
  switch(PnpMessage)//定义见3,是不是很像Windows消息处理例程?:)    
  {    
  ...    
  case   PNP_REMOVE_DEVICE:    
  //处理过程就不必写了吧。    
  break;    
  ...    
  }    
  至此驱动程序和应用程序就完成了一次消息传递,其余可类似循环。    
  以上程序段在98和2000下都经过测试。    
  TigerZD   .2002.7.11.(完)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值