📄 comspy.c
字号:
case IRP_MN_QUERY_INTERFACE:
case IRP_MN_QUERY_CAPABILITIES:
case IRP_MN_QUERY_DEVICE_TEXT:
case IRP_MN_QUERY_RESOURCES:
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
case IRP_MN_READ_CONFIG:
case IRP_MN_WRITE_CONFIG:
case IRP_MN_EJECT:
case IRP_MN_SET_LOCK:
case IRP_MN_QUERY_ID:
case IRP_MN_QUERY_PNP_DEVICE_STATE:
default:
IoSkipCurrentIrpStackLocation(Irp);
status = IoCallDriver(devExt->TargetDeviceObject, Irp);
break;
}
DbgPrint("- ComSpy_Pnp Exit 0x%0x \r\n", status);
return status;
}
/*
1. If *you* delay completion of the IRP yourself, do this:
IoMarkIrpPending();
return STATUS_PENDING;
2. If you want to pass the IRP to another driver and have no interest in
the result, do this:
IoSkipCurrentIrpStackLocation();
return IoCallDriver(...);
3. If you *do* care about the result, or if you need to release some
resource or another, do this:
IoCopyCurrentIrpStackLocationToNext();
IoSetCompletionRoutine();
return IoCallDriver(...);
NTSTATUS CompletionRoutine()
{
if (Irp->PendingReturned) IoMarkIrpPending();
...
return <anything except STATUS_MORE_PROCESSING_REQUIRED>;
}
4. If *you* complete the request, do this:
Irp->IoStatus.Status = <anything but STATUS_PENDING>
Irp->IoStatus.Information = <something>
IoCompleteRequest();
return <same status you stored in the IRP>
5. If you want to pass the request down, wait it for it to complete, and
continue processing in PASSIVE_MODE, do this (but only in a
non-arbitrary thread context where you know you're allowed to block):
IoCopyCurrentIrpStackLocationToNext();
IoSetCompletionRoutine(Irp, CompletionRoutine, &event, TRUE, TRUE, TRUE);
if (IoCallDriver() == STATUS_PENDING) KeWaitForSingleObject(&event,...);
NTSTATUS CompletionRoutine(..., PKEVENT pev)
{
KeSetEvent(pev, ...);
return STATUS_MORE_PROCESSING_REQUIRED;
}
*/
/*
RtlInitUnicodeString( &SerialDeviceName,L\"\\\\Device\\\\Serial0\" );
// open a handle to SERIAL.SYS & obtain a DEVICE_OBJECT
status = IoGetDeviceObjectPointer( &SerialDeviceName,
FILE_ALL_ACCESS,
&pFile,
&pSerialDO);//保存在此
status = ObReferenceObjectByPointer(
pSerialDO,
FILE_ALL_ACCESS,
NULL,
KernelMode);
ObDereferenceObject(pFile);
//然后构建一个irp,发送给获得的串口指针
KeInitializeEvent(
&Event,
NotificationEvent,
FALSE
);
// Build irp to be sent to serial driver
DeviceControlIrp = IoBuildDeviceIoControlRequest(
IOCTL_SERIAL_SET_TIMEOUTS,//以此为例子
pSerialDO,
RequestBuffer,
RequestBufferLength,
ReplyBuffer,
ReplyBufferLength,
FALSE,
&Event,
&IoStatusBlock
);
//发送给获得的串口指针
status = IoCallDriver(pSerialDO, DeviceControlIrp );
就在上面的代码出出错 返回10。
(1) IoGetDeviceObjectPointer处估计是正确的
(2) 可能是错在对获得的pSerialDO的使用上,对于这种IoGetDeviceObjectPointer获得的DEVICE_OBJECT,怎么构建irp发送给它呢?有什么规范吗?
*/
/*
总结应用和驱动之间用事件通讯的办法
作者:yansm
总结应用和驱动之间用事件通讯的办法
1. 驱动中IoCreateNotificationEvent,KeClearEvent
应用中OpenEvent(SYNCHRONIZE, FALSE, EVENT_NAME)
这样,只能在应用中WaitForSingleObject,而不能SetEvent,ResetEvent
驱动中可以KeSetEvent,(而且必须紧接着KeClearEvent,因为在应用中不能修改核心态创建的对象的状态,只能在这个时候清除状态了),即只能由驱动通知应用,在某些应用只需要等待通知的场合足够了,如应用等待数据准备好的通知,这时可以在中断处理函数中设置事件有信号
注意,OpenEvent第一个参数不能为EVENT_ALL_ACCESS,因为应用没有这么大权限操作在系统上下文创建的事件
2.在驱动中创建事件,并把时间句柄传给应用,应用不必OpenEvent。应用程序可以随意set,reset事件,这样可以和驱动中的系统线程进行同步
这种方法的前提条件是,event是在应用的进程上下文(在系统上下文创建的话就不可以)创建,并传给应用的,比如可以在某个IOCtl分支中创建事件并传给应用。
解释:在使用EVENT的PROCESS context中创的HANDLE就在这个进程的句柄表里,经检验没有权限限制,可以由应用直接使用;而在system context中创建的HANDLE当然就在SYSTEM进程里啦,若单单传句柄值给应用,而句柄表里根本就没有对应的句柄,当然不成功了。
代码如下
驱动中:
void ppppp(PVOID event)
{
KeWaitForSingleObject((PKEVENT)event,Executive,UserMode,0,0);
//......验证处
}
......
WCHAR wEventNameBuf[]=L"\\BaseNamedObjects\\SharedEvent";
UNICODE_STRING uEventName;
PKEVENT pEvent;
HANDLE hEvent,hThread;
......
case IOCTL_******:
RtlInitUnicodeString(&uEventName,wEventNameBuf);
pEvent = IoCreateNotificationEvent(&uEventName,&hEvent);
KeResetEvent(pEvent);
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,&hEvent,4);
PsCreateSystemThread(&hThread,THREAD_ALL_ACCESS,0,0,0,ppppp,pEvent);
应用中:
if(!DeviceIoControl(hDevice,IOCTL_******,0,0,&Handle,4,&Bytes,0))
MessageBox("DeviceIo Error!");
esle{
wsprintf(str,"%x,%x,%x",hDevice,Bytes,Handle);
MessageBox(str);
if(!SetEvent((HANDLE)Handle))
......
}
会看到,点击MessageBox OK后ppppp的确继续执行了。
3.在应用中创建事件,然后通过ioctl传给驱动,驱动中ObReferenceObjectByHandle来引用该事件对象。
这样应用和驱动中都可以检查和修改事件状态。
应用程序:
HANDLE m_hCommEvent = CreateEvent(NULL,
false,
false,
NULL);
// download event object to device driver,
// m_hCommDevice is the device object
DeviceIoControl(m_hCommDevice,
IO_REFERENCE_EVENT,
(LPVOID) m_hCommEvent,
0,
NULL,
0,
dwReturn,
NULL);
在需要的地方等待
while(true)
{
WaitForSingleObject(m_hCommEvent, INFINITE);
// After this function, the event is set to
// non signaled. Get information and deal with it.
}
驱动程序:
case IO_REFERENCE_EVENT:
hEvent = (HANDLE) irpStack->
Parameters.DeviceIoControl.Type3InputBuffer;
status = ObReferenceObjectByHandle(hEvent,
GENERIC_ALL,
NULL,
KernelMode,
&gpEventObject,
&objHandleInfo);
the gpEventObject is a PRKEVENT object, so we can use KeEventXXX and KeWaitForXXX to operate it.
当事件发生时,置信号
KeSetEvent(gpEventObject, 0, FALSE);
当不再需要事件对象时:
case IO_DEREFERENCE_EVENT:
if(gpEventObject)
ObDereferenceObject(gpEventObject);
Top
3楼 codewarrior (会思考的草) 回复于 2004-07-20 16:23:32 得分 0
怎样在驱动层和应用层建立准消息机制
作者: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.(完)
*/
/*
驱动程序里:
// 分配与应用程序共享的内存
SystemVirtualAddress = ExAllocatePool(NonPagedPool, 1500);
Mdl = IoAllocateMdl(SystemVirtualAddress, 1500, FALSE, FALSE, NULL);
MmBuildMdlForNonPagedPool(Mdl);
// 将分配的内存映射到用户进程地址空间,并返回地址。
case IO_GET_SHAREMEMORY_ADDR:
try
{
UserVirtualAddress = MmMapLockedPages(Mdl, UserMode);
*((PVOID *)(Irp->AssociatedIrp.SystemBuffer)) = UserVirtualAddress;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = sizeof(PVOID);
}
except(EXCEPTION_EXECUTE_HANDLER){}
在应用程序里:
DeviceIoControl(m_hDevice,
IO_GET_SHAREMEMORY_ADDR,
NULL,
NULL,
szOutputBuffer,
sizeof(szOutputBuffer),
&dwReturn,
NULL
);
psharememory = *((PVOID *)szOutputBuffer);
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -