lsxredrain
驱动中牛
驱动中牛
  • 注册日期2006-08-31
  • 最后登录2008-10-18
  • 粉丝1
  • 关注0
  • 积分540分
  • 威望421点
  • 贡献值1点
  • 好评度420点
  • 原创分4分
  • 专家分0分
阅读:1901回复:5

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

楼主#
更多 发布于:2007-04-10 16:40
  我想从驱动层发送消息给应用层,而不是应用层轮询,学习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.(完)
lsxredrain
驱动中牛
驱动中牛
  • 注册日期2006-08-31
  • 最后登录2008-10-18
  • 粉丝1
  • 关注0
  • 积分540分
  • 威望421点
  • 贡献值1点
  • 好评度420点
  • 原创分4分
  • 专家分0分
沙发#
发布于: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)
一直都不成功,不知道原因在哪里??
zh002008
驱动牛犊
驱动牛犊
  • 注册日期2004-08-22
  • 最后登录2013-09-06
  • 粉丝1
  • 关注1
  • 积分545分
  • 威望155点
  • 贡献值0点
  • 好评度51点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2007-04-11 20:06
建议在驱动层创建一个命名的EVENT,在上层直接打开操作该EVENT.
lsxredrain
驱动中牛
驱动中牛
  • 注册日期2006-08-31
  • 最后登录2008-10-18
  • 粉丝1
  • 关注0
  • 积分540分
  • 威望421点
  • 贡献值1点
  • 好评度420点
  • 原创分4分
  • 专家分0分
地板#
发布于:2007-04-13 17:08
引用第2楼zh0020082007-04-11 20:06发表的“”:
建议在驱动层创建一个命名的EVENT,在上层直接打开操作该EVENT.

恩,已经放弃了这个办法了,还是用信号量来做
参考这个代码
附件名称/大小 下载次数 最后更新
DriveGUIComm_demo.zip (25KB)  65 2007-04-13 17:08
lsxredrain
驱动中牛
驱动中牛
  • 注册日期2006-08-31
  • 最后登录2008-10-18
  • 粉丝1
  • 关注0
  • 积分540分
  • 威望421点
  • 贡献值1点
  • 好评度420点
  • 原创分4分
  • 专家分0分
地下室#
发布于: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();

}
china.ygw
驱动牛犊
驱动牛犊
  • 注册日期2006-09-24
  • 最后登录2008-03-14
  • 粉丝0
  • 关注0
  • 积分140分
  • 威望15点
  • 贡献值0点
  • 好评度14点
  • 原创分0分
  • 专家分0分
5楼#
发布于:2007-05-18 21:16
太好了,正好可以学习一下!!!
游客

返回顶部