views:

436

answers:

2

Hi guys,

I've written a small VBScript to creates a .zip file and then copies the contents of a specified folder into that .zip file.

I copy the files over one by one for a reason (I know I can do the whole lot at once). However my problem is when I try to copy them one by one without a WScript.Sleep between each loop iteration I get a "File not found or no read permission." error; if I place a WScript.Sleep 200 after each write it works but not 100% of the time.

Pretty much I'd like to get rid of the Sleep function and not rely on that because depending on the file size it may take longer to write therefore 200 milliseconds may not be enough etc.

As you can see with the small piece of code below, I loop through the files, then if they match the extension I place them into the .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

Any suggestions on how I can stop relying on the Sleep function?

Thanks

A: 

You can try accessing the file you've just copied, for example with an "exists" check:

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

I'm not sure if target is calculated correctly for this use context, but you get the idea. I'm a bit surprised that this error occurs in the first place... FileSystemObject should be strictly synchronous.

If all else fails, do this:

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
Tomalak
I tried the code. The loop never runs because FileExists = true. I don't know what it is, but it's not like the file isn't there, but someone the script can't access it to write to it because the previous file hasn't completed it's copying.Like you said, I would have assumed the copying would be strictly synchronous.
mlevit
@mlevit: Maybe comparing file sizes is the way to go?
Tomalak
@Tomalak I liked that idea so I tried it, however the problems becomes apparent when you reach files of around 20KB or more. Because I'm placing the files into a ZIP file their size shrinks therefore I cannot estimate the ZIP file size with that file in it, my loop becomes infinite.
mlevit
@mlevit: Admittedly, I've never actually used the FileSystemObject with zip folders, I did not even know that this was possible. I can only guess that the API function that copies a file to a zip folder works differently than the normal "copy file" API function, and returns a bit too early. Maybe there is no other way than to wait long enough, or to keep trying until the error goes away (via `On Error Resume Next`). Don't forget to include a counter to avoid an infinite loop. ;)
Tomalak
@Tomalak The script actually continues running after the error is shown but the file that was meant to be copied when the error is show does not get copied across. This is getting so annoying :P
mlevit
@mlevit: So the error message is not shown by the script (because error messages *definitely* interrupt the thread), but by the windows API itself? Yes, this really sounds like it would get annoying. Hm, have you tried my suggestion to copy it to a separate folder and compress that folder a a whole?
Tomalak
Thanks for all your help Tomalak but the error that is thrown is thrown by Windows, not by VB. I'll keep looking around, maybe something will come up. If you want to look at the script it's here: http://gist.github.com/410815
mlevit
A: 

You are correct, CopyHere is asynchronous. When I do this in a vbscript, I sleep until the count of files in the zip, is greater than or equal to the count of files copied in.

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
Cheeso