有谁知道在系统重新启动时告诉哪些文件将被删除或修改的方法?就像您更新系统时一样,系统要求重新启动,因为某些文件被系统阻止并且需要替换或修改它们。我需要制作一个脚本,告诉我在启动时将删除或修改哪个文件。
2 回答
MoveFileEx的文档指出:
评论
如果
dwFlags
参数指定MOVEFILE_DELAY_UNTIL_REBOOT
,则MoveFileEx在无法访问注册表时失败。该函数将要在重新启动时重命名的文件的位置存储在以下注册表值中:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations
此注册表值的类型为
REG_MULTI_SZ
。每个重命名操作都存储以下以 NULL 结尾的字符串之一,具体取决于重命名是否为删除:
szDstFile\0\0
szSrcFile\0szDstFile\0
该字符串
szDstFile\0\0
表示该文件szDstFile
将在重新启动时被删除。该字符串szSrcFile\0szDstFile\0
表示szSrcFile
要szDstFile
在重新启动时重命名。
您可以使用Win32::TieRegistry查询 Windows 注册表:
#!/usr/bin/env perl
use strict; use warnings;
use Const::Fast;
use Win32::TieRegistry;
use YAML;
const my $REG_DELIMITER => '/';
$Registry->Delimiter($REG_DELIMITER);
my $key = join $REG_DELIMITER, qw(
HKEY_LOCAL_MACHINE
SYSTEM
CurrentControlSet
Control
Session Manager
PendingFileRenameOperations
);
print Dump $Registry->{$key}
虽然很久以前用 Perl 脚本回答了这个问题,但我想在这里提供一个 PowerShell 解决方案,因为它是 Windows 原生的,并且问题被标记为powershell。
我们需要PendingFileRenameOperations
从HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager
注册表项中查询。我有以下函数查询此注册表值并返回您可以操作的惯用 PowerShell 对象(与Windows PowerShell和PowerShell Core兼容):
Function Get-PendingFileRenameOperations {
[CmdletBinding()]
Param()
# Define Registry Variables
$multiSzPrefix = '^\\\?\?\\'
$pendingRebootRegPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
# Get the PendingFileRenameOperations value as an array of strings
Write-Verbose "Reading PendingFileRenameOperations from ${pendingRebootRegPath}"
$pendingFiles = @( Get-ItemProperty $pendingRebootRegPath ).PendingFileRenameOperations
Write-Verbose "Found $($pendingFiles.Count / 2) PendingFileRenameOperations$([System.Environment]::NewLine)"
# Loop over each line from PendingFileRenameOperations
for( $i = 0; $i -lt $pendingFiles.Count; $i++ ) {
# Get the source file
$src = $pendingFiles[$i] -replace $multiSzPrefix
if( !$src ) {
# Better safe than sorry
throw [System.InvalidOperationException]::new("${pendingRebootRegPath}\PendingFileRenameOperations is malformed, expected source but got nothing")
}
Write-Verbose "Found source: ${src}"
# Get the target path
$dst = $pendingFiles[++$i] -replace $multiSzPrefix
if( $dst ) {
Write-Verbose "Found target: ${dst}"
}
# Determine whether it's a "move" or "delete"
$operation = if( $dst ) {
"Move"
} else {
"Delete"
}
Write-Verbose "Operation: ${operation}$([System.Environment]::NewLine)"
# Prepare object and return
[PSCustomObject]@{
Source = $src
Target = $dst
Operation = $operation
}
Remove-Variable src, dst, operation
}
}
这个怎么运作
HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager
REG_SZ_MULTI
是包含(多行字符串)的父键,其中包含用于重命名的暂存路径。偶数行(零索引)是源文件,奇数行是目标路径。- 我们可以使用上面的Session Manager
Get-ItemProperty
键来获取注册表值,然后我们可以在返回的值集合上引用该属性。PendingFileRenameOperations
- 每行代表一个以字符串为前缀的文件路径
\??\
。我们最终需要从返回的路径中删除它,因此要匹配的正则表达式模式是^\\\?\?\\
. 有关表达式本身的更多信息,请参见RegExr。 - 每个操作将有两行长。如前所述,以开头的偶数行
0
是source
文件,奇数行将是target
移动到的路径。 - 空
target
行表示没有目标路径。这意味着“移动”操作实际上将在重新启动时删除源文件。- 我不知道
source
一行是否可以出于任何原因为空,或者是否可以安全跳过,因此如果预期的行为空,此函数会谨慎出错并放弃source
。
- 我不知道
现在我们了解了注册表项的工作原理以及如何获取数据,我们可以看一下循环逻辑。
- 我们从 循环遍历每一行
PendingFileRenameOperations
,当我们引用用于该target
行的下一个索引时递增索引。本质上,我们最终在每次迭代中将循环计数为 2。 - 一旦我们从
source
and中删除前缀,我们就会根据计算结果是否为 falsetarget
来确定它是Move还是Delete 。operation
$dst
Source
使用、Target
和Operation
键创建哈希表,将其转换为PSCustomObject
,并将对象输出到管道。这比多次建立@{}
和管道传输要容易。Add-Member