1

I'm attempting to zip a folder containing subfolders and items, using Windows shell CopyHere command:

https://msdn.microsoft.com/en-us/library/windows/desktop/bb787866(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/ms723207(v=vs.85).aspx

Update: Note, prefer a native solution-- this is for a distributed Excel VBA tool, so bundling 3rd-party files is not ideal. And, need synchronous compression.

I can easily add a folder and its contents to the zip:

oShell.Namespace(sZipPath).CopyHere "C:\My Folder"

So we know CopyHere can process multiple objects inside a folder in 1 statement.

The problem is, the above command puts the containing-folder at the root of the zip, and it's contents inside of it. But, i don't want the containing folder-- just its contents.

The doc mentions a wildcard (option 128), but when i use a wildcard, i get an error:

oShell.Namespace(sZipPath).CopyHere "C:\My Folder\*"

The file name you specified is not valid or too long.

Perhaps there's a way to use my 1st command above, and then move the items in the zip to the root of the zip?

It would be acceptable to loop through each item in the source folder, adding one at a time to the zip. But, because CopyHere is asynchronous, each subsequent CopyHere fails if the previous CopyHere is not finished. None of the fixes work for this issue:

  • Comparing number of items in source-folder and destination-zip fail, because if the zip contains a folder, that counts as only 1 item (the items it contains are not counted. https://stackoverflow.com/a/16603850/209942

  • Waiting a while between each item works, but a timer is unacceptable: it's arbitrary. I cannot guess in advance the size or compress-time of each object.

  • Checking to see if the zip is locked for access failed for me. If I block my loop until the file is not locked, I still get a file-access error. https://stackoverflow.com/a/6666663/209942


Function FileIsOpen(sPathname As String) As Boolean ' true if file is open
    Dim lFileNum As Long
    lFileNum = FreeFile
    Dim lErr As Long
    On Error Resume Next
    Open sPathname For Binary Access Read Write Lock Read Write As #lFileNum
    lErr = Err
    Close #lFileNum
    On Error GoTo 0
    FileIsOpen = (lErr <> 0)
End Function

Update: VBA can call shell commands synchronously (instead of creating a shell32.shell object in VBA), so if CopyHere works on command-line or PowerShell, that could be the solution. Investigating...

4

2 に答える 2

1

あなたがすでに発見したように、シェルオブジェクトを自動化することは実際には実行可能なアプローチではありません。Explorer Shell は、少なくとも Windows Vista より前では、VB6 プログラムまたは VBA マクロから簡単に使用できる方法ではなく、他の方法でこの機能を実際に公開することはありません。

最善の策はサード パーティの ActiveX ライブラリですが、そのようなライブラリの 64 ビット バージョンが必要な 64 ビット VBA ホストには注意してください。

もう 1 つのオプションは、 の新しいコピーを取得し、zlibwapi.dllVB6 ラッパー コードを使用することです。これも 32 ビット ソリューションです。

それがZipper & ZipWriter、VB プログラムからの Zippingです。要件を考慮すると (何らかの理由でタイマー コントロールへの懸念が含まれます)、同期 ZipperSync クラスを使用できます。そこの投稿#4を参照してください。このコードにはAddFolderToZipperSync、単一のファイルではなくフォルダーを追加するための単純なロジックが含まれています。

同期クラスの欠点は、大規模なアーカイブ操作により、完了するまでプログラム UI がフリーズすることです。それを望まない場合は、代わりに Zipper UserControl を使用してください。

そこからアイデアを得て、独自のラッパー クラスを作成することもできます。

于 2016-09-30T07:58:21.180 に答える