`
yexin218
  • 浏览: 970723 次
  • 性别: Icon_minigender_1
  • 来自: 珠海
社区版块
存档分类
最新评论

关于DeviceIoControl实现异步的笔记【2】

阅读更多

前面我们谈到了关于异步I/O的实现:关于DeviceIoControl实 现异步的笔记【1】 。可是实现起来,你会发现你的程序在DevieIoControl已经被挂起,而且返回的结果是非0。这就与真正的异步调用返回结果有出入,理论上应该返回0,且GetLastError()值为ERROR_IO_PENDING。

/**
   Send the packets defined by users
*/
BOOL FilterWrapper::SendMyOwnPacket()
{
   BOOL result = FALSE;
   DWORD bytesWritten = 0;
   DWORD varEventResult;
   OVERLAPPED varOverLapped;
   HANDLE varObjectHandle = 0;
   LPVOID testBuffer = NULL; 

   PBYTE pBuf = NULL;
   DWORD testBufferLength = (DWORD)sizeof("Hi Mon, I finish your request!\n");  
   testBuffer =  new BYTE[testBufferLength];  
    if(testBuffer == NULL)  
    {  
	goto Exit;  
    } 
   
   varObjectHandle = CreateEvent(NULL,TRUE, TRUE,"");
   if(varObjectHandle == NULL)
      goto Exit;
   memset(&varOverLapped,0,sizeof(OVERLAPPED));
   varOverLapped.hEvent = varObjectHandle;
   varOverLapped.Offset = 0;
   varOverLapped.OffsetHigh = 0;
   
    // pass a new io control to underlying driver to send packets
	if(!DeviceIoControl(
                m_hFilter,
                IOCTL_FILTER_SEND_MYOWN_PACKET,
                "Request from user mode to Send A Packet.\n",
                sizeof("Request from user mode to Send A Packet.\n"),
                testBuffer,
                testBufferLength,
                &bytesWritten,
                (LPOVERLAPPED)&varOverLapped))
	{
		//printf("Can't Send Packet\n");
		if(GetLastError() != ERROR_IO_PENDING)
		{
	      printf("Overlapped I/O exception\n");
		  goto Exit;
        }else{
          printf("Overlappedn pending....\n");
       }		
	}
	printf("Son, I am calling you for dinner...\n");
	varEventResult = WaitForSingleObject(varObjectHandle,6000);
	switch(varEventResult)
	{
	  case WAIT_OBJECT_0 :
	       printf("overlapped i/0 workss\n");
		   pBuf = (PBYTE)testBuffer;
		   printf("Return buffer is %s\n",pBuf);
		   result = TRUE;
		   break;
	  case WAIT_TIMEOUT:
	       varEventResult = CancelIo(m_hFilter);
		    result = FALSE;
		   break;
	  default:
	       break;
	}
	// printf("Successfully Send A packet!^_^\n");
     ResetEvent(varObjectHandle);
	 CloseHandle(varObjectHandle);
Exit:
   delete[] testBuffer;
   return result;
}	

 所以每次都不会打印Overlappedn pending....这一句,因为DeviceIoControl返回为非零。我原本愚蠢的以为,底层驱动是不需要更改就可以实现异步I/O。但是我错了,从一开始我就错了。那么亡羊补牢吧。我们进行底层驱动的处理:

由于你要求驱动做的工作不能即时完成,所以我们先返回一个PENDING状态:

case IOCTL_FILTER_SEND_MYOWN_PACKET:
	    InputBuffer = OutputBuffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer;
        InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
        OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
//这里等下讲如何叫底层驱动做该做的事情
//一个疑问在这里:如果像常规的函数在这里调用,那么跟同步I/O有何差异?
//如果不这样,有其他方法吗?
DEBUGP(DL_TEST,("I am waiting this io dispath\n")); 
		Status = STATUS_PENDING;
        IoMarkIrpPending(Irp);
        Irp->IoStatus.Status = Status;
        return Status;
	break;

 这里返回的状态为STATUS_PENDING,所以导致GetLastError值为ERROR_IO_PENDING,而是用overlapped i/o的异步方式导致DeviceIoControl返回为0.

别以为要做好了,还有好多疑问:

 

  1. 如何叫底层驱动做我么要他做的事情呢(很明显这里不能用常规的函数,否则当前线程就会执行这个函数的功能)
  2. 刚才的IRP请求到底执行结束没?
  3. 最后以何种方式告诉User层应用程序,某个时间已经是signaled状态,然后读取最后执行结果?

带着这个三个问题,我们继续讲:

既然不能用常规的函数,我们想想有什么方法可以让这个函数独立运行,而不受当前线程控制,答案就是在创建一个线程,负责该项工作。所以在上面的代码中间添加:

		Status = PsCreateSystemThread(&threadHandle,
                                         THREAD_ALL_ACCESS,
                                         NULL,
                                         NULL,
                                         NULL,
                                        (PKSTART_ROUTINE) printSomething,
                                         Irp
                             );
		if( !NT_SUCCESS(Status))
         {
              DEBUGP(DL_TEST,("Fail to start a thread!\n"));
               return Status;
         }	

 注意这里传入当前IRP的指针。当该线程完成工作后,结束该IRP。

接下来看看线程调用printSomething这个函数:

VOID
printSomething(
   IN PIRP    pIrp
   ){
     PUCHAR    OutputBuffer = NULL;
	 PUCHAR    pTestBuf = "Hi Mon, I finish your request!\n";  
	 ULONG     bufSize = sizeof("Hi Mon, I finish your request!\n");
     mySleepTimer(5);
     DEBUGP(DL_TEST,("Five seconds,I have finished done something,hahhaha\n"));
     pIrp->IoStatus.Status = NDIS_STATUS_SUCCESS;
	 OutputBuffer = (PUCHAR)pIrp->AssociatedIrp.SystemBuffer;
	 NdisMoveMemory(OutputBuffer,pTestBuf,bufSize);
	 pIrp->IoStatus.Information = bufSize;
     IoCompleteRequest(pIrp, IO_NO_INCREMENT);
	 PsTerminateSystemThread(STATUS_SUCCESS);
   }
 

这里,我们等待5秒钟,然后返回。返回前设置输出缓冲区的数据,返回给user,其次设置返回的状态Success等。最后调用IoCompleteRequest()函数通知User中的Event事件,把Event设置成Signaled状态,使得WaitForSignalObject函数可以继续执行。

这样才完成异步I/O的调用,其实自己细看,使用同步时,DeviceIoControl被挂起,现在使用异步,DeviceIoControl立刻返回,但是在WaitForSignalObject中挂起等待Event的状态改变。所以要真正实现异步,估计还需要在User层使用线程,用线程负责该DeviceIoControl的调用。才能真正意义上实现异步。

----------------------------------------附上MySleepTimer()------------------------------

这个函数实现的功能是延迟5秒钟。

VOID 
mySleepTimer(
   IN ULONG time
){
   LARGE_INTEGER my_interval;
   my_interval.QuadPart = RELATIVE(SECONDS(5));
   KeDelayExecutionThread(KernelMode,FALSE,&my_interval);
}

 关键是在SECONDS()的宏定义,来自Osronline的牛人写的:

//Define some times
#define ABSOLUTE(wait) (wait)

#define RELATIVE(wait) (-(wait))

#define NANOSECONDS(nanos) \
(((signed __int64)(nanos)) / 100L)

#define MICROSECONDS(micros) \
(((signed __int64)(micros)) * NANOSECONDS(1000L))

#define MILLISECONDS(milli) \
(((signed __int64)(milli)) * MICROSECONDS(1000L))

#define SECONDS(seconds) \
(((signed __int64)(seconds)) * MILLISECONDS(1000L))

 所以等相对的5秒钟就是 RELATIVE(SECONDS(5)),很强大~

------------------------------------附上图片---------------------------------

执行过程中,WaitForsignalObject被挂起:



 最后执行完成:


下面是Debugview信息:

0005056    261.43447876    NDISLWF:    
00005057    261.43450928    The input length is 42, and inputdata is Request from user mode to Send A Packet.    
00005058    261.43450928        
00005059    261.43460083    NDISLWF:    
00005060    261.43460083    I am waiting this io dispath   

.......

00005229    266.43710327    NDISLWF:    
00005230    266.43713379    Five seconds,I have finished done something,hahhaha 

-------------------参考资料-----------------

  1. DPC定时器何时返回的问题 http://bbs.pediy.com/showthread.php?t=110344
  2. 内核中线程的创建与销毁 http://hi.baidu.com/sysinternal/blog/item/f2b877084535c532e92488cc.html
  3. 关于DeviceIoControl异步调用的笔记【1】:http://yexin218.iteye.com/blog/638445

最后感谢两个人: 南部天天以及古越魂

  • 大小: 39.2 KB
  • 大小: 39.7 KB
1
0
分享到:
评论
1 楼 yexin218 2010-04-11  
Your user-mode code looks OK, except I would initialize all unused
members of OVERLAPPED structure to zero before calling DeviceIoControl.

But what are you doing in the device driver? Here is an excerpt from the
MSDN KB article Q117308 (their site seems to be down, otherwise I would
direct you to the page).

"The driver should not complete the I/O request until an event has occurred.
When the driver receives the I/O request, if an event has occurred and is
waiting to be sent to the application, the driver can complete the request
in the dispatch routine. If no event is waiting to be reported, the driver
should perform the following steps:
1. Mark the Irp pending, using IoMarkIrpPending().
2. Set up a cancel routine for the Irp, using IoSetCancelRoutine().
3. Put the Irp in a storing place (a queue for example).
4. Return STATUS_PENDING from the dispatch routine.

Later, when an event has occurred, the driver can complete the pending
request from its deferred procedure call (DPC) routine. Before the Irp can
be completed, the driver should set the cancel routine address to NULL
using IoSetCancelRoutine."

相关推荐

    使用DeviceIoControl进行通信(直接方式)

    例如,对于可能耗时的操作,驱动可能需要将`DeviceIoControl`转换为异步处理,使用IRP的挂起和完成机制。同时,为了防止内存泄漏,驱动必须正确地释放分配的资源,而应用程序也需要处理可能的错误状态。 总的来说,...

    实战DeviceIoControl.rar

    在Windows系统中,DeviceIoControl函数是驱动程序开发者常用的接口,用于发送控制代码到设备对象,实现对硬件设备的特殊操作。本文将深入探讨DeviceIoControl的相关知识点,并结合Aida整理的实战案例进行解析。 一...

    DeviceIoControl解读

    `DeviceIoControl`是Windows平台下实现设备驱动交互的关键接口之一。通过对该函数的深入理解和合理利用,开发者可以更加灵活地控制和管理各种硬件资源,提高软件的性能和稳定性。在实际开发过程中,还需要注意对错误...

    DeviceIoControl

    综上所述,`DeviceIoControl`函数是Windows系统中与设备交互的重要工具,开发者可以通过它实现对设备的精细控制,实现更复杂的功能。在实际开发中,需要充分理解控制代码的含义,合理使用缓冲区,并注意错误处理和...

    实战DeviceIoControl.rar_deviceiocontrol_driver.IoControl_pdiusbd12

    总之,DeviceIoControl是Windows系统中驱动程序与应用程序间的关键桥梁,通过它,我们可以实现对硬件设备的精细控制。在pdiusbd12这样的USB驱动中,熟练掌握DeviceIoControl的使用技巧,能够帮助开发者高效地进行...

    实战DeviceIoControl:通过API访问设备驱动程序.rar

    本实战教程将深入探讨如何使用DeviceIoControl来直接操控设备,从而实现对硬件的高级控制。以下是对这个主题的详细阐述: DeviceIoControl是Windows系统内核提供的一个系统调用,它允许用户模式的应用程序向设备...

    DeviceIoControl详解及例程

    总的来说,DeviceIoControl是Windows系统中实现设备控制的关键工具,通过它,开发者可以直接与硬件对话,实现设备的个性化操作。理解和掌握DeviceIoControl的使用,对于进行系统级编程和设备驱动开发至关重要。

    windows wdm驱动的例子,采用异步完成的方式实现驱动程序和应用程序通信的程序

    在实现异步完成时,驱动程序通常会执行以下步骤: - **注册I/O完成例程**:当驱动程序接收到I/O请求时,它会为该请求分配一个I/O完成上下文,并在设备队列上设置一个I/O完成例程。这样,当I/O操作完成时,系统会...

    KernelIoControl和DeviceIoControl的区别

    - **实现方式各异**:DeviceIoControl的实现涉及到应用程序与驱动程序之间的通信链路,而KernelIoControl则是通过驱动程序与内核层之间的接口实现的。 - **编程难度差异**:通常来说,DeviceIoControl相对易于理解和...

    DeviceIOControl函数实战演示及源代码

    在设备驱动程序设计中,DeviceIOControl是一个强大的工具,它可以用来实现诸如读写扇区、查询设备状态、配置设备参数等复杂功能。但需要注意的是,不正确的使用可能会导致系统不稳定,因此在编写和使用时必须谨慎。 ...

    实战DeviceIoControl全集

    6. **实例分析**:书中可能涵盖了一些实际的DeviceIoControl应用案例,如读写硬盘扇区、获取硬件配置信息、控制网络适配器等,这些都是通过DeviceIoControl实现的典型操作。 7. **调试技巧**:学习如何使用调试工具...

    易语言DeviceIoControl取硬盘序列号

    易语言DeviceIoControl取硬盘序列号源码,DeviceIoControl取硬盘序列号,倒序排列,DeviceIoControl,CreateFileA,CloseHandle,GetLastError,LocalAlloc,LocalFree

    实战DeviceIoControl

    在驱动开发领域,`DeviceIoControl`函数是一个非常重要的API,它主要用于实现用户模式应用程序与内核模式驱动程序之间的通信。通过这个函数,用户空间的应用程序可以向驱动程序发送控制代码,并接收相应的数据。下面...

    DeviceIoControl读取无线网卡mac完整工程实例

    最后,压缩包中的"opencadr"可能是源代码文件或者相关文档,通过查看这些内容,你可以得到更具体的实现细节和步骤,从而加深对`DeviceIoControl`和`CreateFile`使用的理解。在实际开发中,遵循良好的编程实践,如...

    passthru使用DeviceIoControl进行高层与底层的交互

    本文将深入探讨`passthru`技术,以及如何利用`DeviceIoControl`函数来实现这种交互,同时关注`NDIS`(网络驱动接口规范)的相关知识。 `passthru`技术的核心在于允许应用程序直接对硬件设备发送原始I/O请求,而不是...

    DeviceIoControl取硬盘序列号.rar

    DeviceIoControl取硬盘序列号.rar

    Filter驱动开发笔记

    关于DeviceIoControl实现异步的笔记【2】 - **笔记内容**:进一步深入探讨DeviceIoControl实现异步通信的关键技术和最佳实践。 - **学习价值**:通过实际案例加深对异步通信的理解。 #### 42. Windows NT驱动程序...

    DeviceIoControl、CreateFile,结合WMI读取物理硬盘网卡mac特征码,完整源码工程实例

    DeviceIoControl() CreateFile() 读取硬件物理网卡mac完整工程实例 Vs2012 c++ 源代码 功能:结合WMI和DeviceIoControl获取网卡原生MAC地址和当前MAC地址 入口参数: iQueryType:需要获取的... -2:WMI连接失败

Global site tag (gtag.js) - Google Analytics