9、Windows驱动程序的入口函数规定为_DriverEntry@8,所以用C++编写时要用extern。
驱动程序中,不能使用编译器运行时函数,甚至C语言中的malloc,C++的new函数都不能用,因为他们在VC中的实现都是调用了Win32 API了。要用的话,必须自己重载new等运算符。大部分运行时函数是通过Win32 API实现的。在内核模式下无法调用用户模式的程序,而用户模式下通过参数审核可以调用内核态程序。
内核态的运行时函数来替换用户态的运行时函数,一般形如RtlXXXX。
设备名称用UNICODE字符串指定,且必须是“\Device\[设备名]”形式。用户模式下,通过两种方法找到设备:
法1:符号连接
法2:通过设备接口。
一般虚拟设备用FILE_DEVICE_UNKNOWN类型。
******************
"采用C++编程,所以需要用extern "C",因为我们导入的是C的函数的符号表",系统内核是有C语言编写的,连接时要调用相应的函数,所以应当把驱动头文件编译成C语言形式。
10、用build编译
Build首先设置环境变量,然后调用nmake。nmake解析makefile,调用cl,link来编译程序。
Makefile中依赖关系如下声明:
A:B,C
Action
说明:A依赖于B和C。如果A的最后修改时间早于B和C任一个文件的最后修改时间,则执行action。Action前面是tab。
用VC编译,我们在另一篇中论述。
11、查看调试信息
法一:打印信息
尽量用KdPrint,在free版本中不显示。同prrinf使用,双括号,宽字符用%ws或%S。
法二:用DriverStudio中的DriverMonitor。不过可以用免费的DbgView,下载地址是:
http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx
12、加载驱动
NT式驱动程序的加载,是基本服务的方式加载的。设备驱动程序的动态加载主要由服务控制管理程序(SCM)系统组件完成的。Windows服务应用程序遵循控制管理器。Driver Service是Windows服务的一个特例,它遵守Windows服务的协议。
图 加载 P72
图 卸载驱动流程 P72
代码 加载和卸载驱动代码
1 #include < windows.h >
2 #include < winsvc.h >
3 #include < conio.h >
4 #include < stdio.h >
5
6 #define DRIVER_NAME "HelloDDK"
7 #define DRIVER_PATH "..\\MyDriver\\MyDriver_Check\\HelloDDK.sys"
8
9 // 装载NT驱动程序
10 BOOL LoadNTDriver( char * lpszDriverName, char * lpszDriverPath)
11 {
12 char szDriverImagePath[ 256 ];
13 // 得到完整的驱动路径
14 GetFullPathName(lpszDriverPath, 256 , szDriverImagePath, NULL);
15
16 BOOL bRet = FALSE;
17
18 SC_HANDLE hServiceMgr = NULL; // SCM管理器的句柄
19 SC_HANDLE hServiceDDK = NULL; // NT驱动程序的服务句柄
20
21 // 打开服务控制管理器
22 hServiceMgr = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
23
24 if ( hServiceMgr == NULL )
25 {
26 // OpenSCManager失败
27 printf( " OpenSCManager() Faild %d ! \n " , GetLastError() );
28 bRet = FALSE;
29 goto BeforeLeave;
30 }
31 else
32 {
33 /// /OpenSCManager成功
34 printf( " OpenSCManager() ok ! \n " );
35 }
36
37 // 创建驱动所对应的服务
38 hServiceDDK = CreateService( hServiceMgr,
39 lpszDriverName, // 驱动程序的在注册表中的名字
40 lpszDriverName, // 注册表驱动程序的 DisplayName 值
41 SERVICE_ALL_ACCESS, // 加载驱动程序的访问权限
42 SERVICE_KERNEL_DRIVER, // 表示加载的服务是驱动程序
43 SERVICE_DEMAND_START, // 注册表驱动程序的 Start 值
44 SERVICE_ERROR_IGNORE, // 注册表驱动程序的 ErrorControl 值
45 szDriverImagePath, // 注册表驱动程序的 ImagePath 值
46 NULL,
47 NULL,
48 NULL,
49 NULL,
50 NULL);
51
52 DWORD dwRtn;
53 // 判断服务是否失败
54 if ( hServiceDDK == NULL )
55 {
56 dwRtn = GetLastError();
57 if ( dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_EXISTS )
58 {
59 // 由于其他原因创建服务失败
60 printf( " CrateService() Faild %d ! \n " , dwRtn );
61 bRet = FALSE;
62 goto BeforeLeave;
63 }
64 else
65 {
66 // 服务创建失败,是由于服务已经创立过
67 printf( " CrateService() Faild Service is ERROR_IO_PENDING or ERROR_SERVICE_EXISTS! \n " );
68 }
69
70 // 驱动程序已经加载,只需要打开
71 hServiceDDK = OpenService( hServiceMgr, lpszDriverName, SERVICE_ALL_ACCESS );
72 if ( hServiceDDK == NULL )
73 {
74 // 如果打开服务也失败,则意味错误
75 dwRtn = GetLastError();
76 printf( " OpenService() Faild %d ! \n " , dwRtn );
77 bRet = FALSE;
78 goto BeforeLeave;
79 }
80 else
81 {
82 printf( " OpenService() ok ! \n " );
83 }
84 }
85 else
86 {
87 printf( " CrateService() ok ! \n " );
88 }
89
90 // 开启此项服务
91 bRet = StartService( hServiceDDK, NULL, NULL );
92 if ( ! bRet )
93 {
94 DWORD dwRtn = GetLastError();
95 if ( dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_ALREADY_RUNNING )
96 {
97 printf( " StartService() Faild %d ! \n " , dwRtn );
98 bRet = FALSE;
99 goto BeforeLeave;
100 }
101 else
102 {
103 if ( dwRtn == ERROR_IO_PENDING )
104 {
105 // 设备被挂住
106 printf( " StartService() Faild ERROR_IO_PENDING ! \n " );
107 bRet = FALSE;
108 goto BeforeLeave;
109 }
110 else
111 {
112 // 服务已经开启
113 printf( " StartService() Faild ERROR_SERVICE_ALREADY_RUNNING ! \n " );
114 bRet = TRUE;
115 goto BeforeLeave;
116 }
117 }
118 }
119 bRet = TRUE;
120 // 离开前关闭句柄
121 BeforeLeave:
122 if (hServiceDDK)
123 {
124 CloseServiceHandle(hServiceDDK);
125 }
126 if (hServiceMgr)
127 {
128 CloseServiceHandle(hServiceMgr);
129 }
130 return bRet;
131 }
132
133 // 卸载驱动程序
134 BOOL UnloadNTDriver( char * szSvrName )
135 {
136 BOOL bRet = FALSE;
137 SC_HANDLE hServiceMgr = NULL; // SCM管理器的句柄
138 SC_HANDLE hServiceDDK = NULL; // NT驱动程序的服务句柄
139 SERVICE_STATUS SvrSta;
140 // 打开SCM管理器
141 hServiceMgr = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
142 if ( hServiceMgr == NULL )
143 {
144 // 带开SCM管理器失败
145 printf( " OpenSCManager() Faild %d ! \n " , GetLastError() );
146 bRet = FALSE;
147 goto BeforeLeave;
148 }
149 else
150 {
151 // 带开SCM管理器失败成功
152 printf( " OpenSCManager() ok ! \n " );
153 }
154 // 打开驱动所对应的服务
155 hServiceDDK = OpenService( hServiceMgr, szSvrName, SERVICE_ALL_ACCESS );
156
157 if ( hServiceDDK == NULL )
158 {
159 // 打开驱动所对应的服务失败
160 printf( " OpenService() Faild %d ! \n " , GetLastError() );
161 bRet = FALSE;
162 goto BeforeLeave;
163 }
164 else
165 {
166 printf( " OpenService() ok ! \n " );
167 }
168 // 停止驱动程序,如果停止失败,只有重新启动才能,再动态加载。
169 if ( ! ControlService( hServiceDDK, SERVICE_CONTROL_STOP , & SvrSta ) )
170 {
171 printf( " ControlService() Faild %d !\n " , GetLastError() );
172 }
173 else
174 {
175 // 打开驱动所对应的失败
176 printf( " ControlService() ok !\n " );
177 }
178 // 动态卸载驱动程序。
179 if ( ! DeleteService( hServiceDDK ) )
180 {
181 // 卸载失败
182 printf( " DeleteSrevice() Faild %d !\n " , GetLastError() );
183 }
184 else
185 {
186 // 卸载成功
187 printf( " DelServer:eleteSrevice() ok !\n " );
188 }
189 bRet = TRUE;
190 BeforeLeave:
191 // 离开前关闭打开的句柄
192 if (hServiceDDK)
193 {
194 CloseServiceHandle(hServiceDDK);
195 }
196 if (hServiceMgr)
197 {
198 CloseServiceHandle(hServiceMgr);
199 }
200 return bRet;
201 }
202
203 void TestDriver()
204 {
205 // 测试驱动程序
206 HANDLE hDevice = CreateFile( " \\\\.\\HelloDDK " ,
207 GENERIC_WRITE | GENERIC_READ,
208 0 ,
209 NULL,
210 OPEN_EXISTING,
211 0 ,
212 NULL);
213 if ( hDevice != INVALID_HANDLE_VALUE )
214 {
215 printf( " Create Device ok ! \n " );
216 }
217 else
218 {
219 printf( " Create Device faild %d ! \n " , GetLastError() );
220 }
221 CloseHandle( hDevice );
222 }
223
224 int main( int argc, char * argv[])
225 {
226 // 加载驱动
227 BOOL bRet = LoadNTDriver(DRIVER_NAME,DRIVER_PATH);
228 if ( ! bRet)
229 {
230 printf( " LoadNTDriver error\n " );
231 return 0 ;
232 }
233 // 加载成功
234
235 printf( " press any to create device!\n " );
236 getch();
237
238 TestDriver();
239
240 // 这时候你可以通过注册表,或其他查看符号连接的软件验证。
241 printf( " press any to unload the driver!\n " );
242 getch();
243
244 // 卸载驱动
245 UnloadNTDriver(DRIVER_NAME);
246 if ( ! bRet)
247 {
248 printf( " UnloadNTDriver error\n " );
249 return 0 ;
250 }
251
252 return 0 ;
253 }
254
255
关于WDM驱动的手工加载,见[1]。
13、NT驱动程序的基本结构
1)数据结构
DEVICE_OBJECT
The DEVICE_OBJECT structure is used by the operating system to represent a device object. A device object represents a logical, virtual, or physical device for which a driver handles I/O requests.
http://msdn.microsoft.com/en-us/library/ff543147%28VS.85%29.aspx
每个设备对象指针指向向一个设备对象,最后一个设备对象指向空。
图 设备对象布局图 P92
设备扩展对象定义自己定义的结构体,驱动程序中尽是避免使用全局变量,因为会影响同步,而把全局变量存在设备扩展里。
driver object
Each driver object represents the image of a loaded kernel-mode driver. A pointer to the driver object is an input parameter to a driver's DriverEntry, AddDevice, and optional Reinitialize routines and to its Unload routine, if any.
每个驱动程序与唯一的驱动对象相对应,这个驱动对象是在驱动加载时候,被内核中的对象管理器创建。驱动对象为驱动和个实例被内核加载,并且一个驱动只能加载一个实例。
图 驱动对象布局图 P88
其它参见:
http://www.cnblogs.com/mydomain/archive/2010/10/16/1853235.html
14、DriverEntry主要是对系统进程创建的驱动对象进行初始化。
15、WDM程序的基本结构
一般都是基于分层的,完成一个设备的操作,至少要由两个驱动设备共同完成。一个是PDO(物理设备对象),一个是FDO(功能设备对象),其关系是附加与被附加关系。当PC中插入某个设备时,总线驱动自动创建PDO,由PDO引出FDO。
图 FDO与PDO P103
过滤驱动不是必须的。
与NT的不同:
· 增加了对AddDevice函数的设备。操作系统加载PDO后,调用驱动的AddDevice例程,AddDevice例程负责创建FDO,并且附加到PDO之上。
·设备对象在AddDevice例程中创建。
·必须加入IRP_MJ_PNP的派遣回调函数。
PDO会通过AttachedDevice子域知道它上面的设备是FDO或过滤驱动,但是不知道下面的,可以通过定制自己的设备扩展来记录地址。
16、设备的层次结构
图 设备对象堆栈 P104
图 垂直结构 P111,P113
参考
【1】Windows 驱动开发技术详解
【2】http://msdn.microsoft.com/en-us/library/ff565757%28VS.85%29.aspx
【3】Windows驱动学习笔记,灰狐