阅读:1901回复:5
怎样在驱动层和应用层建立准消息机制
我想从驱动层发送消息给应用层,而不是应用层轮询,学习TigerZD 的文章如下:
我根据Sfilter修改了 a. typedef struct _SFILTER_DEVICE_EXTENSION { ... FAST_MUTEX FsCtxTableMutex; PIRP UserMessageIrp; } SFILTER_DEVICE_EXTENSION, *PSFILTER_DEVICE_EXTENSION; 用PSFILTER_DEVICE_EXTENSION 替换文章中的PDEVICE_EXTENSION 编译是没有问题的,但是对于应用层如何响应驱动发过来的消息搞不明白, 1 特别是PnpMessage怎么传值,怎么用不太明白. 2为什么要在if(GetLastError()==ERROR_IO_PENDING) 为真才开始等待呢? 大牛们能不能指点指点! 谢谢!! 怎样在驱动层和应用层建立准消息机制 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.(完) |
|
沙发#
发布于:2007-04-10 17:11
#define IOCTL_DRIVER_USERMESSAGE (ULONG) CTL_CODE(FILE_DEVICE_COMM_DRIVER, 0x810,METHOD_BUFFERED,FILE_ANY_ACCESS)
int CEveCom::GetDriverState(void) { bool bInstanceisExit = false; HANDLE m_hDriverStatet; 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; m_hDriverStatet = CreateFile( "\\\\.\\Sfilter", GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED, NULL); if(m_hDriverStatet == INVALID_HANDLE_VALUE) { int error = GetLastError(); char nn[] = "INVALID_HANDLE_VALUE"; MessageBox(NULL,(LPCSTR)nn,NULL,NULL); } if(m_hDriverStatet) { MessageBox(NULL,NULL,(LPCSTR)"hello lsx!",NULL); if(!DeviceIoControl( m_hDriverStatet,//设备句柄 IOCTL_DRIVER_USERMESSAGE, NULL, 0, &PnpMessage, sizeof(PnpMessage), &nBytes, &ol) ) { MessageBox(NULL,NULL,(LPCSTR)"hello lsdfdfdfdfx!",NULL); if(GetLastError()==ERROR_IO_PENDING) { while(WaitForSingleObject(FileIOWaiter, 100)==WAIT_TIMEOUT) {//驱动程序没有消息传过来,循环等待 if(bInstanceisExit == TRUE)//应用程序退出标志 { CancelIo(m_hDriverStatet);//见5 } } GetOverlappedResult(m_hDriverStatet, &ol, &nBytes, FALSE);// //驱动程序有消息传过来,见6,得到数据。 } } switch(PnpMessage)//定义见3,是不是很像Windows消息处理例程?:) { case 1: //处理过程就不必写了吧。 MessageBox(NULL,NULL,(LPCSTR)"tests",NULL); break; } } CloseHandle(m_hDriverStatet); return 0; } DeviceIoControl(hDevice,//设备句柄 IOCTL_DRIVER_USERMESSAGE NULL, 0, &PnpMessage, sizeof(PnpMessage), &nBytes, &ol) 一直都不成功,不知道原因在哪里?? |
|
板凳#
发布于:2007-04-11 20:06
建议在驱动层创建一个命名的EVENT,在上层直接打开操作该EVENT.
|
|
地板#
发布于:2007-04-13 17:08
引用第2楼zh002008于2007-04-11 20:06发表的“”: 恩,已经放弃了这个办法了,还是用信号量来做 参考这个代码 |
|
|
地下室#
发布于:2007-04-13 17:09
可以用一个递归
DWORD WINAPI CEveCom::TestThread(LPVOID lpParam) { CEveCom* pThis = (CEveCom*) lpParam; //while(1) //{ if(pThis->m_hCommEvent) { WaitForSingleObject(pThis->m_hCommEvent, INFINITE); //等待是否有信号 AfxMessageBox("dfdfdf"); pThis->OnCreateThread(); // ResetEvent(pThis->m_hCommDecodeEvent); } //} return true; } void CEveCom::OnCreateThread(void) { // TODO: Add your control notification handler code here DWORD dwThreadID; HANDLE hThread = CreateThread( NULL, 0, TestThread, this, 0, &dwThreadID); CloseHandle(hThread); //OnCreateDecodeThread(); } |
|
5楼#
发布于:2007-05-18 21:16
太好了,正好可以学习一下!!!
|
|