使用CopyHere写入文件而不使用WScript.Sleep

问题描述:

我写了一个小的VBScript来创建.zip文件,然后将指定文件夹的内容复制到该.zip文件中。使用CopyHere写入文件而不使用WScript.Sleep

我一个接一个地复制文件出于某种原因(我知道我可以一次完成整件事)。然而,我的问题是,当我试图在没有WScript.Sleep的情况下逐一复制它们时,每次循环迭代时我都会得到一个“未找到文件或没有读取权限”。错误;如果我在每次写入后都设置了WScript.Sleep 200,但不是100%的时间。

差不多,我想摆脱休眠功能,而不是依赖于因为根据文件的大小,可能需要更长的时间,因此写200毫秒可能不够等

,你可以看到小片的下面的代码,我遍历文件,那么如果它们扩展匹配我把它们放入的.zip(zip文件)

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 

我如何能停止依靠睡眠功能的任何建议?

感谢

+0

另一种方式,我想这样做是制作一个数组,并将所有通过过滤器的文件放入它......但我可以在CopyHere函数中使用该数组。 有谁知道它是怎么回事? – mlevit 2010-05-23 01:46:45

+0

不,您不能以任何其他方式使用该数组,而不是迭代它并执行基本相同的操作。那么如何将传递的文件复制到临时文件夹并从那里一次添加它们? – Tomalak 2010-05-23 07:41:17

+0

@Tomalak不知道这是否会有所作为。我想我可能会将它们复制到一个文件中时遇到同样的错误。你如何一次添加它们? – mlevit 2010-05-23 08:44:57

可以尝试访问你刚才复制的文件,例如用“存在”检查:

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 
+0

我试了一下代码。循环从不运行,因为FileExists = true。我不知道它是什么,但它不像文件不存在,但有人脚本无法访问它来写入它,因为前一个文件尚未完成复制。 就像你说的那样,我会假定复制将是严格同步的。 – mlevit 2010-05-22 23:25:27

+0

@mlevit:也许比较文件大小是要走的路? – Tomalak 2010-05-23 07:32:18

+0

@Tomalak我喜欢这个想法,所以我试了一下,但是当你达到大约20KB或更多的文件时问题就变得明显。因为我将这些文件放入一个ZIP文件中,所以它们的大小会缩小,因此我无法用它来估计ZIP文件的大小,我的循环变得无限。 – mlevit 2010-05-23 09:22:37

你是正确的,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 

我们很多的调试和QA后使用的解决方案各种windows风格,包括快速和慢速机器和CPU负载繁重的机器是以下代码片段。

批判和改进的欢迎。

我们无法找到一种没有循环的方法,也就是说,如果您想做一些验证或压缩后的工作。

目标是在尽可能多的windows风格上建立可靠运行的东西。理想情况下也尽可能本地化。

请注意,此代码仍不是100%可靠,但它似乎是〜99%。尽可能稳定,因为我们可以通过dev和QA时间获得它。 它可能增加iSleepTime可以把它100%

点注意事项:

  • 无条件睡眠似乎是最可靠和最兼容的方法,我们发现
  • iSleepTime不应该降低,它似乎循环运行得越频繁,出现错误的概率就越高,似乎与压缩/复制过程的内部操作有关
  • iFiles是源文件数
  • 循环越简单越好,例如在循环中输出oZippp.Items().Count会导致看似可能与文件访问/共享/锁定违例相关的无法解释的错误。我们没有花时间追踪发现。
  • 无论如何,在Windows 7上看来,压缩过程的内部使用位于压缩zip文件夹的cwd中的临时文件,您可以在长时间运行的zip文件中通过刷新资源管理器窗口或使用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 
    
+0

如果项目位于文件夹内,则此操作将失败。如果源文件夹包含20个项目,则它的名称空间将报告20,但zip命名空间仍然只报告1个项目 - 文件夹。 – 2016-09-30 00:10:45

这是我们如何在VB6中完成的。在拉链调用CopyHere后,我们等待异步压缩完成这样

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 

尼斯的副作用是,即使压缩和解失败,这不会结束在无限循环中。

当zipfldr.dll关闭zip文件时,pvCanOpenExclusive返回true时会出现问题。

+0

这看起来像一个非常优雅的方式来检查这个... – tobriand 2014-03-21 09:54:56

+0

你假设了很多shell将要做的事情。你假设它只会打开一次文件,并且它会在你开始复制的那一刻(而不是在评估源目录属性之后的半秒之后)完成它 – EFraim 2015-12-08 16:57:02

+0

@EFraim是的,这是完全的黑客和一般不是一种工作方式。可以使用'IDropTarget'接口同步压缩文件,就像[this]一样(http://www.vbforums.com/showthread.php?808681-VB6-Create-a-ZIP-file-without-any-DLL-depends - 使用-的IStorage和-下降目标)。 – wqw 2015-12-08 23:05:30

这是我在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!"