前面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 existing devices on the computer.
Remarks
UpdateDriverForPlugAndPlayDevices scans the devices on the system 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>devcon update 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.
devcon failed.
c:\studio>devcon install 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.
Drivers installed successfully.
既然说道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。本篇到此结束,下一篇来讨论一下类过滤驱动和多层过滤驱动的安装。
最后附上参考文档