一般来说,不管学习啥第一个实例总是不那么容易跑起来。为了对相关的类型和函数有更深的印象,第一个实例应该全部手工输入,可以参照相关的实例或文档说明。只有通过手工输入所有的代码,在编译不过时不断的比照实例或文档找出问题的产生的原因才能很快的入门。
在看技术文档的过程中最后能马上实践,而且前面的例子最好能跑起来,这样继续后面的阅读才有意义,不然总有那种想实践又不知如何下手的感觉,还有就是看到后面前面的就忘掉了。只有不断的实践才能很快的掌握所学的知识。(对E文也应该如此,只不过本人总是找借口偷懒,所以至今还是马马虎虎)
第一个非WDM实例
所谓第一个实例,当然是越简单也越好,就象各种开发工具文档中的Hello World工程一样。为了简单而且让驱动能跑起来,我们先实践非WDM的实例,只实践了两个必须的函数:驱动入口函数DriverEntry和驱动卸载函数DeviceUnload。
为了能用VC IDE来开发驱动(以VC6为例,配上Visual Assist X),我们新建一个空的Win32 Console Application,实例名为DrvSample。添加空的DrvSample.h和DrvSample.c文件到工程中,然后我们给Additional include dictionaries添加C:\WINDDK\inc\ddk\w2k(Setting->C/C++->Category<Preprocessor>),在XP系统下或开发WDM项目要修改成对就的目录。
1. DrvSample.h的内容:
#ifndef _DRV_SAMPLE_H_
#define _DRV_SAMPLE_H_
#include <ntddk.h>
#if DBG
#define DRVSAMPLE_KDPRINT(_x_) \
DbgPrint("DRVSAMPLE.SYS: "); \
DbgPrint _x_
#else
#define DRVSAMPLE_KDPRINT(_x_)
#endif
#define DRVSAMPLE_DEVICE_NAME_U L"\\Device\\DRVSAMPLE"
#define DRVSAMPLE_DOS_DEVICE_NAME_U L"\\DosDevices\\DrvSample"
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pRegistryPath
);
VOID
DeviceUnload(
IN PDRIVER_OBJECT pDriverObject
);
#endif // _DRV_SAMPLE_H_
2. DrvSample.c的内容:
#include "DrvSample.h"
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pRegistryPath
)
{
NTSTATUS status;
PDEVICE_OBJECT pDevObj;
UNICODE_STRING uniDeviceName;
UNICODE_STRING uniDosDeviceName;
DRVSAMPLE_KDPRINT(("DriverEntry Enter\n"));
RtlInitUnicodeString(&uniDeviceName, DRVSAMPLE_DEVICE_NAME_U);
status = IoCreateDevice(
pDriverObject,
0,
&uniDeviceName,
FILE_DEVICE_UNKNOWN,
0,
(BOOLEAN)FALSE,
&pDevObj
);
if (!NT_SUCCESS(status))
{
return status;
}
DbgPrint("DeviceObject %p\n", pDevObj);
RtlInitUnicodeString(&uniDosDeviceName, DRVSAMPLE_DOS_DEVICE_NAME_U);
status = IoCreateSymbolicLink(&uniDosDeviceName, &uniDeviceName);
if (!NT_SUCCESS(status))
{
IoDeleteDevice(pDevObj);
return status;
}
pDriverObject->DriverUnload = DeviceUnload;
DRVSAMPLE_KDPRINT(("DriverEntry Leave\n"));
return status;
}
VOID
DeviceUnload(
IN PDRIVER_OBJECT pDriverObject
)
{
UNICODE_STRING uniDosDeviceName;
DRVSAMPLE_KDPRINT(("DeviceUnload Leave\n"));
RtlInitUnicodeString(&uniDosDeviceName, DRVSAMPLE_DOS_DEVICE_NAME_U);
IoDeleteSymbolicLink(&uniDosDeviceName);
IoDeleteDevice(pDriverObject->DeviceObject);
DRVSAMPLE_KDPRINT(("DeviceUnload Leave\n"));
}
3. 在DrvSample.c的同一目录下添加Makefile文件,内容总是:
#
# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
# file to this component. This file merely indirects to the real make file
# that is shared by all the driver components of the Windows NT DDK
#
!INCLUDE $(NTMAKEENV)\makefile.def
4. 在DrvSample.c的同一目录下添加Sources文件,内容是:
TARGETNAME=DrvSample
TARGETPATH=obj
TARGETTYPE=DRIVER
SOURCES=DrvSample.c
其中TARGETNAME是要生成的SYS文件名,不包括.sys
5. 用命令行方式编译:
参见驱动开发系列(配置篇)的第5点。
6. 安装:
1) 将编译出得到的DrvSample.sys拷到%windir%\system32\drivers中
2) 往注册表中添加注册项:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DrvSample]
"ErrorControl"=dword:00000001
"Start"=dword:00000001
"Type"=dword:00000001
将上面的代码保存成.reg文件,DrvSample必须与驱动文件名一致。
注意:文档中Start的默认设置是3(Demand),搞得我总是在设备管理器中找不到驱动,试下来才发现设置成1(System)才可以成功安装。需要重新启动机器才能看得到。
Start: 0 – Boot, 1 – System, 2 – Automation, 3 – Demand, 4 – Disabled
7. 测试:
重新启动之后打开设备管理器,在View菜单中选择“显示隐藏设备”,然后在“非即插即用设备”列表中可以看到DrvSample项,双击可以打开属性窗体,切换到Driver而可以启动/停止驱动。在启动/停止驱动时如果运行Dbgview.exe程序就可以看到输出的信息。
万事开头难,完成了第一个可以跑的驱动后,下一步再测试代码就简单多了。