文件历史记录 - 无法扫描用户库以查找更改,也无法对已修改的配置文件进行备份

文件历史记录 - 无法扫描用户库以查找更改,也无法对已修改的配置文件进行备份

尝试在 Windows 8 中运行文件历史记录工具时,它会运行大约 2 秒钟然后停止。没有文件备份到选定的驱动器。

在事件查看器中出现的唯一错误是事件 ID #201:

Unable to scan user libraries for changes and perform backup of modified files 
for configuration
C:\Users\win8User\AppData\Local\Microsoft\Windows\FileHistory\Configuration\Config

或德语

Die Benutzerbibliotheken konnten nicht auf Änderungen überprüft werden,
und geänderte Dateien für die Konfiguration 
"C:\Users\win11User\AppData\Local\Microsoft\Windows\FileHistory\Configuration\Config" 
konnten nicht gesichert werden.

我尝试删除目标驱动器上的配置文件和 FileHistory 目录。再次设置文件历史记录会导致同样的错误。

有没有更好的方法来追踪导致故障的原因?或者以某种方式让文件历史记录工具创建一个更详细的日志文件来显示导致问题的原因?

答案1

这是我所做的。我不确定所有这些步骤是否必要,但我只是想确定一下。

1.

Control panel-> File history-> Advanced Settings->Open file history event logs并修复/删除所有扰乱文件历史记录的文件。对我来说,这个文件夹中有一堆文件:

AppData\Local\Microsoft\Windows\WER\ReportArchive\AppCrash_explorer.exe xxxxx

然后我就把它们删掉了。它们看起来不重要。

2. 我编写了一个简短的 Python 脚本来查找名称中含有非 ASCII 字符的文件。我手动重命名了所有这些文件。这可能是某些音乐文件夹的文件历史记录失败的原因!

import os,string
import sys

validChars='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ. #^=!()&,_\'-+@%[]{$};`~'

for currdir,folders,filenames in os.walk(r'C:\Users\<username>'):

    //Max filename len on ntfs is 255, but file history adds "F:\FileHistory\<username>\<computer name>\Data" to the front the files and a timestamp to the end so the file really needs to be less than 200 to be safe
    if len(currdir)>200:
        print 200,currdir

    if filter(lambda x: x in validChars, currdir[2:].replace('\\',''))!=currdir[2:].replace('\\',''):
        print 'chars',currdir,

    for filename in filenames:
        if filter(lambda x: x in validChars, filename)!=filename:
            print 'char2',os.path.join(currdir,filename)

编辑:不要删除 Autodesk Inventor 文件,否则会破坏 Autodesk。o_o

3.

文件历史记录 -> 选择驱动器 ->(选择您的驱动器)->(选择您的最新备份)-> 文件历史记录发出一些警告,指出您的最新备份不在此驱动器上。我单击了“确定”。

答案2

我发现 Windows 10 上的这个错误是由 JPEG 文件引起的,该文件的文件属性标志已损坏,特别是伪造的目录位。显然,文件历史记录尝试获取此非目录的子项,但失败了,并默默中止了整个备份。

文件历史记录在 a) 报告错误和 b) 处理错误方面存在严重疏忽。

扫描问题文件

我编写了以下 PowerShell 函数来扫描超出范围的文件属性。使用方法如下:

  1. 启动 PowerShell
  2. 粘贴整个函数,包括最后一个“}”后的空行
  3. 通过键入check-Attributes并指定可选路径来运行它,例如。check-Attributes c:\如果没有给出路径,它将从当前目录开始扫描。
Function check-Attributes {
    Param([string]$path)

    # Collect all possible attribute bits
    $attrBits = 0
    [enum]::GetValues([System.Io.FileAttributes]) | ForEach-Object {
        $attrBits = $attrBits -bor $_
    }
    $attrBitMask = -bnot $attrBits
    
    Write-Host "Scanning for out-of-range file/directory attributes..."
    $checked = 0
    $out_of_range = 0
    Get-ChildItem $path -Recurse -ErrorAction SilentlyContinue -ErrorVariable FailedItems | ForEach-Object {
        ++$checked
        Write-Verbose -Message "Checking $_"
        if (($_.Attributes -band $attrBitMask) -ne 0) {
            Write-Host $_.FullName "has out of range attributes:" $_.Attributes
            ++$out_of_range
        }
    }

    if ($out_of_range) {
        Write-Host
    }
    Write-Host "Checked" $checked "files/directories, found" $out_of_range "with attributes out of range."

    if ($FailedItems) {
        # $FailedItems is an array of System.Management.Automation.ErrorRecord
        Write-Host "`nEncountered" $FailedItems.length "error(s) while scanning:"
        $FailedItems | Foreach-Object {$_.CategoryInfo}
    }
}

