Skip to content

cmcccdn/defaultapp_locker

Repository files navigation

默认应用锁定器 (Default App Locker)

中文 | English

Windows 默认应用 锁定 工具 —— 只需配置一次文件后缀关联,后台 Windows 服务会持续地把它写回去,让其他安装程序 / Windows 更新 / 各种应用再也偷不走你的默认打开方式。

GUI 配置器 + 后台 Windows 服务,使用 Python 编写,可打包成独立 EXE。

致谢。 本项目是在 3089464667/default-app 的基础上重写并扩展而来。assoc/ftype + 直接写注册表 + UserChoice 覆盖三层叠加的核心思路,源自该项目。


目录

  1. 功能特性
  2. 实现原理 — 锁定技术
  3. 架构
  4. 安装
  5. 从源码构建
  6. 配置
  7. 优先级体系
  8. 预设分组
  9. 项目结构
  10. 许可
  11. 鸣谢

功能特性

  • 把任意文件后缀锁定到指定应用
  • 后台 Windows 服务按可配置的间隔(默认 300 秒)反复写入规则
  • 现代化 CustomTkinter 界面,集成系统托盘
  • 内置预设分组(常见图片、全部图片、常见视频、全部视频、常见文本、常见文档)
  • 两级优先级体系:单条规则可覆盖分组规则
  • 支持拖拽排序规则
  • 国际化:简体中文 + 英文
  • PyInstaller 单 EXE 打包,可选 Inno Setup / NSIS 安装包

实现原理 — 锁定技术

Windows 提供了若干种、大多没有正式文档且互相重叠的机制,用来决定某个后缀由哪个应用打开。任何一种都可能被另一种覆盖,且 Windows 自己还会周期性地重写它们。要让一个默认关联真正 生效并保持,本项目把以下所有手段叠加使用,再用一个 Windows 服务按定时器周期性重新写入。

核心实现位于 defaultapplocker/core/locker.py

第 1 层 — assocftype(古老的 cmd.exe 机制)

对每条规则我们都会调用经典的 Windows 命令:

assoc .jpg=JPGFile
ftype JPGFile="C:\Windows\System32\mspaint.exe" "%1"

assoc文件后缀 映射到一个 ProgID(逻辑处理器名),ftype 把 ProgID 映射到具体的命令行。这是历史最久、最被广泛尊重的机制 —— 很多命令行工具和老式应用会查询它。

源码:defaultapplocker/core/locker.py 中的 set_assoc()set_ftype()

第 2 层 — 直接写入 HKEY_CLASSES_ROOT

assoc / ftype 只覆盖了注册表的一部分。我们再用 winreg 直接写入:

  • HKCR\.<ext> → ProgID
  • HKCR\<ProgID>\shell\open\command"<app>" "%1"

资源管理器的"打开方式"对话框、以及大多数现代 Win32 应用,在用户没有显式选择时都会读这里。

源码:defaultapplocker/core/locker.py 中的 set_registry_assoc()

第 3 层 — 用户级 UserChoice 覆盖

现代 Windows(8+)允许每个用户在以下位置把后缀绑定到一个应用:

HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.<ext>\UserChoice
    ProgId  =  <ProgID>
    Hash    =  <SHA-256 派生值>

只要这里有 UserChoice 项,它就会覆盖该用户在 HKCR 下的一切。为了不让第三方应用通过这个键偷走默认值,我们自己写入 UserChoice,Hash 由 ProgID + 用户名 + 时间戳派生。

说明:微软在 Win10/11 中使用了一个未公开的专有算法生成 UserChoice.Hash 用于防篡改。我们写入的是尽力而为的 SHA-256 标记 —— 它能满足"只检查 UserChoice 是否存在"的程序,但 Windows 设置下次仍可能弹出"你要如何打开此文件?"。后台服务(第 4 层)通过立即把它写回去来弥补。

源码:defaultapplocker/core/locker.py 中的 set_user_choice()

第 4 层 — 通过 Windows 服务周期性重写

即使前面三层都做了,Windows 更新、MSI 安装程序以及"把我设为默认"提示仍能在任何时候改写注册表。本项目附带一个真正的 Windows 服务(DefaultAppLockerService),它会:

  1. C:\ProgramData\defaultapp-locker\rules.json 读取规则
  2. 对每条已启用的规则,通过 verify_rule() 检查当前关联
  3. 如果当前处理器与期望不一致,重新执行上面四层的写入
  4. 休眠 checkInterval 秒(默认 300)后继续

