ReactOS.exe 安装程序分析
概述
reactos.exe 是 ReactOS 的 GUI 第一阶段安装程序 ,位于 d:\reactos\base\setup\reactos\。它是一个 Win32 属性表(Property Sheet)向导,提供 7 步安装流程(欢迎 → 安装类型 → 设备 → 分区 → 摘要 → 安装进度 → 完成),并调用 setuplib 库完成底层安装操作(分区、格式化、文件复制、注册表更新、引导加载器安装)。
1. 源文件清单
| 文件 | 路径 | 用途 |
|---|---|---|
reactos.c |
base\setup\reactos\reactos.c |
主入口、所有页面过程、安装线程、辅助函数 |
drivepage.c |
base\setup\reactos\drivepage.c |
分区/驱动器选择页面 |
treelist.c |
base\setup\reactos\treelist.c |
TreeList 控件(第三方,分区树形列表) |
fileqsup.c |
base\setup\reactos\spapisup\fileqsup.c |
SetupAPI 文件队列支持 |
infsupp.c |
base\setup\reactos\spapisup\infsupp.c |
SetupAPI INF 文件支持 |
reactos.h |
base\setup\reactos\reactos.h |
主头文件 |
treelist.h |
base\setup\reactos\treelist.h |
TreeList 头文件 |
2. 全局数据结构
文件:reactos.c#L23(file:///d:/reactos/base/setup/reactos/reactos.c#L23)
c
HANDLE ProcessHeap; // 进程堆句柄
SETUPDATA SetupData; // 安装配置数据(源路径、目标路径等)
PPARTENTRY InstallPartition; // 目标安装分区
PPARTENTRY SystemPartition; // 系统分区(活动引导分区)
UI_CONTEXT UiContext; // UI 上下文(字体、语言等)
3. wWinMain --- 程序入口
文件:reactos.c#L3210(file:///d:/reactos/base/setup/reactos/reactos.c#L3210)
_tWinMain(INSTANCE, PREVINSTANCE, CMDLINE, CMD_SHOW)
│
├─ 1. ProcessHeap = GetProcessHeap()
├─ 2. ZeroMemory(&SetupData)
│
├─ ★ 3. InitializeSetup() 调用 setuplib:加载 txtsetup.sif
├─ 4. CheckUnattendedSetup() 检查无人值守配置
│
├─ ★ 5. LoadSetupData() 加载分区、NTOS安装、硬件列表
│ ├─ CreatePartitionList() 枚举磁盘分区
│ ├─ CreateNTOSInstallationsList() 检测已有 NT 系统
│ ├─ CreateComputerTypeList() 计算机类型列表
│ ├─ CreateDisplayDriverList() 显示驱动列表
│ └─ CreateKeyboardDriverList() 键盘驱动列表
│
├─ 6. CreateThread(HotkeyThread) Shift+F10 打开 cmd
├─ 7. InitCommonControls + 注册 TreeList
├─ 8. CreateTitleFont() + CreateBoldFont()
│
├─ 9. 创建 9 个属性页:
│ ├─ IDD_STARTPAGE → StartDlgProc (仅非无人值守)
│ ├─ IDD_TYPEPAGE → TypeDlgProc (仅非无人值守)
│ ├─ IDD_UPDATEREPAIR → UpgradeRepair (仅非无人值守)
│ ├─ IDD_DEVICEPAGE → DeviceDlgProc (仅非无人值守)
│ ├─ IDD_DRIVEPAGE → DriveDlgProc (仅非无人值守)
│ ├─ IDD_SUMMARYPAGE → SummaryDlgProc (仅非无人值守)
│ ├─ IDD_PROCESSPAGE → ProcessDlgProc (始终)
│ ├─ IDD_FINISHPAGE → FinishDlgProc (始终)
│ └─ IDD_ABORTPAGE → FinishDlgProc (始终)
│
├─ 10. PropertySheet(所有页面) ← 以向导模式显示
│
├─ 11. 等待安装线程结束
├─ 12. 清理字体 + TreeList
├─ 13. TerminateThread(HotkeyThread)
├─ 14. FinishSetup() 清理 setuplib
│
└─ 15. ExitWindowsEx(EWX_REBOOT) 需要重启时
4. 7 步安装向导详解
页面 1: StartDlgProc --- 欢迎页面
文件:reactos.c#L352(file:///d:/reactos/base/setup/reactos/reactos.c#L352)
c
StartDlgProc(hwndDlg, uMsg, wParam, lParam)
{
WM_INITDIALOG: 保存 SETUPDATA 指针,设置标题字体,居中窗口
PSN_SETACTIVE: 只显示"下一步"按钮,隐藏"上一步"
PSN_KILLACTIVE: 重新显示"上一步"
}
页面 2: TypeDlgProc --- 安装类型
文件:reactos.c#L422(file:///d:/reactos/base/setup/reactos/reactos.c#L422)
用户选择:
├─ "安装 ReactOS" (全新安装)
│ → PSN_WIZNEXT → 跳到设备页面 (IDD_DEVICEPAGE)
│
└─ "升级或修复现有安装"
→ 检测到有多个 NT 系统?
│ Yes → UpdataRepair 页面 (选要升级的系统)
│ No → 直接跳到设备页面
└─ SetupData.RepairUpdateFlag = TRUE
页面 3: UpdataRepairDlgProc --- 升级选择
文件:reactos.c#L789(file:///d:/reactos/base/setup/reactos/reactos.c#L789)
WM_INITDIALOG: 初始化列表视图,显示检测到的 NTOS 安装列表
PSN_WIZNEXT: 设置选中的 NTOS 安装,RepairUpdateFlag=TRUE
IDC_SKIPUPGRADE: 跳过升级,全新安装
页面 4: DeviceDlgProc --- 设备配置
文件:reactos.c#L975(file:///d:/reactos/base/setup/reactos/reactos.c#L975)
c
DeviceDlgProc(hwndDlg, uMsg, wParam, lParam)
{
WM_INITDIALOG:
InitGenericComboList(IDC_COMPUTER, ComputerTypeList) ← 计算机类型
InitGenericComboList(IDC_DISPLAY, DisplayDriverList) ← 显示驱动
InitGenericComboList(IDC_KEYBOARD, KeyboardDriverList) ← 键盘驱动
PSN_WIZNEXT:
保存三个下拉框的选中值到 SetupData
}
设备列表数据来自 txtsetup.sif 文件,由 LoadSetupData() 在程序启动时加载。
页面 5: DriveDlgProc --- 分区选择
文件:drivepage.c#L1593(file:///d:/reactos/base/setup/reactos/drivepage.c#L1593)
这是最复杂的页面。TreeList 控件显示所有磁盘和分区的树形结构。
DriveDlgProc(hwndDlg, uMsg, wParam, lParam)
│
├─ WM_INITDIALOG:
│ InitPartitionList() → 填充 TreeList (磁盘/分区树)
│ 隐藏创建/删除/格式化按钮
│
├─ TVN_SELCHANGED (选中项变化):
│ 根据选中项类型显示/隐藏按钮:
│ ├─ 未分区空间 → 显示"创建分区"
│ ├─ 已分区 → 显示"格式化"/"删除"
│ ├─ 已格式化 → 显示"格式化"
│ └─ 磁盘头 → 无操作按钮
│
├─ IDC_PARTCREATE → PartitionDlgProc() 创建分区对话框
├─ IDC_PARTDELETE → DoDeletePartition() 删除分区
├─ IDC_PARTFORMAT → FormatDlgProc() 格式化对话框
├─ IDC_PARTMOREOPTS → MoreOptsDlgProc() 高级选项 (安装目录)
│
└─ PSN_WIZNEXT:
├─ 检查选中分区 → 未格式化? → 弹出格式化对话框
├─ 设置 InstallPartition = 选中分区
└─ 检查 BIOS 识别性 → 不可引导? → 警告
PartitionDlgProc (drivepage.c#L608(file:///d:/reactos/base/setup/reactos/drivepage.c#L608)) --- 创建分区:
用户输入分区大小 (MB)
可勾选"使用所有可用空间"
调用 CreatePartition(空间, 大小)
→ DoCreatePartition() → 刷新 TreeList 显示
FormatDlgProc (drivepage.c#L555(file:///d:/reactos/base/setup/reactos/drivepage.c#L555)) --- 格式化:
选择文件系统 (FAT/FAT32/BtrFS)
勾选"快速格式化"
确认 → FsVolCommitOpsQueue → 执行实际格式化
MoreOptsDlgProc (drivepage.c#L212(file:///d:/reactos/base/setup/reactos/drivepage.c#L212)) --- 高级选项:
设置安装目录路径 (默认: \ReactOS)
设置引导加载程序位置 (MBR/软盘/不安装)
调用 IsValidInstallDirectory() 验证目录名合法性
页面 6: SummaryDlgProc --- 安装摘要
文件:reactos.c#L1078(file:///d:/reactos/base/setup/reactos/reactos.c#L1078)
PSN_SETACTIVE:
显示用户选择的全部安装选项:
├─ 安装类型 (新安装/升级)
├─ 计算机类型
├─ 显示驱动
├─ 键盘驱动
├─ 目标分区
└─ 目标路径
按钮文本改为"安装"
复选框 IDC_CONFIRM_INSTALL 勾选后启用"安装"按钮
页面 7: ProcessDlgProc --- 安装进度
文件:reactos.c#L2230(file:///d:/reactos/base/setup/reactos/reactos.c#L2230)
PSN_SETACTIVE:
创建安装线程 PrepareAndDoCopyThread() (挂起)
恢复线程 → 正式开始安装
(属性表页面显示进度信息)
PSN_QUERYCANCEL:
停止安装线程
跳到中止页面 (IDD_ABORTPAGE)
5. 核心安装线程: PrepareAndDoCopyThread
文件:reactos.c#L1847(file:///d:/reactos/base/setup/reactos/reactos.c#L1847)
这是执行实际安装操作的函数,运行在独立线程中,分为 8 个子步骤:
c
DWORD WINAPI PrepareAndDoCopyThread(LPVOID lpParameter)
{
│
├─ Step 1: InitSystemPartition()
│ 查找或设置活动系统分区,如无则自动创建
│
├─ Step 2: FsVolCommitOpsQueue() + FsVolCallback()
│ 执行格式化/检查操作队列:
│ ├─ FSVOLNOTIFY_STARTFORMAT → 显示"正在格式化..."
│ ├─ FSVOLNOTIFY_FORMATPROGRESS → 格式化进度百分比
│ ├─ FSVOLNOTIFY_FORMATSTATUS → 文件系统类型
│ ├─ FSVOLNOTIFY_STARTCHKDSK → 显示"正在检查磁盘..."
│ └─ FSVOLNOTIFY_CHKDSKPROGRESS → 检查进度
│
├─ Step 3: InitDestinationPaths()
│ 重新计算目标路径(ARC路径、NT路径、Win32路径)
│
├─ Step 4: PrepareFileCopy()
│ 准备文件复制队列:
│ ├─ 从 txtsetup.sif [SourceDisksFiles] 读取所有文件
│ ├─ 按目标目录分组
│ └─ 创建 SetupAPI 文件队列
│
├─ Step 5: DoFileCopy() + FileCopyCallback()
│ 执行文件复制:
│ ├─ 从 CAB 或源目录复制文件
│ ├─ FILECOPYNOTIFY_PROGRESS → 进度百分比
│ ├─ FILECOPYNOTIFY_ERROR → 文件错误处理(重试/跳过)
│ ├─ FILECOPYNOTIFY_DELETE → 删除旧文件
│ └─ FILECOPYNOTIFY_RENAME → 重命名文件
│
├─ Step 6: InstallSetupInfFile()
│ 创建 $winnt$.inf 安装配置文件(包含分区、目录等配置)
│
├─ Step 7: UpdateRegistry() + RegistryStatus()
│ 创建并更新注册表配置单元:
│ ├─ RegInitializeRegistry() → 初始化 HIVE
│ ├─ 从 HiveInfs.Fresh/HiveInfs.Install 导入
│ ├─ 更新显示设置
│ ├─ 设置区域/键盘布局
│ ├─ 添加代码页
│ ├─ 设置默认页面文件
│ └─ 更新已挂载设备值
│
└─ Step 8: 安装引导加载器
├─ BootLoader=0: 不安装
├─ BootLoader=1: InstallBootcodeToRemovable() ← 软盘
├─ BootLoader=2: InstallBootManagerAndBootEntries() ← MBR
└─ BootLoader=3: 仅安装 VBR
}
6. 辅助函数清单
文件:reactos.c(file:///d:/reactos/base/setup/reactos/reactos.c)
UI 辅助函数
| 函数 | 行号 | 用途 |
|---|---|---|
CenterWindow |
43 | 居中窗口 |
CreateBoldFont |
70 | 创建粗体字体 |
CreateTitleFont |
104 | 创建 12pt 粗体标题字体 |
LoadAllocStringW |
112 | 从资源加载字符串(自动分配) |
FormatAllocStringWV |
147 | 格式化字符串(va_list) |
DisplayMessageV |
172 | 显示消息框 |
DisplayMessage |
241 | 消息框包装 |
DisplayError |
260 | 错误消息框 |
SetWindowResTextW |
281 | 从资源设置窗口文本 |
SetWindowResPrintfVW |
300 | 格式化窗口文本 |
CreateListViewColumns |
558 | 创建列表视图列 |
InitGenericComboList |
597 | 初始化下拉框列表 |
GetSelectedComboListItem |
635 | 获取选中下拉框项 |
InitGenericListView |
656 | 初始化列表视图 |
GetSelectedListViewItem |
693 | 获取选中列表视图项 |
GetSettingDescription |
712 | 获取设置描述文本 |
AddNTOSInstallationItem |
723 | 添加 NT 安装项到列表 |
安装数据加载
| 函数 | 行号 | 用途 |
|---|---|---|
LoadSetupData |
2627 | 加载分区、NT安装、硬件列表 |
InitNtToWin32PathMappingList |
2718 | 初始化 NT→Win32 路径映射 |
FreeNtToWin32PathMappingList |
2726 | 释放路径映射链表 |
ConvertNtPathToWin32Path |
2747 | NT 路径 → Win32 路径转换 |
EnablePrivilege |
3006 | 启用/禁用特权 |
HotkeyThread |
3036 | Shift+F10 → cmd.exe |
GetLocalSetupDllPath |
3085 | 获取 setuplib.dll 路径 |
setupDelayHook |
3144 | 延迟加载钩子(加载 setuplib.dll) |
安装回调函数
| 函数 | 行号 | 用途 |
|---|---|---|
FormatCallback |
1266 | 格式化进度回调 |
ChkdskCallback |
1313 | 磁盘检查回调 |
FsVolCallback |
1331 | 文件系统/卷操作回调 |
FileCopyCallback |
1676 | 文件复制回调 |
RegistryStatus |
1797 | 注册表操作状态回调 |
PropSheet_SetCloseCancel |
1836 | 启用/禁用关闭/取消按钮 |
完成相关
| 函数 | 行号 | 用途 |
|---|---|---|
IsShellActive |
2353 | 检测 Windows shell 是否活动 |
FindUserClosableWindowProc |
2375 | 查找可关闭的用户窗口 |
AreThereInteractiveWindows |
2411 | 检测交互式窗口存在 |
7. 完成页面 & 重启
文件:reactos.c#L2429(file:///d:/reactos/base/setup/reactos/reactos.c#L2429)
FinishDlgProc(hwndDlg, uMsg, wParam, lParam)
├─ WM_INITDIALOG:
│ 设置"必须重启"标志
│ 显示重启倒计时进度条 (300步 = 15秒)
│ (或显示"稍后重启"文字)
│
├─ WM_TIMER:
│ 每50ms递增进度条
│ 300步到 → PostMessage(PSBTN_FINISH)
│
└─ PSN_SETACTIVE:
只显示"完成"按钮(文字改为"重启")
隐藏"上一步"和"下一步"
wWinMain 在 PropertySheet() 返回后:
c
// reactos.c#L3393-3428
WaitForSingleObject(InstallThread, INFINITE); ← 等安装线程结束
FinishSetup(); ← 清理 setuplib
if (NeedsReboot)
ExitWindowsEx(EWX_REBOOT, 0); ← 重启系统
8. setuplib 库调用关系
reactos.exe 通过延迟加载调用 setuplib.dll。以下是所有被调用的 setuplib 函数及其作用:
| 函数 | 所在文件 | 用途 | 调用点 |
|---|---|---|---|
InitializeSetup |
setuplib.c |
初始化安装(源路径、加载 txtsetup.sif) | wWinMain 第3235行 |
CheckUnattendedSetup |
setuplib.c |
检查无人值守配置 | wWinMain 第3253行 |
CreatePartitionList |
partlist.c |
枚举所有磁盘分区 | LoadSetupData 第2630行 |
CreateNTOSInstallationsList |
osdetect.c |
检测已有 NT 系统 | LoadSetupData 第2637行 |
CreateComputerTypeList |
settings.c |
计算机类型列表 | LoadSetupData 第2643行 |
CreateDisplayDriverList |
settings.c |
显示驱动列表 | LoadSetupData 第2644行 |
CreateKeyboardDriverList |
settings.c |
键盘驱动列表 | LoadSetupData 第2645行 |
CreateLanguageList |
settings.c |
语言列表 | LoadSetupData 第2647行 |
CreateKeyboardLayoutList |
settings.c |
键盘布局列表 | LoadSetupData 第2669行 |
IsValidInstallDirectory |
setuplib.c |
验证安装目录 | MoreOptDlgProc 第312行 |
InitSystemPartition |
setuplib.c |
设置活动系统分区 | 安装线程 Step1 |
FsVolCommitOpsQueue |
fsutil.c |
执行格式化/检查 | 安装线程 Step2 |
InitDestinationPaths |
setuplib.c |
初始化目标路径 | 安装线程 Step3 |
PrepareFileCopy |
install.c |
准备文件复制队列 | 安装线程 Step4 |
DoFileCopy |
install.c |
执行文件复制 | 安装线程 Step5 |
InstallSetupInfFile |
setuplib.c |
创建 winntwinntwinnt.inf | 安装线程 Step6 |
UpdateRegistry |
setuplib.c |
创建/更新注册表 | 安装线程 Step7 |
InstallBootManagerAndBootEntries |
bootsup.c |
安装引导管理器到 MBR | 安装线程 Step8 |
InstallBootcodeToRemovable |
bootsup.c |
安装引导到可移动介质 | 安装线程 Step8 |
FinishSetup |
setuplib.c |
清理安装数据 | wWinMain 第3410行 |
GetRegisteredFileSystems |
fsutil.c |
获取注册的文件系统 | DriveDlgProc |
9. 安装流程图
wWinMain
│
├─ InitializeSetup() ← 加载 txtsetup.sif,获取源路径
│
├─ LoadSetupData() ← 枚举磁盘/分区/硬件
│
├─ PropertySheet(9 pages)
│ │
│ ├─ [欢迎] → [安装类型] → [设备] → [分区] → [摘要]
│ │ │
│ │ 用户点击"安装"
│ │ │
│ └────────────────────────────────────────────────┘
│ │
├─ ProcessDlgProc → CreateThread(PrepareAndDoCopyThread)
│ │
│ PrepareAndDoCopyThread: │
│ ├─ Step 1: InitSystemPartition() │
│ ├─ Step 2: FsVolCommitOpsQueue() 格式化 │
│ ├─ Step 3: InitDestinationPaths() │
│ ├─ Step 4: PrepareFileCopy() 准备复制 │
│ ├─ Step 5: DoFileCopy() 复制文件 │
│ ├─ Step 6: InstallSetupInfFile() $winnt$.inf │
│ ├─ Step 7: UpdateRegistry() 注册表 │
│ └─ Step 8: Install Bootloader 引导加载器 │
│ │
├─ WaitForSingleObject(InstallThread) │
├─ FinishSetup() │
└─ ExitWindowsEx(EWX_REBOOT) │
设计要点
| 概念 | 说明 |
|---|---|
| 属性表向导 | 使用 Win32 PropertySheet 控件实现多步向导 |
| 独立安装线程 | 实际安装操作在 PrepareAndDoCopyThread 中异步执行 |
| setuplib 延迟加载 | 通过 setupDelayHook 动态加载 setuplib.dll |
| txtsetup.sif 驱动 | 所有硬件列表从 txtsetup.sif 读取 |
| SetupAPI 文件队列 | 使用 SetupAPI 管理文件复制、删除、重命名操作 |
| 回调机制 | 格式化/检查/复制/注册表操作通过回调更新 UI 进度 |
| Shift+F10 调试 | 热键线程允许在安装过程中打开命令提示符 |
| 无人值守 | 支持通过 unattend.inf 自动跳过所有配置页面 |