在给电脑重新分盘时,想把 Software 盘的驱动器号(E:)改为对应的首字母 S,因此导致了所有软件崩溃。主要原因是软件主要依据驱动器号识别位置,而更改驱动器号后,并不会将软件设置的驱动器号一并更改。
解决办法有如下两个:
- 删除原有软件文件夹,重装一遍。
- 手动更改注册表,将 E:\ 改为 S:\。
两个方案的工作量都不小:
- 方案一的缺点是,注册表中原有的记录不会被删除,在操作电脑时会偶尔弹出一些无图标的选项很烦人。
- 方案二的缺点是,难以更改完全,因为不清楚每个软件安装和使用时都向注册表写了些什么东西。。。
这里介绍一下方案二的解决办法,但也并不是很好的方案。
1 手动替换
win11下,点击 win 按键,搜索框里搜索“注册表”,打开“注册表编辑器”后,点击左上角“编辑 ->
查找”选项,搜索更改前的盘符,点击“查找下一个。此处我输入 “E:\”。
![image-20240826005740290](https://img-blog.csdnimg.cn/img_convert/8dc03b8527f38ea1ee896b165329797e.png#pic_center)
可以看到,搜索到了一个结果,即软件安装时写入的注册表数据。双击“URL Protocol”,将其手动替换为“S:\BcutBilibili\Bcut.exe”。
2 Python 替换
手动替换效率过慢,因此使用 Python 来自动替换。给出如下示例代码。
注意:在运行代码时,需要右键点击 Python 或 IDE 图标(如 PyCharm、VS Code),并选择“以管理员身份运行”。否则,会抛出错误:[WinError 5] 拒绝访问。
import re
import winreg as reg
def replace_all(old_drive_pattern, new_drive, access_flag=0):
# 打开注册表根键
replace_root(reg.HKEY_CLASSES_ROOT, "HKEY_CLASSES_ROOT", "", old_drive_pattern, new_drive, access_flag)
replace_root(reg.HKEY_CURRENT_USER, "HKEY_CURRENT_USER", "", old_drive_pattern, new_drive, access_flag)
replace_root(reg.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", "", old_drive_pattern, new_drive, access_flag)
replace_root(reg.HKEY_USERS, "HKEY_USERS", "", old_drive_pattern, new_drive, access_flag)
replace_root(reg.HKEY_CURRENT_CONFIG, "HKEY_CURRENT_CONFIG", "", old_drive_pattern, new_drive, access_flag)
def replace_root(root_key, root_name, key_path, old_drive_pattern, new_drive, access_flag=0):
try:
# 打开注册表键
reg_key = reg.OpenKey(root_key, key_path, 0, reg.KEY_READ | reg.KEY_WRITE | access_flag)
# 遍历所有值
i = 0
while True:
try:
value_name, value_data, value_type = reg.EnumValue(reg_key, i)
# 处理 REG_SZ 和 REG_EXPAND_SZ 类型的值
if value_type == reg.REG_SZ or value_type == reg.REG_EXPAND_SZ:
if re.search(old_drive_pattern, value_data):
new_value_data = re.sub(old_drive_pattern, new_drive, value_data)
reg.SetValueEx(reg_key, value_name, 0, value_type, new_value_data)
print(f"{root_name}\\{key_path}\\{value_name}:\n{value_data}\n修改为 -> {new_value_data}\n")
# 处理 REG_MULTI_SZ 类型的值
elif value_type == reg.REG_MULTI_SZ:
new_value_data = []
modified = False
for item in value_data:
if re.search(old_drive_pattern, item):
new_value_data.append(re.sub(old_drive_pattern, new_drive, item))
modified = True
else:
new_value_data.append(item)
if modified:
reg.SetValueEx(reg_key, value_name, 0, value_type, new_value_data)
print(f"{root_name}\\{key_path}\\{value_name}:\n{value_data}\n修改为 -> {new_value_data}\n")
# 替换键名
if re.search(old_drive_pattern, value_name):
reg.DeleteValue(reg_key, value_name) # 删除旧的键值对
new_name = re.sub(old_drive_pattern, new_drive, value_name)
reg.SetValueEx(reg_key, new_name, 0, value_type, value_data) # 创建新的键值对,保留值不变,仅更改键名
print(f"{root_name}\\{key_path}:\n修改了键名: {value_name}\n修改为 -> {new_name}\n")
i += 1
except OSError:
break
# 遍历子键
i = 0
while True:
try:
subkey_name = reg.EnumKey(reg_key, i)
subkey_path = f"{key_path}\\{subkey_name}"
if key_path == "": # 根目录不添加反斜杠 \
subkey_path = subkey_name
replace_root(root_key, root_name, subkey_path, old_drive_pattern, new_drive, access_flag)
i += 1
except OSError:
break
reg.CloseKey(reg_key)
except PermissionError:
# print(f"没有权限访问键: {key_path}")
pass
except FileNotFoundError:
# print(f"未找到键: {key_path}")
pass
except Exception as e:
# print(f"无法打开键: {key_path}, 错误: {e}")
pass
# 正则表达式模式,仅匹配以"E:\"开头的路径,确保盘符后面直接跟随的是反斜杠
old_drive_pattern = r"(?<![A-Z])E:\\"
# 设置新盘符
new_drive = r"S:\\"
# 访问 64 位注册表视图
access_flag = reg.KEY_WOW64_64KEY
# replace_root(reg.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\Folders", old_drive_pattern, new_drive, access_flag)
replace_all(old_drive_pattern, new_drive, access_flag)
print("所有修改完成。")
2.1 注册表数据说明
需要修改的注册表的数据分为以下几种:
- REG_SZ:字符串类型(单行)。
![](https://img-blog.csdnimg.cn/img_convert/ce4493b393ff0f9b2c4a7c0f757a4d7b.png#pic_center)
- REG_EXPAND_SZ:可延展字符串,即字符串中含有其他变量的值,类似 C# 内插字符串。
![image-20240826012350274](https://img-blog.csdnimg.cn/img_convert/b596e6d7d6eac3173798ea38390f980c.png#pic_center)
- REG_MULTI_SZ:字符串类型(多行)。
![image-20240826012434887](https://img-blog.csdnimg.cn/img_convert/e3b63b49030831cdf0b01eb65d06bac6.png#pic_center)
在 replace_root()
函数中,对每种类型进行了分类处理。主要过程还是字符串的匹配替换,以及注册表的读取和写入。
-
REG_SZ 和 REG_EXPAND_SZ:
在程序中,对应的
value_data
为字符串,直接匹配替换即可。 -
REG_MULTI_SZ:
在程序中,对应的
value_data
为字符串列表,需要遍历其中的字符串进行匹配替换。 -
键名:
在程序中,
value_name
也是字符串,直接匹配替换即可。
2.2 正则匹配
使用正则匹配:old_drive_pattern = r"(?<![A-Z])E:\\"
。(?<![A-Z])
是一个负向前瞻断言,确保在 E:
之前没有大写字母。防止像 "SOME:\XXX"
类似情况被匹配。
2.3 其他
- 推荐最好先进行测试,并备份注册表,以防出错。测试时可将 92 行取消注释,并注释 93 行。在 92 行指定测试的路径,这样即便出错,也仅是小范围,方便更改回来。
- 测试通过时,建议程序多运行几次,以更改完全。
- 可以根据具体需求更改正则匹配。
- 注册表部分内容在程序运行时不允许更改,需重启电脑后才能生效。
- 完成注册表修改后,先测试相关软件或功能是否按预期运行。如果没有生效,再考虑重启。如果不确定某些系统配置的更改是否需要重启,重启电脑可以确保所有更改生效。
3 图标问题
对于桌面上已经失效的图标,只有一个个手动更改路径了。
或者将其删除,然后新创建桌面快捷方式,但也需要一个个创建。
4 总结
最好是不要替换盘符,新电脑分区时一旦确定后就不要更改,因为永远不清楚安装的什么软件在什么时候向注册表写了哪些东西。
花了快 2 天时间,初步解决问题。但不清楚后续可能发生什么其他问题,遇到了再说吧!