1

有谁知道在系统重新启动时告诉哪些文件将被删除或修改的方法?就像您更新系统时一样,系统要求重新启动,因为某些文件被系统阻止并且需要替换或修改它们。我需要制作一个脚本,告诉我在启动时将删除或修改哪个文件。

4

2 回答 2

3

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表示szSrcFileszDstFile在重新启动时重命名。

您可以使用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}
于 2012-06-28T22:18:10.150 回答
0

虽然很久以前用 Perl 脚本回答了这个问题,但我想在这里提供一个 PowerShell 解决方案,因为它是 Windows 原生的,并且问题被标记为

我们需要PendingFileRenameOperationsHKLM:\SYSTEM\CurrentControlSet\Control\Session Manager注册表项中查询。我有以下函数查询此注册表值并返回您可以操作的惯用 PowerShell 对象(与Windows PowerShellPowerShell 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 ManagerREG_SZ_MULTI是包含(多行字符串)的父键,其中包含用于重命名的暂存路径。偶数行(零索引)是源文件,奇数行是目标路径。
  • 我们可以使用上面的Session ManagerGet-ItemProperty键来获取注册表值,然后我们可以在返回的值集合上引用该属性。PendingFileRenameOperations
  • 每行代表一个以字符串为前缀的文件路径\??\。我们最终需要从返回的路径中删除它,因此要匹配的正则表达式模式是^\\\?\?\\. 有关表达式本身的更多信息,请参见RegExr
  • 每个操作将有两行长。如前所述,以开头的偶数行0source文件,奇数行将是target移动到的路径。
  • target行表示没有目标路径。这意味着“移动”操作实际上将在重新启动时删除源文件。
    • 我不知道source一行是否可以出于任何原因为空,或者是否可以安全跳过,因此如果预期的行为空,此函数会谨慎出错并放弃source

现在我们了解了注册表项的工作原理以及如何获取数据,我们可以看一下循环逻辑。

  • 我们从 循环遍历每一行PendingFileRenameOperations,当我们引用用于该target行的下一个索引时递增索引。本质上,我们最终在每次迭代中将循环计数为 2。
  • 一旦我们从sourceand中删除前缀,我们就会根据计算结果是否为 falsetarget来确定它是Move还是Delete 。 operation$dst
  • Source使用、TargetOperation键创建哈希表,将其转换为PSCustomObject,并将对象输出到管道。这比多次建立@{}和管道传输要容易。Add-Member
于 2021-09-09T18:48:22.147 回答