前面toaster驱动程序的安装都是通过手动安装的方式,把sys/inf等文件安装到系统中。说实话,这么复杂的过程除了开发者,其他人能流畅的安装上驱动还真是天方夜谭了。如果读者曾在电脑城买过电脑配件,如无线网卡,蓝牙适配器,应该有印象:到手的除了这么设备本身,还会附带一张提供安装程序的小光盘和一份安装说明。安装说明一般都会要求先运行安装程序,然后重启插入设备,之后设备就可以正常使用了。这张光盘就是所谓的预安装光盘,里面的程序就是驱动预安装程序,这正是本篇的主题--驱动预安装程序。
winddk示例目录src/setup/devcon提供了预安装驱动的方法。编译后,可以通过devcon install path_to_inffile hwid的方式安装驱动程序或通过devcon update path_to_inffile hwid的方式更新驱动,前者是后者的扩展。devcon update调用newdev.dll!UpdateDriverForPlugAndPlayDevices函数实现驱动程序的更新。调用时传入inf文件的路径和inf文件中的DeviceId即可。但是按ms文档的说明,UpdateDriverForPlugAndPlayDevices只能为已存在的设备更新驱动,也就所谓的Hardware-first安装方式。
HardwareId [in] A pointer to a NULL-terminated string that supplies the hardware identifier to match <span style="color:#FF0000;">existing devices</span> on the computer.
Remarks UpdateDriverForPlugAndPlayDevices <span style="color:#FF0000;">scans the devices on the system</span> and attempts to install the drivers specified by FullInfPath for any devices that match the specified HardwareId value.
以toaster驱动为例,在底层总线驱动BusEnum.sys已经成功安装的情况下,执行devcon update会提示安装失败,而执行devcon install 能成功预安装驱动:
c:\studio><span style="color:#FF0000;">devcon update</span> C:\studio\toaster\wdm\inf\amd64\simple.inf {b85b7c50-6a0 1-11d2-b841-00c04fad5171}\MsToaster Updating drivers for {b85b7c50-6a01-11d2-b841-00c04fad5171}\MsToaster from C:\st udio\toaster\wdm\inf\amd64\simple.inf. <span style="color:#FF0000;">devcon failed.</span>
c:\studio><span style="color:#FF0000;">devcon install</span> C:\studio\toaster\wdm\inf\amd64\simple.inf {b85b7c50-6a 01-11d2-b841-00c04fad5171}\MsToaster Device node created. Install is complete when drivers are installed... Updating drivers for {b85b7c50-6a01-11d2-b841-00c04fad5171}\MsToaster from C:\st udio\toaster\wdm\inf\amd64\simple.inf. <span style="color:#FF0000;">Drivers installed successfully.</span>
截图显示,devcon install安装驱动后,toaster设备实例为ROOT\TOASTER\0000。从结果来看,驱动虽然安装了,但并没有安装在BusEnum总线下,而出现在root总线下,换句话说toaster驱动并没有运行只是静静的蹲在角落里;当真正的toaster设备接入系统后,由pnp管理器为该设备匹配devcon install预安装的驱动程序。
既然说道UpdateDriverForPlugAndPlayDevices是Hardware-first安装方式,如果先运行enum.exe -p 1(由于没有驱动程序,会在设备管理器下出现带黄色感叹号的Unknow Device),再以devcon update方式后安装设备驱动,也能使前面虚拟出来的toaster设备正常工作。再继续新的内容前,容我先总结一下UpdateDriverForPlugAndPlayDevices函数的作用:本质上,安装驱动需要依靠UpdateDriverForPlugAndPlayDevices这个函数,执行这个函数相当于在设备管理器右键菜单上点击"扫描检测硬件改动"。它会查找当前已经添加到系统中但还没有安装驱动程序的硬件设备,并为之安装驱动。
前面介绍的cmdUpdate函数并不能实现驱动程序预安装,接下来的篇幅我们来看看它的加强版----cmdInstall,它是如何实现驱动程序预安装的。在设备插入系统前,系统没有设备信息,因此cmdInstall所做的是虚拟出一个设备(在root下虚拟一个设备),并将它注册到系统注册表中去。
cmdInstall先调用SetupDiGetINFClass,从inf文件中获取设备类GUID。然后将获得的设备类Guid传递给SetupDiCreateDeviceInfoList用以创建设备信息块列表和SetupDiCreateDeviceInfo创建设备信息块。这个很好理解,windows将属性相近的设备归入同一个设备类,如usb类,hid类。系统应该用一个列表维护设备类中的各个设备,除此之外系统中应该还有一个像面向对象语言中基类一样的描述符,用以描述设备类的属性(我按代码猜的,毕竟windows的pnp管理器没公开)。现在要在这个设备类下创建一个新设备,那必定要获得设备类的描述符,并添加新设备属性---这个设备的HardwareID(就如派生类除了有基类的公共属性还有自己特有的属性),然后往设备类列表中添加新设备。SetupDiSetDeviceRegistryProperty完成新设备的注册工作。一切完成后,还要调用类安装器做设备类安装时的相似工作(如设置注册表,拷贝文件等),这个动作反应到代码中就是调用SetupDiCallClassInstaller。现在虚拟的设备已经存在了,就可以调用UpdateDriverForPlugAndPlayDevices更新驱动了。这些过程完成了我们的目标----预安装驱动程序。
cmdInstall函数很强大,可以预安装总线驱动/功能驱动和简单的过滤驱动,如果inf文件中提供了hardwareID。本篇到此结束,下一篇来讨论一下类过滤驱动和多层过滤驱动的安装。