测试属性并不能保证会发现所有损坏问题。但是,由于它还会报告迭代目录的问题,因此可能会暴露导致文件历史记录失败的其他问题。

我如何发现问题

我使用了“设置”备份选项控制来执行此操作。要打开它,请在“设置”中搜索“备份”,或单击“设置”>“系统”>“存储”>“查看备份选项”>“更多选项”。

错误日志也可能有帮助。要直接跳转到正确的事件查看器类别以查看错误,请滚动到上面对话框的底部,单击查看高级设置(这将打开控制面板)>高级设置>打开文件历史记录事件日志以查看最近的事件或错误。日志路径是%SystemRoot%\System32\Winevt\Logs\Microsoft-Windows-FileHistory-Engine%4BackupLog.evtx

问题文件位于我的“照片”目录中数百个子目录之一。我首先创建了一个新的并行目录“Photos-Pending-Backup”,将其从文件历史记录备份中排除,然后将“照片”中的所有目录移至该目录。我运行文件历史记录进行完整性检查并检查是否存在错误。

重要的:检查是否成功时,请查看备份选项的“概述”部分以了解新的完成时间。这里没有错误指示。

备份成功后,我逐步将一批目录移入“照片”,直到备份失败,再将它们移出,直到备份成功,重复此操作,直到找到问题。

我决定不从备份中排除所有子目录,因为 a) 使用备份选项UI,b)由于排除的文件夹没有按排序顺序显示,所以很容易错过一个,c)取消排除它们也需要很长时间。

答案3

我改编了上面由 apple16 发布的脚本在 Python 3 中运行并对其进行了一些增强。通过有选择地从文件历史记录中排除文件夹和使用此脚本来查看可能的问题,我将问题缩小到同一目录中两个名称几乎相同的文件。这两个文件的文件名中都有单词 Espanol,n 上带有波浪号。但其中一个文件的编码为:Espan\N{COMBINING TILDE}ol,另一个为 Espa\N{LATIN SMALL LETTER N WITH TILDE}ol。这两个文件名在 Windows 资源管理器中列出的名称相同,n 上都有波浪号。当我将这两个文件放在不同的文件夹中时,文件历史记录可以很好地备份它们,但当它们在同一个目录中时,它们似乎会互相干扰并阻止文件历史记录操作。

这是我使用的脚本 CheckFileNames.py:

#CheckFileNames.py, 10 May 2016, LewisN
#Python 3 version adapted and enhanced by LewisN; based on script by apple16 posted on https://superuser.com/a/802187
#Scans Windows file system for potentially problematic directories and files affecting File History operation.
#---------------------------------------------------------------------------------
#SET THE FOLLOWING BEFORE RUNNING:
DirectoryTreeToScan = r'C:\Users\<username>'
DaysBackToList = 0    #Number of days back to list. Set to 0 to list all FLAGGED FILES regardless of file dates.
PrintDates = False    #Set True to show File Creation and Modification dates.

#You may also want to play with the following settings:
flagDirLen=200
flagFullNameLen=200
  # Max filename len on Windows is ~255, but File History adds "F:\FileHistory\<username>\<computer name>\Data" to 
  # the front the files and a timestamp to the end, so backing off on name lengths flagged to be safe.
  # Files with long filenames are actually saved to F:\FileHistory\<username>\<computer name>\Data\$OF and can be
  # retrieved using the File History GUI, but you may want to avoid this.  Also there may be cases where
  # long paths and/or filenames cause errors.

#Notes: 
#  1. Filenames with Unicode characters from various languages are also often ok for File History.  
#     This script exposes these characters so they can be examined for potential problems.
#  2. Filenames with initial dots (also flagged here) are ok for File History (use cmd prompt to edit them).
#---------------------------------------------------------------------------------

