注册表Uninstall

Microsoft\Windows\CurrentVersion\Uninstall 是 Windows 注册表中的一个键(key),它包含了用户通过“控制面板”中的“程序和功能”(以前称为“添加或删除程序”)安装的所有应用程序的列表。这个注册表键位于 HKEY_LOCAL_MACHINE 分支下。

当你通过“程序和功能”卸载应用程序时,Windows 会在这个注册表键下为每个应用程序创建一个子键(subkey)。每个子键都包含了与该应用程序相关的卸载信息,包括:

  • 应用程序的显示名称
  • 发行者
  • 卸载字符串(Uninstall String),指向卸载程序的路径
  • 版本号
  • 安装源(安装介质的路径,如果是从网络或CD安装)
  • 估计大小
  • 安装日期
  • 状态(是否已安装、正在安装等)
  • 系统组件(是否是系统必需的应用程序)
  • 其他与卸载相关的信息

代码实现

初始化列表和集合:

1
2
key_list = []
t = [] # 去重列表

这部分代码初始化了两个列表:key_list 用于存储打开的注册表键,t 用于存储已处理数据的MD5哈希值,用于去重。

尝试打开注册表键:

1
2
3
4
5
6
7
try:
sub_key = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
setting.log.info("正在打开: HKEY_LOCAL_MACHINE\\%s" % sub_key)
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, sub_key)
key_list.append(key)
except Exception as e:
setting.log.error("打开失败, 原因: %s" % str(e))

这部分代码尝试打开注册表中的 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall 键,并将打开的键添加到 key_list 中。如果打开失败,会记录错误日志。

重复上述步骤,尝试打开其他注册表键:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try:
sub_key = r"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
setting.log.info("正在打开: HKEY_LOCAL_MACHINE\\%s" % sub_key)
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, sub_key)
key_list.append(key)
except Exception as e:
setting.log.error("打开失败, 原因: %s" % str(e))

try:
sub_key = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
setting.log.info("正在打开: HKEY_CURRENT_USER\\%s" % sub_key)
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, sub_key)
key_list.append(key)
except Exception as e:
setting.log.error("打开失败, 原因: %s" % str(e))

这部分代码重复上述步骤,尝试打开 HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\UninstallHKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall 键,并将打开的键添加到 key_list 中。如果打开失败,会记录错误日志。

遍历每个打开的注册表键:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
for key in key_list:
sub_num, _, _ = winreg.QueryInfoKey(key)

for i in range(sub_num):
name = winreg.EnumKey(key, i)

try:
setting.log.info("正在打开: %s" % name)
key_1 = winreg.OpenKey(key, name)
except Exception as e:
setting.log.error("打开失败, 原因: %s" % str(e))
continue

try:
display_name = winreg.QueryValueEx(key_1, "DisplayName")
except:
display_name = ""
try:
display_version = winreg.QueryValueEx(
key_1, "DisplayVersion")
except:
display_version = ""
try:
_, _, timestamp = winreg.QueryInfoKey(key_1)
except:
timestamp = 0

这部分代码遍历每个打开的注册表键,获取每个子键的名称,并尝试打开这些子键。然后,它尝试获取每个子键中的 DisplayNameDisplayVersiontimestamp 值。如果获取失败,会将相应的值设置为空字符串或0。

处理获取到的软件信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
if display_name:
source_data = {
"DisplayName": display_name,
"InstallTime": timestamp,
"DisplayVersion": display_version
}

clean_data = get_line(self, source_data, self.display)
data = json.dumps(clean_data, ensure_ascii=False)
md5 = get_md5(data)
if md5 not in t:
write_file(self.file, data)
t.append(md5)

这部分代码检查 display_name 是否存在,如果存在,则将获取到的软件信息封装成一个字典 source_data。然后,它调用 get_line 方法处理 source_data,并将处理后的数据转换为 JSON 格式。接着,它计算 JSON 数据的 MD5 哈希值,并检查该哈希值是否已存在于 t 中。如果不存在,它将 JSON 数据写入到输出文件中,并将 MD5 哈希值添加到 t 中。