Запись в файл с помощью CopyHere без использования WScript.Sleep

Я написал небольшой VBScript для создания .zip файла и затем копирует содержимое указанной папки в этот .zip файл.

Я копирую файлы по одному по какой-то причине (я знаю, что могу делать всю партию сразу). Однако моя проблема в том, что когда я пытаюсь скопировать их один за другим без WScript.Sleep между каждой итерацией цикла, я получаю "Файл не найден или не имеет разрешения на чтение". ошибка; если я поместил WScript.Sleep 200 после каждой записи, это работает, но не в 100% времени.

В значительной степени я хотел бы избавиться от функции Sleep и не полагаться на это, потому что в зависимости от размера файла может потребоваться больше времени для записи, поэтому может быть недостаточно 200 миллисекунд и т.д.

Как вы можете видеть с небольшим фрагментом кода ниже, я просматриваю файлы, а если они соответствуют расширению, я помещаю их в .zip(zipFile)

For Each file In folderToZip.Items
    For Each extension In fileExtensions
        if (InStr(file, extension)) Then
            zipFile.CopyHere(file)
            WScript.Sleep 200
            Exit For
        End If
    Next
Next

Любые предложения о том, как я могу перестать полагаться на функцию сна?

Спасибо

Ответ 1

Вот как мы это делаем в VB6. После вызова CopyHere в zip мы ожидаем завершения async-сжатия, как это

    Call Sleep(100)
    Do
        Do While Not pvCanOpenExclusive(sZipFile)
            Call Sleep(100)
        Loop
        Call Sleep(100)
    Loop While Not pvCanOpenExclusive(sZipFile)

где вспомогательная функция выглядит так:

Private Function pvCanOpenExclusive(sFile As String) As Boolean
    Dim nFile       As Integer

    nFile = FreeFile
    On Error GoTo QH
    Open sFile For Binary Access Read Lock Write As nFile
    Close nFile
    pvCanOpenExclusive = True
QH:
End Function

Хороший побочный эффект заключается в том, что даже если прошивка невозможна, это не закончится бесконечным циклом.

Проблема возникает при доступе к zip файлу при закрытии zipfldr.dll, то есть когда pvCanOpenExclusive возвращает true.

Ответ 2

Вы правы, CopyHere является асинхронным. Когда я делаю это в vbscript, я сплю, пока количество файлов в zip больше или равно количеству копируемых файлов.

Sub NewZip(pathToZipFile)

   WScript.Echo "Newing up a zip file (" & pathToZipFile & ") "

   Dim fso
   Set fso = CreateObject("Scripting.FileSystemObject")
   Dim file
   Set file = fso.CreateTextFile(pathToZipFile)

   file.Write Chr(80) & Chr(75) & Chr(5) & Chr(6) & String(18, 0)

   file.Close
   Set fso = Nothing
   Set file = Nothing

   WScript.Sleep 500

End Sub



Sub CreateZip(pathToZipFile, dirToZip)

   WScript.Echo "Creating zip  (" & pathToZipFile & ") from (" & dirToZip & ")"

   Dim fso
   Set fso= Wscript.CreateObject("Scripting.FileSystemObject")

   If fso.FileExists(pathToZipFile) Then
       WScript.Echo "That zip file already exists - deleting it."
       fso.DeleteFile pathToZipFile
   End If

   If Not fso.FolderExists(dirToZip) Then
       WScript.Echo "The directory to zip does not exist."
       Exit Sub
   End If

   NewZip pathToZipFile

   dim sa
   set sa = CreateObject("Shell.Application")

   Dim zip
   Set zip = sa.NameSpace(pathToZipFile)

   WScript.Echo "opening dir  (" & dirToZip & ")"

   Dim d
   Set d = sa.NameSpace(dirToZip)

   ' for diagnostic purposes only
   For Each s In d.items
       WScript.Echo  s
   Next


   ' http://msdn.microsoft.com/en-us/library/bb787866(VS.85).aspx
   ' ===============================================================
   ' 4 = do not display a progress box
   ' 16 = Respond with "Yes to All" for any dialog box that is displayed.
   ' 128 = Perform the operation on files only if a wildcard file name (*.*) is specified. 
   ' 256 = Display a progress dialog box but do not show the file names.
   ' 2048 = Version 4.71. Do not copy the security attributes of the file.
   ' 4096 = Only operate in the local directory. Don't operate recursively into subdirectories.

   WScript.Echo "copying files..."

   zip.CopyHere d.items, 4

   Do Until d.Items.Count <= zip.Items.Count
       Wscript.Sleep(200)
   Loop

End Sub

Ответ 3

Вы можете попробовать получить доступ к только что скопированному файлу, например, с помощью проверки "существует":