import os,string,time,datetime
import sys

validChars="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ. #^=!()&,_'-+@%[]{$};`~"
disallowedChars=r'\/:*?"<>|'
# Note that many other characters are also actually allowed.  Windows expressly disallows \/:*?"<>|    

lastdirheader=''

def PrintDirHeaderIfNeeded():
    global lastdirheader
    if currdir!=lastdirheader:
        print ('===FLAGGED FILES IN DIRECTORY',currdir.encode("ascii","replace").decode()+':')
        lastdirheader = currdir
        return;
        
def PrintFileDates():
    fname=os.path.join(currdir,filename)
    print('      Created: ' + time.ctime(os.path.getctime(fname)) + ', Modified: ' + time.ctime(os.path.getmtime(fname)))
    return;

def IsRecent(DaysBack):
    fname=os.path.join(currdir,filename)
    if DaysBack==0: return True   # 0 disables limiting file listings based on file dates
    if ((datetime.datetime.now()-datetime.datetime.fromtimestamp(os.path.getctime(fname))).days<DaysBack) or \
       ((datetime.datetime.now()-datetime.datetime.fromtimestamp(os.path.getmtime(fname))).days<DaysBack):
        return True 
    else: return False;

for currdir,folders,filenames in os.walk(DirectoryTreeToScan):
   
    if len(currdir)>flagDirLen:
        print('===DIRLEN>' + str(flagDirLen) + ':', currdir.encode("ascii","replace").decode())
   
    if ''.join([x for x in currdir[2:].replace('\\','') if x in validChars])!=currdir[2:].replace('\\',''):
        print('===DIR CHARS+:',currdir.encode("ascii","namereplace").decode())
   
    for filename in filenames:
            
        if (len(currdir)+len(filename)>flagFullNameLen) and IsRecent(DaysBackToList):
            PrintDirHeaderIfNeeded()
            print('     FULLNAMELEN>' + str(flagFullNameLen) + ':', filename.encode("ascii","replace").decode())
            if PrintDates: PrintFileDates() 
    
        if ''.join([x for x in filename if x in validChars])!=filename and IsRecent(DaysBackToList):
            PrintDirHeaderIfNeeded()
            print('     CHARS+:',filename.encode("ascii","namereplace").decode())
            if PrintDates: PrintFileDates() 
            
        if filename[0:1] == "." and IsRecent(DaysBackToList):
            PrintDirHeaderIfNeeded()
            print('     INITIAL DOT:',filename.encode("ascii","replace").decode())
            if PrintDates: PrintFileDates()
            
        if any(True for x in filename if x in disallowedChars) and IsRecent(DaysBackToList):
            PrintDirHeaderIfNeeded()
            print('     DISALLOWED (' + disallowedChars + '):',filename.encode("ascii","replace").decode())
            if PrintDates: PrintFileDates()
            

答案4

对于原始发帖人的问题:我认为没有好的方法来追踪这些类型的问题,并且我还没有找到将日志记录增加到有用水平的方法。

如果你想尝试修复自己的问题,在这里阅读所有答案,相得益彰!

我的看法:构建于这里还有另一个答案,我曾写过一个剧本主动重命名文件包含可能导致问题的字符,因为在我的情况下,问题的根源分布在 4 个目录中,数据量约为 10 GB。以下脚本将“奇怪”的字符重命名为“更正常的字符”,同时接受许多我通过实验发现不会引起问题的字符。也许它可以为您解决问题。请注意,如果您还有一些需要重命名的文件夹,则需要稍微修改代码。

#CheckFileNames.py, 2024-01-05, JonasHeidelberg 
#Python 3 version adapted and enhanced by JonasHeidelberg
# Based on script by LewisN from https://superuser.com/a/1075326/72707 which in turn was 
# based on script by apple16 posted on https://superuser.com/a/802187/72707
#Scans Windows file system for potentially problematic directories and files affecting File History operation.
#Then tries to rename some of thos those files, if "RenameCount is set>0"
#---------------------------------------------------------------------------------
#SET THE FOLLOWING BEFORE RUNNING:
DirectoryTreeToScan = r'C:\\Users\\Username\\' 

