zoukankan      html  css  js  c++  java
  • 11、Windows驱动开发技术详解笔记(7) 基本语法回顾

    9、驱动程序与应用程序通信 

    1)使用WriteFile通信 

    可以在应用层调用ReadFile WriteFile 分别从驱动中读取和写入数据,他们通过两个不同的IRP来传递信息。

    http://msdn.microsoft.com/en-us/library/ff549427%28VS.85%29.aspx

    一个例子如下:

    首先是我们的应用层程序代码:

     

    代码
    1 #include "windows.h"
    2
    3 #include "stdio.h"
    4
    5 int main()
    6
    7 {
    8
    9 char szInBuffer[20] = {0};
    10
    11 DWORD nLen = 0;
    12
    13 // 打开设备句柄
    14
    15 HANDLE hDevice = ::CreateFile("\\\\.\\Test", // 符号链接
    16
    17 GENERIC_READ | GENERIC_WRITE,
    18
    19 0,
    20
    21 NULL,
    22
    23 OPEN_EXISTING,
    24
    25 FILE_ATTRIBUTE_NORMAL,
    26
    27 NULL);
    28
    29 if (hDevice == INVALID_HANDLE_VALUE)
    30
    31 { return -1;
    32
    33 }
    34
    35 // 向驱动设备写入连续10 个字节的A
    36
    37 memset(szInBuffer, 'A', 10);
    38
    39 BOOL ret = WriteFile(hDevice,
    40
    41 szInBuffer,
    42
    43 10,
    44
    45 &nLen,
    46
    47 NULL);
    48
    49 // 关闭设备句柄
    50
    51 CloseHandle(hDevice);
    52
    53 return 0;
    54
    55 }
    56
    57 下面开始写驱动层的代码,首先添加一个IRP_MJ_WRITE 的派遣例程,如下所示:
    58
    59 NTSTATUS
    60
    61 TestDispatchWrite(
    62
    63 IN PDEVICE_OBJECT DeviceObject,
    64
    65 IN PIRP Irp
    66
    67 )
    68
    69 {
    70
    71 NTSTATUS Status = STATUS_SUCCESS;
    72
    73 PIO_STACK_LOCATION irpStack;
    74
    75 // 得到当前栈
    76
    77 irpStack = IoGetCurrentIrpStackLocation(Irp);
    78
    79 // 输出缓冲区字节数和内容
    80
    81 DbgPrint("[Test] %d", irpStack->Parameters.Write.Length);
    82
    83 DbgPrint("[Test] %s", Irp->AssociatedIrp.SystemBuffer);
    84
    85 // 完成IRP
    86
    87 Irp->IoStatus.Information = irpStack->Parameters.Write.Length;
    88
    89 Irp->IoStatus.Status = Status;
    90
    91 IoCompleteRequest(Irp, IO_NO_INCREMENT);
    92
    93 return Status;
    94
    95 }

    另外,还要有:在DriverEntry 中添加一行“deviceObject->Flags |= DO_BUFFERED_IO;”

    这样,加裁驱动后,运行应用层代码。

    2)使用DeviceIoControl 通信

    使用前面的方法,我们得调用ReadFileWriteFile来读写数据,实际上更好更通用的做法是使用DeviceIoControl函数。这个函数还可以用来做一些除读写之外的操作。

    DeviceIoControl函数会使操作系统产生一个IRP_MJ_DEVICE_CONTROL类型的IRP,然后这个IRP 会被分发到相应的派遣例程中。

    我们先来看一下DeviceIoControl 函数的原型声明:

    BOOL DeviceIoControl(

    HANDLE hDevice, // handle to device

    DWORD dwIoControlCode, // operation

    LPVOID lpInBuffer, // input data buffer

    DWORD nInBufferSize, // size of input data buffer

    LPVOID lpOutBuffer, // output data buffer

    DWORD nOutBufferSize, // size of output data buffer

    LPDWORD lpBytesReturned, // byte count

    LPOVERLAPPED lpOverlapped // overlapped information

    );

    需要重点掌握的是第二个参数dwIoControlCode,它是I/O 控制码,即IOCTL值,是一个32位的无符号整型数值。

    实际上DeviceIoControlReadFileWriteFile相差不大,不过它可以同时提供输入/输出缓冲区,而且还可以通过控制码传递一些特殊信息。IOCTL值的定义必须遵循DDK的规定,我们可以使用宏CTL_CODE来声明,如下:

    #define MY_DVC_IN_CODE \

    (ULONG)CTL_CODE(FILE_DEVICE_UNKNOWN, \

    0xa01, \

    METHOD_BUFFERED, \

    FILE_READ_DATA|FILE_WRITE_DATA)

    其中0xa01这个数字是用户可以自定义的,其他的参数请照抄。

    3)一个示例如下: 

    驱动程序: 

    #define MY_DVC_IN_CODE \ 

    (ULONG)CTL_CODE(FILE_DEVICE_UNKNOWN, \

    0xa09, \

    METHOD_BUFFERED, \

    FILE_READ_DATA|FILE_WRITE_DATA)

    代码
    1 /************************************************************************
    2
    3 * 函数名称:MyDeviceIoControl
    4
    5 * 功能描述:创建设备文件
    6
    7 * 参数列表:
    8
    9 pDevObj:功能设备对象
    10
    11 pIrp:从IO请求包
    12
    13 * 返回 值:返回状态
    14
    15 *************************************************************************/
    16
    17 NTSTATUS MyDeviceIoControl(
    18
    19 PDEVICE_OBJECT dev,
    20
    21 PIRP irp)
    22
    23 {
    24
    25 // 得到irpsp的目的是为了得到功能号、输入输出缓冲
    26
    27 // 长度等信息。
    28
    29 PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);
    30
    31 // 首先要得到功能号
    32
    33 ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode;
    34
    35 // 得到输入输出缓冲长度
    36
    37 ULONG in_len = irpsp->Parameters.DeviceIoControl.InputBufferLength;
    38
    39 ULONG out_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
    40
    41 // 请注意输入输出缓冲是公用内存空间的
    42
    43 PVOID buffer = irp->AssociatedIrp.SystemBuffer;
    44
    45 // 如果是符合定义的控制码,处理完后返回成功
    46
    47 if(code == MY_DVC_IN_CODE)
    48
    49 {
    50
    51 //… 在这里进行需要的处理动作
    52
    53 DbgPrint("[Test] %s", L"In if code == MY_DVC_IN_CODE\n");
    54
    55 //KdPrint(("In if code == MY_DVC_IN_CODE\n"));
    56
    57 // 因为不返回信息给应用,所以直接返回成功即可。
    58
    59 // 没有用到输出缓冲
    60
    61 irp->IoStatus.Information = 0;
    62
    63 irp->IoStatus.Status = STATUS_SUCCESS;
    64
    65 }
    66
    67 else
    68
    69 {
    70
    71 // 其他的请求不接受。直接返回错误。请注意这里返
    72
    73 // 回错误和前面返回成功的区别。
    74
    75 DbgPrint("[Test] %s", L"In if esle code != MY_DVC_IN_CODE\n");
    76
    77 irp->IoStatus.Information = 0;
    78
    79 irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
    80
    81 }
    82
    83 IoCompleteRequest (irp,IO_NO_INCREMENT);
    84
    85 return irp->IoStatus.Status;
    86
    87 }
    88
    89 pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyDeviceIoControl;
    90
    91 应用程序:
    92
    93 #include "windows.h"
    94
    95 #include "stdio.h"
    96
    97 #include "Driver.h"
    98
    99 int main()
    100
    101 {
    102
    103 HANDLE device=CreateFile(L"\\\\.\\MyCDOSL1",
    104
    105 GENERIC_READ|GENERIC_WRITE,0,0,
    106
    107 OPEN_EXISTING,
    108
    109 FILE_ATTRIBUTE_SYSTEM,0);
    110
    111 DWORD length = 0; // 返回的长度
    112
    113 if (device == INVALID_HANDLE_VALUE)
    114
    115 {
    116
    117 // … 打开失败,说明驱动没加载
    118
    119 printf("Error in device == INVALID_HANDLE_VALUE\n");
    120
    121 }
    122
    123 char in_buffer[10] = {"Helllo"};
    124
    125 int in_buffer_len = 10;
    126
    127 BOOL ret = DeviceIoControl(device,
    128
    129 MY_DVC_IN_CODE, // 功能号
    130
    131 in_buffer, // 输入缓冲,要传递的信息,预先填好
    132
    133 in_buffer_len, // 输入缓冲长度
    134
    135 NULL, // 没有输出缓冲
    136
    137 0, // 输出缓冲的长度为0
    138
    139 &length, // 返回的长度
    140
    141 NULL);
    142
    143 if(!ret)
    144
    145 {
    146
    147 // … DeviceIoControl失败
    148
    149 printf("Error in !ret\n");
    150
    151 }
    152
    153 // 关闭
    154
    155 CloseHandle(device);
    156
    157 return 0;
    158
    159 }

    4)驱动层信息传出

    驱动主动通知应用和应用通知驱动的通道是同一个,只是方向反过来。应用程序需要开启一个线程调用DeviceIoControl,(调用ReadFile亦可)。而驱动在没有消息的时候,则阻塞这个IRP的处理,等待有信息的时候返回。

    应用程序调用DeviceIoControl,当没有消息的时候,这个调用不返回,应用程序自动等待(相当于等待事件),有消息的时候函数返回,并从缓冲区中读到消息。

    实际上,驱动内部要实现这个功能,还是要用事件的,只是不用在应用和驱动之间传递事件了。

    驱动内部需要制作一个链表,当有消息要通知应用的时候,则把消息放入链表中并设置事件,在DeviceIoControl的处理中等待事件。驱动如果有消息要通知应用,必须把消息放入队列尾并设置事件g_my_notify_eventMyGetPendingHead获得第一条消息。

    代码
    1 NTSTATUS MyDeviceIoCtrlOut(PIRP irp,ULONG out_len)
    2
    3 {
    4
    5 MY_NODE *node;
    6
    7 ULONG pack_len;
    8
    9 // 获得输出缓冲区。
    10
    11 PVOID buffer = irp->AssociatedIrp.SystemBuffer;
    12
    13 // 从队列中取得第一个。如果为空,则等待直到不为空。
    14
    15 while((node = MyGetPendingHead()) == NULL)
    16
    17 {
    18
    19 KeWaitForSingleObject(
    20
    21 &g_my_notify_event,// 一个用来通知有请求的事件
    22
    23 Executive,KernelMode,FALSE,0);
    24
    25 }
    26
    27 // 有请求了。此时请求是node。获得PACK要多长。
    28
    29 pack_len = MyGetPackLen(node);
    30
    31 if(out_len < pack_len)
    32
    33 {
    34
    35 irp->IoStatus.Information = pack_len; // 这里写需要的长度
    36
    37 irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
    38
    39 IoCompleteRequest (irp,IO_NO_INCREMENT);
    40
    41 return irp->IoStatus.Status;
    42
    43 }
    44
    45 // 长度足够,填写输出缓冲区。
    46
    47 MyWritePackContent(node,buffer);
    48
    49 // 头节点被发送出去了,可以删除了
    50
    51 MyPendingHeadRemove ();
    52
    53 // 返回成功
    54
    55 irp->IoStatus.Information = pack_len; // 这里写填写的长度
    56
    57 irp->IoStatus.Status = STATUS_SUCCESS;
    58
    59 IoCompleteRequest (irp,IO_NO_INCREMENT);
    60
    61 return irp->IoStatus.Status;
    62
    63 }
    64
    65 这个函数的处理要追加到MyDeviceIoControl中。如下:
    66
    67 NTSTATUS MyDeviceIoControl(
    68
    69 PDEVICE_OBJECT dev,
    70
    71 PIRP irp)
    72
    73 {
    74
    75
    76
    77 if(code == MY_DVC_OUT_CODE)
    78
    79 return MyDeviceIoCtrlOut(dev,irp);
    80
    81
    82
    83 }

    在这种情况下,应用可以循环调用DeviceIoControl,来取得驱动驱动通知它的信息。

    参考

    1Windows 驱动开发技术详解

    2http://msdn.microsoft.com/en-us/library/ff565757%28VS.85%29.aspx

    3Windows驱动学习笔记,灰狐

  • 相关阅读:
    C#心得与经验(二)
    C#心得与经验(一)
    与C#的第一次~
    2014应届生面试经验详谈。
    Block基本用法
    OC中得那些“点”
    PCH文件的使用
    UIScrollView的subViews使用小注意
    分享一下本人录制图像处理与OpenCV学习视频
    OpenCV 3.2正式发布啦
  • 原文地址:https://www.cnblogs.com/mydomain/p/1857057.html
Copyright © 2011-2022 走看看