服务基于 pywin32win32serviceutil.ServiceFramework)实现,会出现在 services.msc 中,可设为开机自启,注销后仍在运行,以 LocalSystem 身份执行。工作循环跑在守护线程里,用 Win32 事件句柄发出停止信号以保证干净停机。

源码:defaultapplocker/service/locker_service.py

为什么需要全部四层?

谁来写 谁会读 谁能击败它
assoc / ftype cmd.exe 内建 老式 / CLI 程序 重新跑 assoc 或改注册表
直接写 HKCR winreg 资源管理器、多数 Win32 程序 UserChoice、其它安装程序
UserChoice(HKCU) winreg 现代 Windows Shell Windows 设置、微软算法
后台服务 pywin32 服务 不读 —— 它 重写 一切 停掉/卸载这个服务

四层叠加,基本覆盖了 Windows 10/11 上读写默认关联的所有路径。


架构

+--------------------+         +-----------------------+
|  GUI (gui.py)      |  编辑   |  rules.json           |
|  CustomTkinter     +--------->  C:\ProgramData\      |
|  + 系统托盘        |         |  defaultapp-locker\   |
+--------+-----------+         +-----------+-----------+
         |                                 |
         | 安装 / 启动 / 停止 服务         | 每隔
         |                                 | checkInterval 秒读一次
         v                                 v
+--------+-----------+         +-----------+-----------+
|  服务控制          | pywin32 |  Windows 服务:       |
|  (win32serviceutil)+--------->  DefaultAppLocker     |
+--------------------+         |  Service              |
                               +-----------+-----------+
                                           |
                                           | 对每条规则
                                           v
                         +-----------------+----------------+
                         | 第 1 层: assoc / ftype           |
                         | 第 2 层: HKCR 直接写             |
                         | 第 3 层: HKCU UserChoice + Hash  |
                         +----------------------------------+
  • gui.py —— 配置器 GUI 入口
  • service.py —— Windows 服务入口
  • defaultapplocker/core/locker.py —— 四层锁定原语
  • defaultapplocker/core/config.py —— 规则 JSON 的加载/保存/排序
  • defaultapplocker/core/i18n.py —— 中英双语字符串表
  • defaultapplocker/service/locker_service.py —— ServiceFramework 子类 + 工作循环
  • defaultapplocker/gui/main_window.py —— 主窗口
  • defaultapplocker/gui/tray.py —— 系统托盘图标(pystray)

技术栈