DaysBackToList = 0    #Number of days back to list. Set to 0 to list all FLAGGED FILES regardless of file dates.
PrintDates = False    #Set True to show File Creation and Modification dates.

#You may also want to play with the following settings:
RenameCount = 0 # For testing purposes - limits how many files are being renamed. Set to 0 to avoid any renaming. Can probably set to float('inf') for no limit
flagDirLen=200
flagFullNameLen=200
  # Max filename len on Windows is ~255, but File History adds "F:\FileHistory\<username>\<computer name>\Data" to 
  # the front the files and a timestamp to the end, so backing off on name lengths flagged to be safe, e.g. 200.
  # Files with long filenames are actually saved to F:\FileHistory\<username>\<computer name>\Data\$OF and can be
  # retrieved using the File History GUI, but you may want to avoid this.  Also there may be cases where
  # long paths and/or filenames cause errors.

#Notes: 
#  1. Filenames with Unicode characters from various languages are also often ok for File History.  
#     This script exposes these characters so they can be examined for potential problems.
#  2. Filenames with initial dots (also flagged here) are ok for File History (use cmd prompt to edit them).
#---------------------------------------------------------------------------------

import os,string,time,datetime
import sys
from collections import defaultdict

validChars="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ. #^=!()&,_'-+@%[]{$};`~öäüöäüäÖÄÜߧ–é´¨„“€ŽÂÉóδñ�" 
# Note: äöü appear 2-3 times, but differently encoded!
# Note that many other characters are also actually allowed.  Windows expressly disallows \/:*?"<>|    
validCharsSet = set(validChars)
outsideValidChars = defaultdict(int)
disallowedChars=r'\/:*?"<>|'
replacement_table = {
    '\\N{LATIN CAPITAL LETTER A WITH TILDE}\\N{VULGAR FRACTION ONE QUARTER}': 'ü',
    '\\N{LATIN CAPITAL LETTER A WITH TILDE}\\N{CURRENCY SIGN}': 'ä',
    'aI\\N{COMBINING GRAVE ACCENT}\\x88': 'ä',
    'u\\N{BROKEN BAR}\\N{LATIN SMALL LETTER E WITH CIRCUMFLEX}': 'ü',
    'U\\N{BROKEN BAR}\\N{LATIN SMALL LETTER E WITH CIRCUMFLEX}': 'Ü',
    '\\N{LATIN CAPITAL LETTER A WITH TILDE}\\x83\\N{LATIN CAPITAL LETTER A WITH CIRCUMFLEX}\x9c': 'Ü',
    'A\\x83\N{LATIN CAPITAL LETTER A WITH CIRCUMFLEX}\\x9c': 'Ü',
    'A\\x9c': 'Ü',
    '\\N{HORIZONTAL ELLIPSIS}': '...',
    '\\N{ZERO WIDTH SPACE}': '',
    '\\N{NO-BREAK SPACE}': '',
    '\\N{RIGHT SINGLE QUOTATION MARK}': "'",
    '\\N{LATIN SMALL LETTER E WITH CIRCUMFLEX}': 'e',
    '\\N{LATIN CAPITAL LETTER A WITH TILDE}': 'A',
    '\\N{MIDDLE DOT}': '.',
    '\\N{BULLET}': '.',
    '\\N{LEFT-TO-RIGHT MARK}': '',
    '\\N{LATIN SMALL LETTER DOTLESS I}': 'i',
    '\\N{EM DASH}': '-',
    '\\N{LATIN SMALL LETTER I WITH ACUTE}': 'i',
    '\\N{LATIN SMALL LETTER C WITH CEDILLA}': 'c',
    '\\N{SINGLE RIGHT-POINTING ANGLE QUOTATION MARK}': "'",
    '\\N{SINGLE LEFT-POINTING ANGLE QUOTATION MARK}': "'",
    '\\N{SINGLE LOW-9 QUOTATION MARK}': "'",
    '\\N{LEFT SINGLE QUOTATION MARK}': "'",
    '\\N{LATIN SMALL LETTER U WITH ACUTE}': 'u',
    '\\N{LATIN SMALL LETTER O WITH MACRON}': 'o',
    '\\N{LATIN CAPITAL LETTER D WITH STROKE}': 'D',
    '\\N{LATIN SMALL LETTER D WITH STROKE}': 'd',
    '\\N{LATIN SMALL LETTER C WITH ACUTE}': 'c',
    '\\N{LATIN SMALL LETTER E WITH GRAVE}': 'e',
    '\\N{LATIN SMALL LETTER A WITH ACUTE}': 'a',
    '\\N{SOFT HYPHEN}': '-',
    '\\N{LATIN SMALL LIGATURE OE}': 'oe',
    '\\N{LATIN CAPITAL LETTER E WITH CIRCUMFLEX}': 'E'
}
lastdirheader=''

