第二部分:Hello World驱动对象与设备对象
这里所说的驱动对象是一种数据结构,在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。当系统用到你的驱动,会向你的DO发送IRP(这是Windows所有驱动的共同工作方式)。您的任务是在这些dispatch function中处理这些请求。您可以让IRP失败,也可以成功返回,也可以修改这些IRP,甚至可以自己发出IRP。
设备对象则是指DEVICE_OBJECT。下边简称DO。
但是实际上每个IRP都是针对DO发出的。只有针对由该驱动所生成的DO的IRP,才会发给该驱动来处理。
当一个应用程序打开文件并读写文件的时候,Windows系统将这些请求变成IRP发送给文件系统驱动。
文件系统过滤驱动可以iguolv这些IRP。这样,您就拥有了捕获和改变文件系统的能力。
像Fat32、NTFS这样的文件系统(File System简称FS),可能生成好几种设备。首先文件系统驱动本身往往生成一个控制设备(CDO)。这个设备的主要任务是修改驱动的内部配置。因此一个Driver只对应一个CDO。
另一种设备是被这个文件系统Mount的Volume。一个FS可能有多个Volume,也可能一个都没有。解释一下,如果您有C:、D:、E:、F:四个分区。C:、D:为NTFS,E:、F:为Fat32,那么C:、D:则是Fat的两个Volume设备对象。(不是太明白,谁能给解释一下?)
实际上“C:”是该设备的符号连接(Symbolic Link)名。而不是真正的设备名。可以打开Symbolic Links Viewer,能看到:C: DeviceHarddiskVolume1。因此该设备的设备名为:“DeviceHarddiskVolume1”。
这里也看出来,文件系统驱动是针对每个Volume来生成一个DeviceObject,而不是针对每个文件的。实际上对文件的读写IRP都发送到Volume设备对象上去了。并不会生成一个“文件设备对象”。
掌握了这些概括的话,我们现在用简单的代码来生成我们的CDO,作为我们开发文件系统驱动的第一步小试牛刀。
我不喜欢用微软风格的代码。太长而且难看。我对大部分的数据结构和函数进行了重定义。为此我写了一个名为wdf.h的头文件帮助我转换。有兴趣的读者可以发邮件向我索取这个文件。没有也没有关系,我总是会写出WD_XXX系列的东西在DDK中的原型。
// -----------------wdf_filter.c中的内容-------------------------
#include "wdf.h"
wd_stat wdff_cdo_create(in wd_drv *driver , in wd_size exten_len , in wd_ustr *name , out wd_dev **device)
{
return wd_dev_create(driver , exten_len , name , wd_dev_disk_fs , wdf_dev_secure_open , wd_false , device);
}
wd_stat wd_main(in wd_drv * driver , in wd_ustr * reg_path)
{
wd_ustr name;
wd_stat status = wd_stat_suc;
//然后我生成控制设备,虽然现在我的控制设备什么都不干
wd_ustr_init(&name , L"\FileSystem\Filters\our_fs_filter");
status = wdff_cdo_create(driver , 0 , &name , &g_cdo);
if (!wd_suc(status))
{
if (status == wd_stat_path_not_found)
{
//这种情况发生于FileSystemFilters路径不存在。这个路径是
//在XP上才加上的。所以2000下会运行到这里。
wd_ustr_init(&name , L"\FileSystem\our_fs_filter");
}
if (!wd_suc(status))
{
wd_printf0("Error : Create CDO Failed.m");
return status;
}
}
wd_printf0("Success : Create CDO Ok.m");
return status;
}
为了让代码看起来像上边的那样,我不得不作了很多转换。如:#define DriverEntry wd_main
一种爽得感觉,终于可以在写看起来更像是main()的函数中工作了。wd_dev_create这个函数内部调用的是IoCreateDevice。而wd_suc实际上是SUCCESS()这样的宏。
// ----------------------wdf.h中的内容------------------------------
#include "ntifs.h"
#define in IN
#define out OUT
#define optional OPTIONAL
#define wd_ustr UNICODE_STRING
#define wdp_ustr PUNICODE_STRING
#define wd_main DriverEntry
// 设备、驱动对象类型
typedef DRIVER_OBJECT wd_drv;
typedef DEVICE_OBJECT wd_dev;
typedef PDRIVER_OBJECT wd_pdrv;
typedef PDEVICE_OBJECT wd_pdev;
enum {
wd_dev_disk_fs = FILE_DEVICE_DISK_FILE_SYSTEM,
wd_dev_cdrom_fs = FILE_DEVICE_CD_ROM_FILE_SYSTEM,
wd_dev_network_fs = FILE_DEVICE_NETWORK_FILE_SYSTEM
};
// 状态相关的类型和宏
typedef NTSTATUS wd_stat;
enum {
wd_stat_suc = STATUS_SUCCESS,
wd_stat_path_not_found = STATUS_OBJECT_PATH_NOT_FOUND,
wd_stat_insufficient_res = STATUS_INSUFFICIENT_RESOURCES,
wd_stat_invalid_dev_req = STATUS_INVALID_DEVICE_REQUEST,
wd_stat_no_such_dev = STATUS_NO_SUCH_DEVICE,
wd_stat_image_already_loaded = STATUS_IMAGE_ALREADY_LOADED,
wd_stat_more_processing = STATUS_MORE_PROCESSING_REQUIRED,
wd_stat_pending = STATUS_PENDING
};
(inline的使用是有所限制的,inline函数一般必须在头文件内,inline只适合函数体内代码简单的函数使用,不能包含复杂的结构控制语句例如while、switch,并且不能内联函数本身不能是直接地跪函数(自己内部还调用自己的函数))
_inline wd_bool wd_suc(wd_stat state) //内聯函數
{
return NT_SUCCESS(state);
}
#define wd_printf0 DbgPrint
_inline wd_void wd_ustr_init(in out wd_ustr* str, in const wd_wchar* chars)
{
RtlInitUnicodeString(str,chars);
};
_inline wd_void wd_ustr_init_em(in out wd_ustr *str ,in wd_wchar *chars, in wd_size size)
{
RtlInitEmptyUnicodeString(str,chars,size);
};
wdf.h这个文件我仅仅节选了需要的部分。以上您已经拥有了一个简单的“驱动”的完整的代码。它甚至可以编译、安装(请修改sfilter.inf文件,其方法不过是将多处的sfilter修改为“our_fs_filter”,希望这个过程中您不会出现问题)。然后把wdf.h和wdf_filter.c放在您新建立的目录下,这个目录下还应该有另两个文件。一个是MakeFile,请从SFilter目录下拷贝。另一个是SOURCES,请输入如下内容:
TARGETNAME = our_fs_filter
TARGETPATH = obj
TARGETTYPE = DRIVER
DRIVERTYPE = FS
BROWSER_INFO = 1
SOURCES = wdf_filter.c
使用DDK编译之后您将得到our_fs_filter.sys。把这个文件与前所描述的inf文件同一目录,按上节所述方法安装。
这个驱动不起任何作用,但是您已经成功的完成了“Hello World”。