有两U方式可以让讑֤和应用程序之间联p:(x)

1.         通过备创建的一个符号链Q?/span>

2.         通过输出C个接?/span>

WDM驱动E序使用输出C个接口而不推荐使用创徏W号铄Ҏ(gu)。这个接口保?/span>PDO的安全,也保证安全地创徏一个惟一的、独立于语言的访问设备的Ҏ(gu)?/span>

一个应用程序?/span>Win32APIs来调用设备。在某个Win32 APIs和设备对象的分发函数之间存在一个映关pR?/span>

获得对设备对象访问的W一步就是打开一个设备对象的句柄?/span>

用符号链打开一个设备的句柄

Z(jin)打开一个设备,应用E序需要?/span>CreateFile?/span>如果该设备有一个符号链出口Q应用程序可以用下面q个例子的Ş式打开句柄Q?/span>

hDevice = CreateFile(""""".""OMNIPORT3",
  GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ,
  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL ,NULL
);

文g路径名的前缀“""."”告诉pȝ本调用希望打开一个设备。这个设备必L一个符号链Q以便应用程序能够打开它。有关细节查看有?/span>Kdevice?/span>CreateLink的内宏V在上述调用中第一个参C前缀后的部分是q个W号铄名字?/span>

注意Q?/span>CreatFile中的W一个参C?/span>Windows 98/2000中驱动程?/span>(.sys文g)的\径。是到设备对象的W号链?/span>

如果使用DriverWizard产生驱动E序Q它通常使用c?/span>KunitizedName来构成设备的W号链。这意味着W号铑֐有一个附加的数字Q通常?/span>0。例如:(x)如果链接名称的主q是LTestDevice”那么?/span>CreateFile中的串就该是“"""".""TestDevice0”?/span>

如果应用E序需要被覆盖?/span>I/OQ第六个参数(Flags)必须或上FILE_FLAG_OVERLAPPED?/span>

使用一个输出接口打开句柄

用这U方式打开一个句柄会(x)E微ȝ(ch)一些?/span>DriverWorks库提供两个助手类来获得对该接口的访问容易一些,q两个类?/span>CDeviceInterface, ?/span> CdeviceInterfaceClass?/span>

CdeviceInterfaceClasscd装了(jin)一个设备信息集Q该信息集包含了(jin)Ҏ(gu)cM的所有设备接口信息?/span>

应用E序能有?/span>CdeviceInterfaceClasscȝ一个实例来获得一个或更多?/span>CdeviceInterfacecȝ实例?/span>CdeviceInterfacecL一个单一讑֤接口的抽象。它的成员函?/span>DevicePath()q回一个\径名的指针,该指针可以在CreateFile中用来打开讑֤?/span>

下面用一个小例子来显C些类最基本的用方法:(x)

extern GUID TestGuid;
HANDLE OpenByInterface(
  GUID* pClassGuid,
  DWORD instance,
  PDWORD pError
)
{
  CDeviceInterfaceClass DevClass(pClassGuid, pError);
  if (*pError != ERROR_SUCCESS)
    return INVALID_HANDLE_VALUE;
  CDeviceInterface DevInterface(&DevClass, instance, pError);
  if (*pError != ERROR_SUCCESS)
    return INVALID_HANDLE_VALUE;
  cout << "The device path is "
    << DevInterface.DevicePath()
    << endl;

  HANDLE hDev;
  hDev = CreateFile(
   DevInterface.DevicePath(),
    GENERIC_READ | GENERIC_WRITE,
    FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    NULL
  );
  if (hDev == INVALID_HANDLE_VALUE)
    *pError = GetLastError();
  return hDev;
}

在设备中执行I/O操作

一旦应用程序获得一个有效的讑֤句柄Q它?yu)p使用Win32 APIs来生到讑֤对象?/span>IRPs。下面的表显CZ(jin)q种对应关系?/span>

Win32 API

DRIVER_FUNCTION_xxx
IRP_MJ_xxx

KDevice subclass member function

CreateFile

CREATE

Create

ReadFile

READ

Read

WriteFile

WRITE

Write

DeviceIoControl

DEVICE_CONTROL

DeviceControl

CloseHandle

CLOSE
CLEANUP

Close
CleanUp

需要解释一下设备类成员?/span>Close?/span>CleanUpQ?/span>CreateFile使内ؓ(f)讑֤创徏一个新的文件对象。这使得多个句柄可以映射同一个文件对象。当q个文g对象的最后一个用L(fng)句柄被撤销后,I/O理器调?/span>CleanUp。当没有M用户U和核心(j)U的Ҏ(gu)件对象的讉K的时候,I/O理器调?/span>Close?/span>

如果被打开的设备不支持指定的功能,则调用相应的Win32引起错误(无效功能Q?/span>

以前?/span>Windows95~写?/span>VxD的应用程序代码中可能?x)在打开讑֤的时候?/span>FILE_FLAG_DELETE_ON_CLOSE属性。在Windows NT/2000中,不要使用q个属性,因ؓ(f)它将D没有Ҏ(gu)的用户企图打开q个讑֤Q这是不可能成功的?/span>

