我想编一个键盘驱动(用Win2k DDK),由应用软件来控制键盘驱动的行为,如:我想屏蔽Ctrl+Alt+Del键的时候给驱动发一个消息,驱动程序收到后才屏蔽键组合。 问题:应用程序和驱动程序(*.SYS)如何通讯?DDK编写的程序中能用诸如内存映射文件、Mutex、Event等吗? 分不够可以再加。谢谢!

解决方案 »

  1.   

    键盘不了解,但是你的需求应该不难,在www.driverdevelop.com上找找应该有
      

  2.   

    总结应用和驱动之间用事件通讯的办法 
    作者: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); 
     
      

  3.   

    怎样在驱动层和应用层建立准消息机制 
    作者: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.(完)
      

  4.   

    www.driverdevelop.com
    applications can not SYS correspond
      

  5.   

    经典还可以去看看www.vchelp.com
      

  6.   

    后 在看..谢谢wyz_csdn (网语者) 和codewarrior(会思考的草)还有其它 回答问题的兄弟
      

  7.   

    鍵盤驅動無法與應用層直接通信,因爲級別太低(可以察看驅動的級別),因此需要與應用層通信就必須還要一個在驅動,記得不太清楚了,DDK的函數在寫驅動的時候都規定了適用的級別,用錯了就藍屏。因爲級別的緣故,鍵盤驅動層無法調用很多與應用層通訊的函數,只有幾個與普通驅動的通信的函數可用。
      

  8.   

    谢谢大家的回复,我会尽快结帖的!  我对驱动程序编程刚刚接触,可以说什么都不懂,我现在在看Mark Russinovich的例子Ctrl2cap,其中没有与应用通讯,所以我想加入通讯功能。  我大概看了上面的一些帖子和驱动开发网站上的相关文档,利用CreateFile和DeviceIoControl来通讯,但是要想打开键盘驱动,必须创建应用程序可以访问的Symbol
    (用IoCreateSymbolicLink创建),我加入了该语句,但没有创建成功(用WinObj.EXE软件查看)。 我把IoCreateSymbolicLink加在IoCreateDevice、IoAttachDevice之后,应该放在哪里啊?  另外,能不能简单给我说说如何调试驱动程序?我看代码中有好多DbgPrint语句,不知道输出到哪里了。
      

  9.   

    我大概看了上面的一些帖子和驱动开发网站上的相关文档,利用CreateFile和DeviceIoControl来通讯,但是要想打开键盘驱动,必须创建应用程序可以访问的Symbol
    (用IoCreateSymbolicLink创建),我加入了该语句,但没有创建成功(用WinObj.EXE软件查看)。 我把IoCreateSymbolicLink加在IoCreateDevice、IoAttachDevice之后,应该放在哪里啊?
    -------------------------------------------------------------------------
    鍵盤驅動是不能直接被應用層控制的,DeviceIoControl將會報訪問被拒!!!需要其它的驅動中轉!!DbgPrint直接輸出到DebugView中,DbgPrint也有級別控制,用錯了就藍屏,鍵盤驅動尤其套注意。
      

  10.   

    SoftICE可以進行驅動的調試,源代碼級調試需要check版本
      

  11.   

    1,啥叫“驱动的级别”?从没听说过。只知道驱动在Device Stack中的层次位置,和IRQL的级别。
    2,创建符号连接的位置是随便你的,只要在IoCreateDevice之后就可以了。并且你必须要给你的设备对象命名。设备对象的名称,在Device目录下,符号连接放在DosDevice目录下。
    举例如下:
    NTSTATUS AddDevice(IN PDRIVER_OBJECT pDriverObject, IN PDEVICE_OBJECT pDeviceObject)
    {
    NTSTATUS       ntStatus = STATUS_SUCCESS;
    PDEVICE_OBJECT    pFdo = NULL;
        UNICODE_STRING    wstrDevObjName = {0};
        UNICODE_STRING    wstrDevLink = {0};
    PDEVICE_EXTENSION pDevExt = NULL;

    //初始化设备名,然后创建设备
    RtlInitUnicodeString(&wstrDevObjName, DEVICE_OBJECT_NAME);
    ntStatus = IoCreateDevice
       (
     pDriverObject, // IN 驱动程序对象
     sizeof(DEVICE_EXTENSION), // IN 设备扩展大小
     &wstrDevObjName, // IN 设备名称(可选)
     FILE_DEVICE_UNKNOWN, // IN 设备类型
     0, // IN 设备特征
     FALSE, // IN 是否排斥
     &pFdo // OUT 设备对象
       );
    if(!NT_SUCCESS(ntStatus))
    {
    dprintf("HelloWDM.sys: IoCreateDevice failed!\n");
    return ntStatus;
    } //创建符号连接
    RtlInitUnicodeString(&wstrDevLink, SYMBOL_LINK_NAME);
    ntStatus = IoCreateSymbolicLink(&wstrDevLink, &wstrDevObjName);
    if(!NT_SUCCESS(ntStatus))
    {
    dprintf("HelloWDM.sys: IoCreateSymbolicLink failed.\n");
    IoDeleteDevice(pFdo);
    return ntStatus;
    }         //以下可以挂接设备堆栈,略
    }
      

  12.   

    #define DEVICE_OBJECT_NAME L"\\Device\\HELLOWDM" //设备对象名称
    #define SYMBOL_LINK_NAME L"\\DosDevices\\HELLOWDM" //设备对象的符号连接
      

  13.   

    察看调试信息,这类工具举不胜举,用的比较多的是这位兄台写的debugview。
    调试驱动程序,一般用SoftICE或者WinDbg,后者要双机环境,所以一般用VMWare+WinDbg的方法来实现。
      

  14.   

    ,想要说的codewarrior都说完了,而且更加详细,我就不多说了,楼主结贴时别忘了通知我,我会把着张帖加入精华贴
      

  15.   

    补充一下,近日在Codeproject上看到了一个例子,是关于应用程序和驱动之间通讯的,对于入门者挺适合的,推荐给大家:http://www.codeproject.com/system/driveguicomm.asp