子文档三:Zadig 核心架构与主程序分析
zadig.c 是 Zadig 工具的主程序,它是一个完整的 Windows GUI 应用程序,使用原生 Win32 API 构建,不依赖 MFC 或 .NET Framework。Zadig 的主要功能是提供一个用户友好的界面,让用户选择 USB 设备并安装相应的驱动程序。其代码量约 2000 行,是 libwdi 最复杂也最完整的应用示例。
整体架构
Zadig 采用经典的 "事件驱动 + 模态对话框" 模式。其核心是一个主对话框(MAIN_DIALOG),所有控件(下拉列表、按钮、编辑框、日志窗口等)都放置在此对话框上。主窗口过程 main_callback 处理所有 Windows 消息,包括控件通知、设备变化事件、自定义消息等。
关键数据结构与全局状态
Zadig 使用大量全局变量管理应用程序状态,这些变量在 zadig.c 顶部声明,包括:
struct wdi_device_info *device, *list:当前选中的设备和完整设备链表。struct wdi_options_*:多个 options 结构体,用于配置 libwdi 的行为。int current_device_index:下拉列表中的当前选中项索引。BOOL advanced_mode:是否显示高级选项(日志窗口、文件夹浏览等)。BOOL create_device:是否处于"手动创建设备"模式(即不通过枚举,而是手动输入 VID/PID)。enum wcid_state has_wcid:当前设备是否支持 WCID。UINT64 target_driver_version:目标驱动版本,用于比较并提示升级/降级。
全局状态变量使得 UI 状态与底层逻辑紧密关联,但也增加了代码的耦合度。Zadig 通过精心组织的消息处理来维护状态一致性。
主窗口初始化流程
main_callback 处理 WM_INITDIALOG 时执行以下关键步骤:
- 调用
init_dialog:该函数完成所有控件的创建、字体设置、图标加载、工具提示创建等。 - 调用
parse_ini:读取zadig.ini配置文件,应用用户自定义的默认选项(如默认驱动类型、日志级别、高级模式等)。 - 调用
set_loglevel:根据配置设置日志级别。 - 调用
set_default_driver:检测系统支持的驱动类型,选择合适的默认驱动。 - 调用
toggle_advanced:根据配置显示或隐藏高级控件。 - 发送
UM_REFRESH_LIST消息:触发设备列表的初始枚举和显示。
设备枚举与显示
设备列表的刷新由 UM_REFRESH_LIST 消息驱动。其处理逻辑为:
- 调用
wdi_create_list获取设备链表。 - 调用
display_devices将设备描述填充到下拉列表(IDC_DEVICELIST)中,并将设备结构体指针作为itemdata存储,便于后续快速检索。 - 若设备列表为空,则禁用"编辑描述"和"安装"按钮。
- 自动选择当前设备(基于
current_device_hardware_id或默认选择第一个)。
设备选择与信息显示
当用户从下拉列表中选择设备时,CBN_SELCHANGE 事件触发以下操作:
- 通过
ComboBox_GetItemData获取对应的wdi_device_info指针。 - 更新
device全局变量。 - 读取
VID、PID、MI、驱动名称、驱动版本等信息,并显示在对应的静态文本和编辑框中。 - 调用
wdi_get_vendor_name解析 VID 对应的厂商名称,若无法解析则显示"提交厂商"按钮(IDC_VID_REPORT),引导用户向 USB ID 仓库提交数据。 - 检测设备的
compatible_id是否以USB\MS_COMP_开头,判断是否为 WCID 设备,并相应地显示 WCID 状态图标。 - 根据当前设备的驱动类型和版本,计算"安装"、"替换"、"升级"、"降级"、"重装"等按钮动作,并更新 UI 提示。
安装流程
当用户点击"安装"按钮(IDC_INSTALL)时,主程序调用 install_driver 函数,其流程为:
- 若处于"手动创建设备"模式,从编辑框中读取描述、VID、PID、MI,构造一个临时的
wdi_device_info对象。 - 调用
wdi_prepare_driver准备驱动文件。 - 若成功,调用
wdi_install_driver执行安装。 - 根据返回值显示成功/失败消息框,并刷新设备列表(
PostMessage(WM_DEVICECHANGE))以反映驱动状态的变化。
安装过程中,installation_running 全局变量被设为 TRUE,禁止用户在安装过程中进行其他操作(如切换设备、修改选项),避免干扰。
设备热插拔处理
Zadig 通过处理 WM_DEVICECHANGE 消息来响应 USB 设备的插入和拔出。由于该消息可能被频繁触发,Zadig 采用 "延迟通知" 策略:
- 首次收到
WM_DEVICECHANGE时,创建一个后台线程,该线程等待NOTIFICATION_DELAY(1000ms)后发送自定义消息UM_DEVICE_EVENT回到主线程。 - 在主线程收到
UM_DEVICE_EVENT后,刷新设备列表。若当前处于"手动创建"模式,则弹出提示询问用户是否放弃修改并刷新。
这种设计避免了在设备快速插拔时多次刷新 UI,同时也保证了当用户插入一个新设备后,界面能及时更新。
高级功能菜单
Zadig 提供了丰富的菜单选项,包括:
- 设备 :创建新设备(手动输入 VID/PID)、打开预设配置文件(
.cfg)。 - 选项:高级模式、列出所有设备、忽略集线器、生成 CAT、签名 CAT、日志级别选择。
- 帮助:在线帮助、检查更新、关于。
这些菜单项通过 WM_COMMAND 处理,调用相应的功能函数(如 toggle_advanced、toggle_driverless 等)。