I/O理器将ReadFile?/span>WriteFile?/span>buff参数转换?/span>IRP域的Ҏ(gu)依赖于设备对象的属性。当讑֤讄DO_DIRECT_IO标志Q?/span>I/O理器将buff锁住在存储器中,q且创徏?jin)一个存储在IRP中的MDL域。一个设备可以通过调用Kirp::Mdl来存?/span>MDL?/span>

当设备设|?/span>DO_BUFFERED_IO标志Q设备对象分别通过KIrp::BufferedReadDest?/span> KIrp::BufferedWriteSource或写操作获得buff地址?/span>

当设备不讄DO_BUFFERED_IO标志也不讄DO_DIRECT_IOQ内核设|?/span>IRP ?/span>UserBuffer域来对应ReadFile?/span>WriteFile中的buff参数。然而,存储区ƈ没有被锁住而且地址只对调用q程有效。驱动程序可以?/span>KIrp::UserBuffer来存?/span>IRP域?/span>

对于DeviceIoControl调用Q?/span>buffer参数的{换依赖于Ҏ(gu)?/span>I/O控制代码Q它不在讑֤对象的特性中。宏CTL_CODEQ在winioctl.h中定义)(j)用来构造控制代码。这个宏的其中一个参数指明缓冲方法是METHOD_BUFFERED, METHOD_IN_DIRECT, METHOD_OUT_DIRECT, ?/span>METHOD_NEITHER。下面的表显CZ(jin)q些Ҏ(gu)和与之对应的能获得输入缓冲与输出~冲?/span>KIrp中的成员函数Q?/span>

Method

Input Buffer Parameter

Output Buffer Parameter

METHOD_BUFFERED

KIrp::IoctlBuffer

KIrp::IoctlBuffer

METHOD_IN_DIRECT

KIrp::IoctlBuffer

KIrp::Mdl

METHOD_OUT_DIRECT

KIrp::IoctlBuffer

KIrp::Mdl

METHOD_NEITHER

KIrp::IoctlType3InputBuffer

KIrp::UserBuffer

如果控制代码指明METHOD_BUFFEREDQ系l分配一个单一的缓冲来作ؓ(f)输入与输出。驱动程序必d向输出缓冲放数据之前拯输入数据。驱动程序通过调用KIrp::IoctlBuffer获得~冲地址。在完成ӞI/O理器从pȝ~冲拯数据到提供给Ring 3U调用者用的~冲中。驱动程序必dl束前存储拷贝到IRP?/span>Information成员中的数据个数?/span>

如果控制代码不指?/span>METHOD_IN_DIRECT?/span>METHOD_OUT_DIRECTQ则DeviceIoControl的参数呈C同的含义。参?/span>InputBuffer被拷贝到一个系l缓Ԍq个~冲驱动E序可以通过调用KIrp::IoctlBuffer。参?/span>OutputBuffer被映到KMemory对象Q驱动程序对q个对象的访问通过调用KIrp::Mdl来实现。对?/span>METHOD_OUT_DIRECTQ调用者必L对缓冲的写访问权限?/span>

注意Q对METHOD_NEITHERQ内核只提供虚拟地址Q它不会(x)做映来配置~冲。虚拟地址只对调用q程有效?/span>

q里是一个用METHOD_BUFFERED的例子:(x)

首先Q用宏CTL_CODE来定义一?/span>IOCTL代码Q?/span>

#define IOCTL_MYDEV_GET_FIRMWARE_REV "

CTL_CODE (FILE_DEVICE_UNKNOWN,0,METHOD_BUFFERED,FILE_ANY_ACCESS)

现在使用一?/span>DeviceIoControl调用Q?/span>

BOOLEAN b;
CHAR FirmwareRev[60];
ULONG FirmwareRevSize;
b = DeviceIoControl(hDevice, IOCTL_MYDEV_GET_VERSION_STRING,
  NULL, // no input 注意Q这里放的是包含有执行操作命令的字符串指?/span>
  0,

FirmwareRev,      //q里?/span>output串指针,存放从驱动程序中q回的字W串?/span>

sizeof(FirmwareRev),& FirmwareRevSize,
  NULL // not overlapped I/O
 );

如果输出~冲_大,讑֤拯串到里面q将拯的资l束讄?/span>FirmwareRevSize中?/span>

在驱动程序中Q代码看h如下所C:(x)

const char* FIRMWARE_REV = "FW 16.33 v5";
NTSTATUS MyDevice::DeviceControl( KIrp I )
{
  ULONG fwLength=0;
  switch ( I.IoctlCode() )
  {
    case IOCTL_MYDEV_GET_FIRMWARE_REV:
      fwLength = strlen(FIRMWARE_REV)+1;
      if (I.IoctlOutputBufferSize() >= fwLength)
      {
        strcpy((PCHAR)I.IoctlBuffer(),FIRMWARE_REV);
        I.Information() = fwLength;      
        return I.Complete(STATUS_SUCCESS);
      }
      else

      {
        
      }
    case . . .
   }
 }