解决方案:
Windows 包含另一个本机压缩实用程序:CreateFromDirectory
在 PowerShell 提示符下。
https://msdn.microsoft.com/en-us/library/system.io.compression.zipfile.createfromdirectory(v=vs.110).aspx
https://blogs.technet.microsoft.com/heyscriptingguy/2015/03/09/use-powershell-to-create-zip-archive-of-folder/
这需要 .Net 4.0 或更高版本:
> Add-Type -AssemblyName System.IO.Compression
> $src = "C:\Users\v1453957\documents\Experiment\rezip\aFolder"
> $zip="C:\Users\v1453957\Documents\Experiment\rezip\my.zip"
> [io.compression.zipfile]::CreateFromDirectory($src, $zip)
请注意,您可能必须提供完整的路径名——我的机器上没有隐含活动目录。
如 OP 请求,上述压缩在 PowerShell 提示符下是同步的。
下一步是从 VBA 同步执行。解决方案是Windows Script Host Object Model.Run
中的方法。在 VBA 中,设置对它的引用,然后执行以下操作,将命令的第三个参数设置为:.Run
bWaitOnReturn
True
Function SynchronousShell(sCmd As String)As Long
Dim oWSH As New IWshRuntimeLibrary.WshShell
ShellSynch = oWSH.Run(sCmd, 3, True)
Set oWSH = Nothing
End Function
现在调用SynchronousShell
,并将整个压缩脚本传递给它。
我相信这个过程起作用的唯一方法是 ifCreateFromDirectory
在与Add-Type
.
因此,我们必须将整个内容作为 1 个字符串传递。也就是说,将所有 4 个命令加载到一个sCmd
变量中,以便Add-Type
与后续的CreateFromDirectory
. 在 PowerShell 语法中,您可以将它们分开;
https://thomas.vanhoutte.be/miniblog/execute-multiple-powershell-commands-on-one-line/
此外,您需要使用单引号而不是双引号,否则当菊花链命令传递给 powershell.exe 时,字符串周围的双引号将被删除
https://stackoverflow.com/a/39801732/209942
sCmd = "ps4 Add-Type -AssemblyName System.IO.Compression; $src = 'C:\Users\v1453957\documents\Experiment\rezip\aFolder'; $zip='C:\Users\v1453957\Documents\Experiment\rezip\my.zip'; [io.compression.zipfile]::CreateFromDirectory($src, $zip)"
解决了。以上构成了完整的解决方案。
额外信息:以下附加评论适用于特殊情况:
多版本 .Net 环境
如果 .NET < 4.0 是您操作系统上的活动环境,System.IO.Compression
则不存在 - 该Add-Type
命令将失败。但是,如果您的机器有可用的 .NET 4 程序集,您仍然可以这样做:
Add-Type -Path "C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.IO.Compression.FileSystem\v4.0_4.0.0.0__b77a5c561934e089\System.IO.Compression.FileSystem.dll"
可移植性
事实证明,在我的机器上,我可以将压缩 dll 复制到任何文件夹,然后调用该副本并且它可以工作:
Add-Type -Path "C:\MyFunnyFolder\System.IO.Compression.FileSystem.dll"
我不知道需要什么来确保它有效——它可能需要将完整的 .Net 4.0 或 2.0 文件放在预期的目录中。我假设 dll 调用其他 .Net 程序集。也许我们只是幸运地遇到了这个:)
字数限制
根据我们的路径和文件名的深度,字符数可能是一个问题。PowerShell 可能有 260 个字符的限制(不确定)。
https://support.microsoft.com/en-us/kb/830473
https://social.technet.microsoft.com/Forums/windowsserver/en-US/f895d766-5ffb-483f-97bc-19ac446da9f8/powershell-command-size-limit?forum=winserverpowershell
由于.Run
通过 Windows shell,您还必须担心字符限制,但在 8k+ 时,它有点宽敞:
https ://blogs.msdn.microsoft.com/oldnewthing/20031210-00/?p=41553
https: //stackoverflow.com/a/3205048/209942
下面的网站提供了 24k+ 字符的方法,但我还没有研究过:http:
//itproctology.blogspot.com/2013/06/handling-freakishly-long-strings-from.html
至少,因为我们可以将 dll 放在我们喜欢的任何地方,我们可以将它放在 C:root 附近的文件夹中——保持我们的字符数减少。
更新: 这篇文章展示了我们如何将整个内容放在一个脚本文件中,并使用 ps4.cmd 调用它。这可能会成为我的首选答案:
.\ps4.cmd GC .\zipper.ps1 | IEX
- 取决于这里的答案。
复制这里:
关于问题:命令可以CopyHere
在命令行上执行吗?
CopyHere
可以直接在 PowerShell 提示符下执行(代码如下)。然而,即使在 powershell 中它也是异步的——控制在进程完成之前返回到 PowerShell 提示符。因此,OP没有解决方案。这是如何完成的:
> $shellapp=new-object -com shell.application
> $zippath="test.zip"
> $zipobj=$shellapp.namespace((Get-Location).Path + "\$zippath")
> $srcpath="src"
> $srcobj=$shellapp.namespace((Get-Location).Path + "\$srcpath")
> $zipobj.Copyhere($srcobj.items())