For Each file In folderToZip.Items
    For Each extension In fileExtensions
        If LCase(oFSo.GetExtensionName(file)) = LCase(extension) Then
            zipFile.CopyHere(file)
            Dim i: i = 0
            Dim target: target = oFSO.BuildPath(zipFile, oFSO.GetFileName(file))
            While i < 100 And Not oFSO.FileExists(target) 
              i = i + 1
              WScript.Sleep 10
            Wend
            Exit For
        End If
    Next
Next

Я не уверен, правильно ли вычисляется target для этого контекста использования, но вы получаете эту идею. Я немного удивлен, что эта ошибка возникает в первую очередь... FileSystemObject должна быть строго синхронной.

Если все остальное не работает, выполните следующее:

For Each file In folderToZip.Items
    For Each extension In fileExtensions
        If LCase(oFSo.GetExtensionName(file)) = LCase(extension) Then
            CompressFailsafe zipFile, file
            Exit For
        End If
    Next
Next

Sub CompressFailsafe(zipFile, file)
  Dim i: i = 0
  Const MAX = 100

  On Error Resume Next
  While i < MAX
    zipFile.CopyHere(file)
    If Err.Number = 0 Then 
      i = MAX
    ElseIf Err.Number = xxx ''# use the actual error number!
      Err.Clear
      WScript.Sleep 100
      i = i + 1
    Else 
      ''# react to unexpected error
    End Of
  Wend
  On Error GoTo 0
End Sub

Ответ 4

Решение, которое мы использовали после большой отладки и QA для различных оконных аксессуаров, включая быстрые и медленные машины и машины при большой загрузке процессора, было следующим фрагментом.

Критика и улучшения приветствуются.

Мы не смогли найти способ сделать это без цикла, т.е. если вы хотите выполнить некоторую проверку или выполнить постпиксельную работу.

Цель заключалась в том, чтобы создать что-то, что надежно работало на максимально возможном количестве окон. Идеально как можно более возможно.

Имейте в виду, что этот код по-прежнему НЕ на 100% надежный, но его значение составляет ~ 99%. Как стабильный, как мы могли бы получить его с Dev и QA время доступны. Возможно, что увеличение iSleepTime может сделать 100%

Точки обзора:

  • Безусловный сон, кажется, самый надежный и совместимый подход, который мы нашли.
  • iSleepTime не следует сокращать, кажется, чем чаще цикл работает, тем выше вероятность ошибки, которая, по-видимому, связана с внутренними операциями процесса zip/copy
  • iFiles - это номер исходного файла
  • Чем более упрощен цикл, тем лучше, например, вывод oZippp.Items().Count в цикле вызвал необъяснимые ошибки, которые выглядели так, как будто они могут быть связаны с нарушениями доступа к файлам/совместного использования/блокировки. Мы не проводили трассировку времени, чтобы узнать.
  • В любом случае, похоже, что в Windows 7 внутренняя часть процесса zipping использует временный файл, расположенный в cdd сжатой ZIP-папки, вы можете видеть это во время длительных задержек, обновляя окно проводника или список dir с помощью cmd
  • У нас был успех с этим кодом в Windows 2000, XP, 2003, Vista, 7
  • Вероятно, вы захотите добавить тайм-аут в цикле, чтобы избежать бесконечных циклов

    'Copy the files to the compressed folder
    oZippp.CopyHere oFolder.Items()
    iSleeps = 0
    iSleepTime = 5
    On Error Resume Next
    Do
        iSleeps = iSleeps + 1
        wScript.Sleep (iSleepTime * 1000)
    Loop Until oZippp.Items().Count = iFiles
    On Error GoTo 0
    
    
    If iFiles <> oZippp.Items().Count Then
        ' some action to handle this error case
    Else
        ' some action to handle success
    End If
    

Ответ 5

Вот трюк, который я использовал в VB; получить длину zip файла до изменения и дождаться его изменения, а затем подождать еще секунду или две. Мне нужны только два конкретных файла, но вы можете сделать из этого цикл.

Dim s As String
Dim F As Object 'Shell32.Folder
Dim h As Object 'Shell32.Folder
Dim g As Object 'Shell32.Folder
Dim Flen As Long, cntr As Long, TimerInt As Long
Err.Clear
s = "F:\.zip"
NewZipFolder s
Flen = FileLen(s)
Set F = CreateObject("Shell.Application").namespace(CVar(s))
TimerInt = FileLen("F:\MyBigFile.txt") / 100000000  'set the loop longer for bigger files
F.CopyHere "F:\DataSk\DemoData2010\Test.mdf"
Do
    cntr = Timer + TimerInt
    Do
        DoEvents: DoEvents
    Loop While cntr > Timer
    Debug.Print Flen
Loop While Flen = FileLen(s)
    cntr = Timer + (TimerInt / 2)
    Do
        DoEvents: DoEvents
    Loop While cntr > Timer
Set F = Nothing
Set F = CreateObject("Shell.Application").namespace(CVar(s))


F.CopyHere "F:\MynextFile.txt"

MsgBox "Done!"