驱动对象与设备对象(转)
这里所说的
驱动对象
是一种数据结构, 在DDK 中名为
DRIVER_OBJECT。任何驱动程序都对应一个
DRIVER_OBJECT.如何获得本人所写的驱动对应的 DRIVER_OBJECT 呢?驱动程序的入口函数为 DriverEntry,因
此,当你写一个驱动的开始,你会写下如下的代码:
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING
RegistryPath )
{
}
这个函数就相当与喜欢c 语言的你所常用的main().IN 是 无意义的宏,仅仅表明后边的参数是一种输入,而对应的
OUT 则代表这个参数是一种返回。这里没有使用引用,因此如果想在参数中返回结果,一律传入指针。
DriverObject 就是你所写的驱动对应的 DRIVER_OBJECT, 是系统在加载你的驱动时候所分配的。
RegisteryPath 是 专用于你记录你的驱动相关参数的注册表路径。 这两者都由系统分配并通过这两个参数传递给你。
DriverObject 重要之处,在于它 拥有一组函数指针,称为 dispatch functions.
开发驱动的 主要任务就是 亲手撰写这些dispatch functions. 当系统用到你的驱动, 会向你的驱动发送IRP(这是
windows 所有驱动的共同工作方式)。你的任务是 在dispatch function 中处理这些请求。 你可以让irp 失败,也可以
成功返回, 也可以修改这些irp,甚至可以自己发出irp。
设备对象则是指 DEVICE_OBJECT.下边简称DO.
但是实际上每个 irp 都是针对 DO 发出的。只有针对由该驱动所生成的 DO 的 IRP, 才会发给该驱动来处理。具体的
分发函数,决定于 DO 下的 DriverObject 域。
当一个应用程序打开文件并读写文件的时候, windows 系统将这些请求变成irp 发送给文件系统驱动。
文件系统过滤驱动将 可以过滤这些irp.这样,你就拥有了 捕获和改变文件系统操作的 能力。
象 Fat32,NTFS 这样的 文件系统(File System,简称FS),可能生成好几种设备。首先文件系统驱动本身往往生成
一个 控制设备(CDO).这个设备的主要任务是 修改整个驱动的内部配置。因此一个 Driver 只 对应一个 CDO.
另一种设备是被这个文件系统 Mount 的 Volume。一个 FS 可能有多个 Volume,也 可能一个都没有。解释一下,如
果你有 C:,D:,E:,F:四个分区。 C:,D:为 NTFS, E:,F:为 Fat32.那么 E:,F:则是 Fat 的两个 Volume 设备对象.
实际上"C:"是该设备的符号连接(Symbolic Link)名。 而不是真正的设备名。 可以打开Symbolic Links Viewer,
能看到:
C: /Device/HarddiskVolume1
因此该设备的 设备名为“/Device/HarddiskVolume1”.
这里也看出来,文件系统驱动是针对每个 Volume 来生成一个 DeviceObject,而不是针对每个文件的。实际上对文
件的读写的 irp,都 发到Volume 设备对象上去了。并不会生成一个“文件设备对象”。
掌握了这些概念的话,我们现在用简单的代码来生成我们的CDO,作为我们开发文件系统驱动的第一步牛刀小试。
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
// 定义一个Unicode 字符串。
UNICODE_STRING nameString;
RtlInitUnicodeString( &nameString, L"//FileSystem//Filters//SFilter" );
// 生成控制设备
status = IoCreateDevice( DriverObject,
0, //has no device extension
&nameString,
FILE_DEVICE_DISK_FILE_SYSTEM,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&gSFilterControlDeviceObject );
// 如果因为路径没找到而生成失败
if (status == STATUS_OBJECT_PATH_NOT_FOUND) {
// 这是因为一些低版本的操作系统没有/FileSystem/Filters/这个目录
// 如果没有,我们则改变位置,生成到/FileSystem/下.
RtlInitUnicodeString( &nameString, L"//FileSystem//SFilterCDO" );
status = IoCreateDevice( DriverObject,
0,&nameString,
FILE_DEVICE_DISK_FILE_SYSTEM,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&gSFilterControlDeviceObject );
// 成功后,用KdPrint 打印一个log.
if (! NT_SUCCESS( status )) {
KdPrint(( "SFilter!DriverEntry: Error creating control device object /"%wZ/",
status=%08x/n", &nameString, status ));
return status;
}
} else if (! NT_SUCCESS( status )) {
// 失败也打印一个。并直接返回错误
KdPrint(( "SFilter!DriverEntry: Error creating control device object /"%wZ/",
status=%08x/n", &nameString, status ));
}
return status;
}
sfilter.sys.把这个文件与前所描述的inf 文件同一目录,按上节所叙述方法安装。
这个驱动不起任何作用,但是你已经成功的完成了"hello world".
初次看这些代码可能有一些慌乱。但是只要注意了以下几点,你就会变得轻松了:
1) 习惯使用UNICODE_STRING 字符串。这些字符串用Rtl…系列的函数来操作。你应该阅读DDK 帮助,然
后熟悉这些字符串的用法。
2) 用KdPrint(())来代替printf 输出信息。这些信息可以在DbgView 中看到。KdPrint(())自身是一个宏,
为了完整传入参数所以使用了两重括弧。这个比DbgPrint 调用要稍好。因为在free 版不被编译。
3) 查看DDK 帮助了解生成设备对象IoCreateDevice 的用法。
请注意CDO 生成后,保存在gSFilterControlDeviceObject 中。这样以后我们得到一个DEVICE_OBJECT 时,
就很容易判断是否是我们的控制设备。
DRIVER_OBJECT.如何获得本人所写的驱动对应的 DRIVER_OBJECT 呢?驱动程序的入口函数为 DriverEntry,因
此,当你写一个驱动的开始,你会写下如下的代码:
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING
RegistryPath )
{
}
这个函数就相当与喜欢c 语言的你所常用的main().IN 是 无意义的宏,仅仅表明后边的参数是一种输入,而对应的
OUT 则代表这个参数是一种返回。这里没有使用引用,因此如果想在参数中返回结果,一律传入指针。
DriverObject 就是你所写的驱动对应的 DRIVER_OBJECT, 是系统在加载你的驱动时候所分配的。
RegisteryPath 是 专用于你记录你的驱动相关参数的注册表路径。 这两者都由系统分配并通过这两个参数传递给你。
DriverObject 重要之处,在于它 拥有一组函数指针,称为 dispatch functions.
开发驱动的 主要任务就是 亲手撰写这些dispatch functions. 当系统用到你的驱动, 会向你的驱动发送IRP(这是
windows 所有驱动的共同工作方式)。你的任务是 在dispatch function 中处理这些请求。 你可以让irp 失败,也可以
成功返回, 也可以修改这些irp,甚至可以自己发出irp。
设备对象则是指 DEVICE_OBJECT.下边简称DO.
但是实际上每个 irp 都是针对 DO 发出的。只有针对由该驱动所生成的 DO 的 IRP, 才会发给该驱动来处理。具体的
分发函数,决定于 DO 下的 DriverObject 域。
当一个应用程序打开文件并读写文件的时候, windows 系统将这些请求变成irp 发送给文件系统驱动。
文件系统过滤驱动将 可以过滤这些irp.这样,你就拥有了 捕获和改变文件系统操作的 能力。
象 Fat32,NTFS 这样的 文件系统(File System,简称FS),可能生成好几种设备。首先文件系统驱动本身往往生成
一个 控制设备(CDO).这个设备的主要任务是 修改整个驱动的内部配置。因此一个 Driver 只 对应一个 CDO.
另一种设备是被这个文件系统 Mount 的 Volume。一个 FS 可能有多个 Volume,也 可能一个都没有。解释一下,如
果你有 C:,D:,E:,F:四个分区。 C:,D:为 NTFS, E:,F:为 Fat32.那么 E:,F:则是 Fat 的两个 Volume 设备对象.
实际上"C:"是该设备的符号连接(Symbolic Link)名。 而不是真正的设备名。 可以打开Symbolic Links Viewer,
能看到:
C: /Device/HarddiskVolume1
因此该设备的 设备名为“/Device/HarddiskVolume1”.
这里也看出来,文件系统驱动是针对每个 Volume 来生成一个 DeviceObject,而不是针对每个文件的。实际上对文
件的读写的 irp,都 发到Volume 设备对象上去了。并不会生成一个“文件设备对象”。
掌握了这些概念的话,我们现在用简单的代码来生成我们的CDO,作为我们开发文件系统驱动的第一步牛刀小试。
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
// 定义一个Unicode 字符串。
UNICODE_STRING nameString;
RtlInitUnicodeString( &nameString, L"//FileSystem//Filters//SFilter" );
// 生成控制设备
status = IoCreateDevice( DriverObject,
0, //has no device extension
&nameString,
FILE_DEVICE_DISK_FILE_SYSTEM,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&gSFilterControlDeviceObject );
// 如果因为路径没找到而生成失败
if (status == STATUS_OBJECT_PATH_NOT_FOUND) {
// 这是因为一些低版本的操作系统没有/FileSystem/Filters/这个目录
// 如果没有,我们则改变位置,生成到/FileSystem/下.
RtlInitUnicodeString( &nameString, L"//FileSystem//SFilterCDO" );
status = IoCreateDevice( DriverObject,
0,&nameString,
FILE_DEVICE_DISK_FILE_SYSTEM,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&gSFilterControlDeviceObject );
// 成功后,用KdPrint 打印一个log.
if (! NT_SUCCESS( status )) {
KdPrint(( "SFilter!DriverEntry: Error creating control device object /"%wZ/",
status=%08x/n", &nameString, status ));
return status;
}
} else if (! NT_SUCCESS( status )) {
// 失败也打印一个。并直接返回错误
KdPrint(( "SFilter!DriverEntry: Error creating control device object /"%wZ/",
status=%08x/n", &nameString, status ));
}
return status;
}
sfilter.sys.把这个文件与前所描述的inf 文件同一目录,按上节所叙述方法安装。
这个驱动不起任何作用,但是你已经成功的完成了"hello world".
初次看这些代码可能有一些慌乱。但是只要注意了以下几点,你就会变得轻松了:
1) 习惯使用UNICODE_STRING 字符串。这些字符串用Rtl…系列的函数来操作。你应该阅读DDK 帮助,然
后熟悉这些字符串的用法。
2) 用KdPrint(())来代替printf 输出信息。这些信息可以在DbgView 中看到。KdPrint(())自身是一个宏,
为了完整传入参数所以使用了两重括弧。这个比DbgPrint 调用要稍好。因为在free 版不被编译。
3) 查看DDK 帮助了解生成设备对象IoCreateDevice 的用法。
请注意CDO 生成后,保存在gSFilterControlDeviceObject 中。这样以后我们得到一个DEVICE_OBJECT 时,
就很容易判断是否是我们的控制设备。