# bsod-simulation-app **Repository Path**: ping-ant/bsod-simulation-app ## Basic Information - **Project Name**: bsod-simulation-app - **Description**: No description available - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-17 - **Last Updated**: 2026-05-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # BSOD Wallpaper 把 Windows 桌面壁纸变成经典 Win10 蓝屏 (BSOD) 的动态壁纸,进度百分比 0% → 100% 无限循环。 * 支持 **Windows 10 / 11**(已实测 22H2 / 25H2 / **26200**) * 类 Wallpaper Engine 的 **WorkerW 注入**:BSOD 显示在桌面图标之下、壁纸之上 * 启动**从系统壁纸直接切到蓝色 BSOD**,没有任何黑屏过渡帧(GDI 预填 + Qt 缓存预热) * 系统托盘图标,右键"退出"会同时移除开机自启项 * 单实例运行,DPI 自适应 ## 快速使用(已打包好的 exe) 1. 运行 `BSODWallpaper.exe` 2. 主屏立刻被替换为蓝屏画面 3. 程序已自动加入开机自启 4. 想关闭:托盘 → 右键图标 → 退出 ## 从源码运行 / 打包 ```powershell python -m venv .venv .\.venv\Scripts\Activate.ps1 pip install -r requirements.txt # 一次性生成图标 python resources\generate_icon.py # 直接运行 python main.py # 打包成单文件 exe pyinstaller build.spec --clean --noconfirm # 产物: dist\BSODWallpaper.exe ``` ## 项目结构 ``` . ├── main.py 入口 ├── app/ │ ├── win32_api.py user32 / kernel32 / shcore 的 ctypes 绑定 │ ├── workerw.py WorkerW 查找与窗口注入(核心兼容性逻辑) │ ├── bsod_widget.py BSOD 画面绘制 │ ├── progress.py 进度模拟器 │ ├── tray.py 系统托盘 │ ├── autostart.py HKCU\Run 注册表 │ └── paths.py 冻结/源码模式下的路径解析 ├── resources/ │ ├── icon.ico 由 generate_icon.py 生成 │ └── generate_icon.py ├── build.spec └── requirements.txt ``` ## 当前状态 主体功能已在 Win10 22H2 / Win11 22H2 / 25H2 / **26200** 上全部跑通: - ✅ WorkerW 注入成功,BSOD 铺满整个主显示器 - ✅ 桌面图标依然可见可点 - ✅ 从系统壁纸直接切到 BSOD,无黑屏过渡帧 - ✅ 边框/标题栏已去除 - ✅ 托盘 + 退出 + 自启动 全部正常 技术细节见 [`DEV_GUIDE.md`](./DEV_GUIDE.md)。 ## 实现要点(关键差异点) | 项目 | 关键决策 | |------|----------| | 壁纸层定位(统一) | `FindWindowExW(progman, 0, "WorkerW", NULL)` 找 Progman-子 WorkerW;这是 Win10 22H2~Win11 26200 通用的注入目标 | | 优先快路径 | `find_wallpaper_host` 先查再 spawn;现代 Win11 WorkerW 已经存在时跳过 `WM_SPAWN_WORKER`,避免 Explorer 重建壁纸引发的全桌面黑屏 | | **DWM 渲染** | 我方 HWND 必须自己 `WS_EX_LAYERED` + `SetLayeredWindowAttributes(LWA_ALPHA, 255)`,且**早于 `SetParent`**——否则 Win11 26200 的 DWM 合成器静默丢弃我们 | | **无黑屏过渡** | 在 `WS_VISIBLE` 翻位之前,用 GDI `FillRect` 把 back buffer 涂成 BSOD 蓝,DWM 合成的第一帧就是对的颜色(WPE 同款 trick) | | 首帧加速 | `BSODWidget.prewarm()` 在 show 前预生成 QR + 加载 CJK 字体,Qt 第一次 `paintEvent` 几毫秒就能画完 | | 兼容性兜底 | 找不到 Progman-子 WorkerW 时,再发 `0x052C, (0x0D, 0x01)` 试 Layout A 顶层分裂;最后兜底 SetParent 到 Progman + `HWND_BOTTOM` | | 子窗口样式 | `SetParent` 之后 `WS_POPUP → WS_CHILD`,并剥掉 `WS_CAPTION/WS_THICKFRAME/WS_MINIMIZEBOX/WS_MAXIMIZEBOX/WS_SYSMENU` | | 几何同步 | `setGeometry` + 两次 `processEvents` 让 Qt 吞掉跨进程 reparent 期间漏发的 WM_SIZE | | DPI | `SetProcessDpiAwarenessContext(-4)` 必须在 `QApplication` 构造之前 | | 自启动路径 | 一律使用 `sys.executable`,避免写入临时目录脚本路径 | | 单实例 | Named mutex `Global\BSODWallpaper_v1_singleton` | ### 我们踩过的坑(不要重复尝试) | 失败方案 | 现象 | |----------|------| | 顶层 EnumWindows 找壁纸 WorkerW | Win11 26200 上根本不创建 | | SetParent 到 Progman + HWND_BOTTOM | DefView 全屏不透明,盖住我们 | | SetParent 到 SHELLDLL_DefView 之下层 | DefView 是 LAYERED,跨进程低 Z 序子窗口被合成器丢弃 | | SetParent 到 Progman-子 WorkerW 但**不加 LAYERED** | 跨进程非 LAYERED 子被 DWM 合成器静默丢弃 | | CreateWindowEx 自建 WorkerW 子窗口(未加 LAYERED)| 同上,不显示 | | 每次启动都无脑发 `WM_SPAWN_WORKER` | Win11 26200 上 Explorer 真的重建壁纸,全桌面闪 ~1s 黑 | | `bsod.show()` 在 reparent 前 | 左上角先闪一个 ~800×600 黑色 popup | | 翻 `WS_VISIBLE` 后才让 Qt 画首帧 | Qt cold-start paintEvent ~1s,期间全桌面黑屏 | ## 退出行为 托盘的"退出"会执行: 1. `remove_autostart()` — 删除注册表项 2. 隐藏并销毁 BSOD widget(WorkerW 中的画面随之消失) 3. `QApplication.quit()` 不会主动恢复用户原本的壁纸 —— 系统会自然重绘桌面背景。 ## 已知边界 * 切换到 Spotlight / 幻灯片壁纸后偶发首次注入失败,会在 3 次重试内恢复 * 若 Explorer 崩溃重启,需要重启本程序才能重新注入 * 仅在主显示器显示,其它显示器维持系统壁纸