def PrintDirHeaderIfNeeded():
    global lastdirheader
    if currdir!=lastdirheader:
        print ('===FLAGGED FILES IN DIRECTORY',currdir.encode("ascii","replace").decode()+':')
        lastdirheader = currdir
        return;

def PrintFileDates():
    fname=os.path.join(currdir,filename)
    print('      Created: ' + time.ctime(os.path.getctime(fname)) + ', Modified: ' + time.ctime(os.path.getmtime(fname)))
    return;

def IsRecent(DaysBack):
    fname=os.path.join(currdir,filename)
    if DaysBack==0: return True   # 0 disables limiting file listings based on file dates
    if ((datetime.datetime.now()-datetime.datetime.fromtimestamp(os.path.getctime(fname))).days<DaysBack) or \
       ((datetime.datetime.now()-datetime.datetime.fromtimestamp(os.path.getmtime(fname))).days<DaysBack):
        return True 
    else: return False;


def replace_unicode_in_filename(directory, filename ):
    new_filename = filename
    
    # Go through all patterns to replace critical elements in filename
    for unicode_pattern, replacement in replacement_table.items():
        new_filename = new_filename.replace(unicode_pattern.encode().decode('unicode-escape'), replacement)
    
    print(filename.encode("ascii","namereplace").decode() + "-->" + new_filename + ": ", end="")
    print(os.rename(os.path.join(directory, filename), os.path.join(directory, new_filename)))
    
    return

print('Scanning ' + DirectoryTreeToScan + '...')
for currdir,folders,filenames in os.walk(DirectoryTreeToScan):

    if len(currdir)>flagDirLen:
        print('===DIRLEN>' + str(flagDirLen) + ':', currdir.encode("ascii","replace").decode())

    mod_currdir = currdir[2:].replace('\\', '')
    if ''.join([x for x in mod_currdir if x in validChars])!=mod_currdir:
        print('===DIR CHARS+:',currdir.encode("ascii","namereplace").decode())
        for char in mod_currdir:
            if char not in validCharsSet:
                outsideValidChars[char] += 1            

    for filename in filenames:

        if (len(currdir)+len(filename)>flagFullNameLen) and IsRecent(DaysBackToList):
            PrintDirHeaderIfNeeded()
            print('     FULLNAMELEN>' + str(flagFullNameLen) + ':', filename.encode("ascii","replace").decode())
            if PrintDates: PrintFileDates() 

        if any(True for x in filename if x in disallowedChars) and IsRecent(DaysBackToList):
            PrintDirHeaderIfNeeded()
            print('     DISALLOWED (' + disallowedChars + '):',filename.encode("ascii","replace").decode())
            if PrintDates: PrintFileDates()

        if ''.join([x for x in filename if x in validChars])!=filename and IsRecent(DaysBackToList):
            PrintDirHeaderIfNeeded()
            print('     CHARS+:',filename.encode("ascii","namereplace").decode())
            if PrintDates: PrintFileDates() 
            for char in filename:
                if char not in validCharsSet:
                    outsideValidChars[char] += 1
            if RenameCount > 0:
                replace_unicode_in_filename(currdir,filename)
                RenameCount = RenameCount - 1
    # HERE you would rename directories if needed -- NOT IMPLEMENTED YET!
    
# Now sort & print outsideValidChars:
sorted_chars = sorted(outsideValidChars.items(), key=lambda x: x[1], reverse=True)

print("Overview of the unusual characters found, by occurence:")

print("Character | Occurence")
print("---------------------")
for char, count in sorted_chars:
    readable_char = char.encode("ascii", "namereplace").decode()
    print(f"{readable_char} | {count}")

相关内容