关注点 选型
语言 Python 3.8+
GUI 工具包 customtkinter(现代化 Tk)
系统托盘 pystray + Pillow
Win32 / 服务 pywin32
注册表 winreg(标准库)
命令层 subprocess.run(带 shell=True,用于 assoc / ftype
Hash hashlib.sha256(UserChoice 标记)
打包 PyInstaller(scripts/ 下的 .spec
安装包 Inno Setup(首选)+ NSIS(备选)

安装

普通用户

从发布页下载 DefaultAppLocker_Setup.exe(或自行打包,见从源码构建)。以管理员身份运行,按向导安装即可,然后从开始菜单启动 Default App Locker

系统要求

  • Windows 10 或 Windows 11
  • 管理员权限(写注册表与安装服务都需要)

从源码构建

所有打包脚本都在 scripts/ 下,文件名全部使用英文。完整流程见 docs/BUILD.md,安装包流程见 docs/INSTALLER.md

快速上手(项目根目录下执行):

pip install -r requirements.txt
scripts\build.bat

会产出:

output\
    DefaultAppLocker.exe          (GUI)
    DefaultAppLockerService.exe   (Windows 服务)

如果要 Windows 安装包(需要 Inno Setup 6):

scripts\build_installer.bat

不打包直接从源码运行:

python gui.py                  REM 启动 GUI
python service.py install      REM 安装服务(需要管理员)
python service.py start

或使用辅助脚本:

scripts\start_gui.bat

配置

规则保存在:

C:\ProgramData\defaultapp-locker\rules.json

示例:

{
  "rules": [
    {
      "name": "Common Images",
      "ext": ["jpg", "jpeg", "png", "gif", "bmp"],
      "app": "C:\\Windows\\System32\\mspaint.exe",
      "priority": 1000,
      "isGroup": true,
      "enabled": true
    },
    {
      "name": "PNG override",
      "ext": ["png"],
      "app": "C:\\Program Files\\IrfanView\\i_view64.exe",
      "priority": 100,
      "isGroup": false,
      "enabled": true
    }
  ],
  "enabled": true,
  "checkInterval": 300
}

顶层字段:

类型 说明
rules 数组 有序的规则列表
enabled 布尔 总开关 —— 为 false 时服务空转
checkInterval 整数 两次重写之间的秒数(默认 300)

规则字段:

类型 说明
name 字符串 显示名
ext 字符串数组 后缀(带不带前导点都行)
app 字符串 目标 .exe 的绝对路径
priority 整数 数值越小优先级越高,详见下文
isGroup 布尔 true 为预设/分组规则,false 为单条覆盖
enabled 布尔 单条规则的开关

优先级体系

priority 数值越小,优先级越高(按升序排)。

区间 类型 说明
1 – 999 单条规则 用于覆盖分组规则
1000+ 分组规则 预设分类或用户分组

priority 升序排序后依次写入,于是 最后 命中某后缀的规则胜出。实际效果是:单条规则(小数值)先写、分组规则(大数值)后覆盖 —— 但分组规则覆盖范围广,所以 重要的 单后缀覆盖应放进高优先级(大数值)区间,让它能再覆盖回来。GUI 用两个独立的数值范围来强制这个约定。

(参见 defaultapplocker/core/config.py 中的 get_sorted_rules()。)


预设分组

开箱即用的预设分组(见 defaultapplocker/core/config.py 中的 DEFAULT_CONFIG):

  • 常见图片 —— jpg, jpeg, png, gif, bmp
  • 全部图片 —— webp, svg, ico, raw, psd, heic, tiff, tif
  • 常见视频 —— mp4, avi, mkv, mov, wmv, flv
  • 全部视频 —— webm, m4v, 3gp, ts, m2ts, rmvb
  • 常见文本 —— txt, md, json, xml, log, ini, cfg
  • 常见文档 —— doc, docx, xls, xlsx, ppt, pptx, pdf

默认只启用 "常见图片" 一组,目标程序为 mspaint.exe。可在 GUI 中编辑。


项目结构

defaultapp_locker/
+-- gui.py                          # GUI 入口
+-- service.py                      # 服务入口
+-- fix_default_app.py              # 参考用一次性修复脚本(见鸣谢)
+-- requirements.txt
+-- README.md                       # 本文件(中文)
+-- README_en.md                    # English
+-- LICENSE                         # Apache-2.0
+-- defaultapplocker/               # 主包
|   +-- core/
|   |   +-- locker.py               # 四层锁定原语
|   |   +-- config.py               # JSON 配置 + 排序
|   |   +-- i18n.py                 # 中英双语字符串
|   +-- service/
|   |   +-- locker_service.py       # ServiceFramework + 工作循环
|   +-- gui/
|       +-- main_window.py
|       +-- tray.py
+-- scripts/                        # 所有打包辅助脚本(文件名全英文)
|   +-- build.bat                   # 主打包(PyInstaller,两个 EXE,复制到 output\)
|   +-- build_quick.bat             # 一行 pyinstaller,无 spec
|   +-- build_simple.bat            # 旧版双二进制打包
|   +-- build_installer.bat         # Inno Setup 安装包一键生成
|   +-- build_gui.spec              # GUI 的 PyInstaller spec
|   +-- build_service.spec          # 服务的 PyInstaller spec
|   +-- build.spec                  # 旧版合并 spec
|   +-- fix_default_app.spec        # 一次性修复工具的 spec
|   +-- setup_inno.iss              # Inno Setup 脚本
|   +-- setup_script.nsi            # NSIS 备选脚本
|   +-- start_gui.bat               # 从源码运行 GUI
|   +-- USAGE.txt                   # 终端用户使用说明(打包进安装包)
+-- docs/
|   +-- BUILD.md                    # 完整打包指南
|   +-- INSTALLER.md                # 安装包指南
|   +-- ...
+-- resources/                      # 图标 / 图标生成器
+-- icons/, imgs/, default-app/     # 图片素材与参考资料
+-- output/                         # 由 build.bat 生成(已 gitignore)
+-- dist/                           # PyInstaller 原始产物

许可

Apache License 2.0 —— 详见 LICENSE


鸣谢

  • 原始思路与核心注册表锁定技术: 3089464667/default-app —— 本项目是该工作的结构化重写,扩展了 GUI、系统托盘、国际化、预设分组、 拖拽排序、带重写循环的真正 PyWin32 服务,以及一键 PyInstaller / Inno Setup 打包。
  • customtkinter —— Tom Schimansky
  • pystray —— Moses Palmér
  • pywin32 —— Mark Hammond 等

About

Windows工具,用于设置文件扩展名的默认打开程序。大家可能经常会遇到用windows指定默认的应用程序打开某种文件扩展名

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors